feat: polish

This commit is contained in:
2024-01-14 09:39:56 +07:00
parent ab27bd5f44
commit f908c46a19
34 changed files with 671 additions and 421 deletions

View File

@@ -114,7 +114,7 @@ export function NoteZap() {
</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-xl dark:bg-white/20" />
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
<div className="relative w-full max-w-xl bg-white h-min rounded-xl dark:bg-black">
<div className="inline-flex items-center justify-between w-full px-5 py-3 shrink-0">

View File

@@ -5,6 +5,7 @@ import {
NOSTR_EVENTS,
NOSTR_MENTIONS,
VIDEOS,
canPreview,
cn,
regionNames,
} from "@lume/utils";
@@ -27,9 +28,11 @@ import { useNoteContext } from "./provider";
export function NoteContent({
className,
mini = false,
isTranslatable = false,
}: {
className?: string;
mini?: boolean;
isTranslatable?: boolean;
}) {
const storage = useStorage();
@@ -52,7 +55,7 @@ export function NoteContent({
const words = text.split(/( |\n)/);
const urls = [...getUrls(text)];
if (storage.settings.media && !storage.settings.lowPower) {
if (storage.settings.media && !storage.settings.lowPower && !mini) {
images = urls.filter((word) =>
IMAGES.some((el) => {
const url = new URL(word);
@@ -79,9 +82,11 @@ export function NoteContent({
);
}
events = words.filter((word) =>
NOSTR_EVENTS.some((el) => word.startsWith(el)),
);
if (!mini) {
events = words.filter((word) =>
NOSTR_EVENTS.some((el) => word.startsWith(el)),
);
}
const hashtags = words.filter((word) => word.startsWith("#"));
const mentions = words.filter((word) =>
@@ -198,7 +203,7 @@ export function NoteContent({
(match, i) => {
const url = new URL(match);
if (!linkPreview) {
if (!linkPreview && canPreview(match)) {
linkPreview = match;
return <LinkPreview key={match + i} url={url.toString()} />;
}
@@ -217,9 +222,11 @@ export function NoteContent({
},
);
parsedContent = reactStringReplace(parsedContent, "\n", () => {
return <div key={nanoid()} className="h-3" />;
});
if (!mini) {
parsedContent = reactStringReplace(parsedContent, "\n", () => {
return <div key={nanoid()} className="h-3" />;
});
}
if (typeof parsedContent[0] === "string") {
parsedContent[0] = parsedContent[0].trimStart();
@@ -259,7 +266,12 @@ export function NoteContent({
return (
<div className={cn(className)}>
<div className="break-p select-text whitespace-pre-line text-balance leading-normal">
<div
className={cn(
"break-p select-text text-balance leading-normal",
!mini ? "whitespace-pre-line" : "",
)}
>
{richContent}
</div>
{isTranslatable && storage.settings.translation ? (

View File

@@ -1,13 +1,17 @@
import { PinIcon } from "@lume/icons";
import { COL_TYPES } from "@lume/utils";
import { memo } from "react";
import { Link } from "react-router-dom";
import { Note } from "../";
import { useEvent } from "../../../hooks/useEvent";
import { useColumnContext } from "../../column/provider";
import { User } from "../../user";
export const MentionNote = memo(function MentionNote({
eventId,
openable = true,
}: { eventId: string; openable?: boolean }) {
const { addColumn } = useColumnContext();
const { isLoading, isError, data } = useEvent(eventId);
if (isLoading) {
@@ -34,11 +38,11 @@ export const MentionNote = memo(function MentionNote({
return (
<Note.Provider event={data}>
<Note.Root className="flex flex-col w-full gap-1 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800">
<Note.Root className="flex flex-col w-full my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900 border border-neutral-100 dark:border-neutral-900">
<User.Provider pubkey={data.pubkey}>
<User.Root className="px-3 mt-3 flex h-6 items-center gap-2">
<User.Root className="flex h-10 px-3 items-center gap-2">
<User.Avatar className="size-6 shrink-0 rounded-md object-cover" />
<div className="flex flex-1 items-baseline gap-2">
<div className="flex-1 inline-flex gap-2">
<User.Name className="font-semibold text-neutral-900 dark:text-neutral-100" />
<span className="text-neutral-600 dark:text-neutral-400">·</span>
<User.Time
@@ -48,17 +52,30 @@ export const MentionNote = memo(function MentionNote({
</div>
</User.Root>
</User.Provider>
<div className="px-3 pb-3 mt-1">
<Note.Content />
{openable ? (
<Note.Content mini className="px-3" />
{openable ? (
<div className="mt-2 px-3 flex items-center justify-between">
<Link
to={`/events/${data.id}`}
className="mt-2 text-blue-500 hover:text-blue-600"
className="text-sm font-medium text-blue-500 hover:text-blue-600"
>
Show more
</Link>
) : null}
</div>
<button
type="button"
onClick={async () =>
await addColumn({
kind: COL_TYPES.thread,
title: "Thread",
content: data.id,
})
}
className="inline-flex items-center justify-center rounded-md text-neutral-600 dark:text-neutral-400 size-6 bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
>
<PinIcon className="size-4" />
</button>
</div>
) : null}
</Note.Root>
</Note.Provider>
);

View File

@@ -12,7 +12,7 @@ export function NoteUser({
return (
<User.Provider pubkey={event.pubkey}>
<User.Root className={cn("flex items-center gap-3", className)}>
<User.Avatar className="size-9 shrink-0 rounded-lg bg-white object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
<User.Avatar className="size-9 shrink-0 rounded-lg object-cover ring-1 ring-neutral-200/50 dark:ring-neutral-800/50" />
<div className="flex h-6 flex-1 items-start justify-between gap-2">
<User.Name className="font-semibold text-neutral-950 dark:text-neutral-50" />
<User.Time

View File

@@ -1,3 +1,4 @@
import { useStorage } from "@lume/storage";
import { cn } from "@lume/utils";
import * as Avatar from "@radix-ui/react-avatar";
import { minidenticon } from "minidenticons";
@@ -7,6 +8,8 @@ import { useUserContext } from "./provider";
export function UserAvatar({ className }: { className?: string }) {
const user = useUserContext();
const storage = useStorage();
const fallbackAvatar = useMemo(
() =>
`data:image/svg+xml;utf8,${encodeURIComponent(
@@ -20,7 +23,7 @@ export function UserAvatar({ className }: { className?: string }) {
<div className="shrink-0">
<div
className={cn(
"bg-black/20 dark:bg-white/20 animate-pulse",
"bg-black/20 dark:bg-white/20 rounded animate-pulse",
className,
)}
/>
@@ -30,13 +33,23 @@ export function UserAvatar({ className }: { className?: string }) {
return (
<Avatar.Root className="shrink-0">
<Avatar.Image
src={user.image}
alt={user.pubkey}
loading="eager"
decoding="async"
className={className}
/>
{storage.settings.lowPower ? (
<Avatar.Image
src={fallbackAvatar}
alt={user.pubkey}
loading="eager"
decoding="async"
className={cn("bg-black dark:bg-white", className)}
/>
) : (
<Avatar.Image
src={user.image}
alt={user.pubkey}
loading="eager"
decoding="async"
className={className}
/>
)}
<Avatar.Fallback delayMs={120}>
<img
src={fallbackAvatar}

View File

@@ -8,7 +8,7 @@ export function UserName({ className }: { className?: string }) {
return (
<div
className={cn(
"h-4 w-20 bg-black/20 dark:bg-white/20 animate-pulse",
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
className,
)}
/>
@@ -16,7 +16,7 @@ export function UserName({ className }: { className?: string }) {
}
return (
<div className={cn("w-full max-w-[15rem] truncate", className)}>
<div className={cn("truncate", className)}>
{user.displayName || user.name || "Anon"}
</div>
);

View File

@@ -29,7 +29,7 @@ export function UserNip05({
return (
<div
className={cn(
"h-4 w-20 bg-black/20 dark:bg-white/20 animate-pulse",
"h-4 w-20 bg-black/20 dark:bg-white/20 rounded animate-pulse",
className,
)}
/>