feat: polish
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user