clean up and improve perf
This commit is contained in:
@@ -34,7 +34,7 @@ export function ComposerModal() {
|
||||
<ComposeIcon className="h-4 w-4 text-white" />
|
||||
</button>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal className="relative z-10">
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-2xl" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||
<div
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { minidenticon } from 'minidenticons';
|
||||
import { ImgHTMLAttributes, useState } from 'react';
|
||||
import { ImgHTMLAttributes, memo, useState } from 'react';
|
||||
|
||||
export function Image({ src, ...props }: ImgHTMLAttributes<HTMLImageElement>) {
|
||||
export const Image = memo(function Image({
|
||||
src,
|
||||
...props
|
||||
}: ImgHTMLAttributes<HTMLImageElement>) {
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
if (isError || !src) {
|
||||
@@ -20,9 +23,10 @@ export function Image({ src, ...props }: ImgHTMLAttributes<HTMLImageElement>) {
|
||||
currentTarget.onerror = null;
|
||||
setIsError(true);
|
||||
}}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt="lume default img"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { NavLink, useNavigate } from 'react-router-dom';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { ChatsList } from '@app/chats/components/list';
|
||||
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
import { ActiveAccount } from '@shared/accounts/active';
|
||||
import { ComposerModal } from '@shared/composer';
|
||||
import { Frame } from '@shared/frame';
|
||||
import { BellIcon, NavArrowDownIcon, NwcIcon, SpaceIcon, WorldIcon } from '@shared/icons';
|
||||
import {
|
||||
ArrowLeftIcon,
|
||||
ArrowRightIcon,
|
||||
BellIcon,
|
||||
NavArrowDownIcon,
|
||||
NwcIcon,
|
||||
SpaceIcon,
|
||||
WorldIcon,
|
||||
} from '@shared/icons';
|
||||
|
||||
import { useActivities } from '@stores/activities';
|
||||
import { useSidebar } from '@stores/sidebar';
|
||||
@@ -15,6 +25,9 @@ import { useSidebar } from '@stores/sidebar';
|
||||
import { compactNumber } from '@utils/number';
|
||||
|
||||
export function Navigation() {
|
||||
const { db } = useStorage();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const totalNewActivities = useActivities((state) => state.totalNewActivities);
|
||||
|
||||
const [chats, toggleChats] = useSidebar((state) => [state.chats, state.toggleChats]);
|
||||
@@ -30,8 +43,28 @@ export function Navigation() {
|
||||
>
|
||||
<div
|
||||
data-tauri-drag-region
|
||||
className="inline-flex h-16 w-full items-center justify-end px-3"
|
||||
className="inline-flex h-16 w-full items-center justify-between px-3"
|
||||
>
|
||||
{db.platform !== 'darwin' ? (
|
||||
<div className="inline-flex items-center gap-4 pl-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate(-1)}
|
||||
className="inline-flex h-9 items-center justify-center"
|
||||
>
|
||||
<ArrowLeftIcon className="h-5 w-5 text-white/50" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate(1)}
|
||||
className="inline-flex h-9 items-center justify-center"
|
||||
>
|
||||
<ArrowRightIcon className="h-5 w-5 text-white/50" />
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
<ComposerModal />
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -49,7 +49,7 @@ export function ChildNote({ id, root }: { id: string; root?: string }) {
|
||||
<div className="absolute bottom-0 left-[18px] h-[calc(100%-3.4rem)] w-0.5 bg-gradient-to-t from-white/20 to-white/10" />
|
||||
<div className="relative mb-5 flex flex-col">
|
||||
<div className="relative z-10 flex items-start gap-3">
|
||||
<div className="inline-flex h-11 w-11 items-end justify-center rounded-lg bg-black pb-1">
|
||||
<div className="inline-flex h-10 w-10 items-end justify-center rounded-lg bg-black pb-1">
|
||||
<img src="/lume.png" alt="lume" className="h-auto w-1/3" />
|
||||
</div>
|
||||
<h5 className="truncate font-semibold leading-none text-white">
|
||||
|
||||
@@ -12,19 +12,26 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
||||
<div className="relative z-10">
|
||||
<div className="relative flex flex-col">
|
||||
<User pubkey={event.pubkey} time={event.created_at} />
|
||||
<div className="-mt-6 flex items-start gap-3">
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="-mt-5 flex items-start gap-3">
|
||||
<div className="w-10 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<TextNote content={event.content} />
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} root={root} />
|
||||
<NoteActions
|
||||
id={event.id}
|
||||
pubkey={event.pubkey}
|
||||
root={root}
|
||||
extraButtons={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{event.replies ? (
|
||||
event.replies.map((sub) => <SubReply key={sub.id} event={sub} />)
|
||||
) : (
|
||||
<div className="pb-3" />
|
||||
)}
|
||||
<div className="pl-14">
|
||||
{event.replies ? (
|
||||
event.replies.map((sub) => <SubReply key={sub.id} event={sub} />)
|
||||
) : (
|
||||
<div className="pb-3" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ export function RepliesList({ id }: { id: string }) {
|
||||
<h5 className="mb-2 text-lg font-semibold text-white">
|
||||
{data?.length || 0} replies
|
||||
</h5>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-5">
|
||||
{data?.length === 0 ? (
|
||||
<div className="mt-2 flex w-full items-center justify-center rounded-xl bg-white/10 backdrop-blur-xl">
|
||||
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
||||
|
||||
@@ -7,11 +7,11 @@ export function SubReply({ event }: { event: NDKEvent }) {
|
||||
return (
|
||||
<div className="relative z-10 mb-3 mt-5 flex flex-col">
|
||||
<User pubkey={event.pubkey} time={event.created_at} />
|
||||
<div className="-mt-6 flex items-start gap-3">
|
||||
<div className="w-11 shrink-0" />
|
||||
<div className="-mt-5 flex items-start gap-3">
|
||||
<div className="w-10 shrink-0" />
|
||||
<div className="flex-1">
|
||||
<TextNote />
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
||||
<TextNote content={event.content} />
|
||||
<NoteActions id={event.id} pubkey={event.pubkey} extraButtons={false} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,8 @@ export function NoteSkeleton() {
|
||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
||||
<div className="h-3 w-20 rounded bg-white/10 backdrop-blur-xl" />
|
||||
</div>
|
||||
<div className="-mt-6 animate-pulse pl-[49px]">
|
||||
<div className="-mt-5 flex animate-pulse gap-3">
|
||||
<div className="w-10 shrink-0" />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="h-3 w-full rounded bg-white/10 backdrop-blur-xl" />
|
||||
<div className="h-3 w-2/3 rounded bg-white/10 backdrop-blur-xl" />
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import * as Avatar from '@radix-ui/react-avatar';
|
||||
import * as HoverCard from '@radix-ui/react-hover-card';
|
||||
import { minidenticon } from 'minidenticons';
|
||||
import { memo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { Link } from 'react-router-dom';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
import { RepostIcon, WorldIcon } from '@shared/icons';
|
||||
import { Image } from '@shared/image';
|
||||
import { NIP05 } from '@shared/nip05';
|
||||
|
||||
import { formatCreatedAt } from '@utils/createdAt';
|
||||
@@ -32,7 +33,10 @@ export const User = memo(function User({
|
||||
embedProfile?: string;
|
||||
}) {
|
||||
const { status, user } = useProfile(pubkey, embedProfile);
|
||||
const createdAt = time ? formatCreatedAt(time, variant === 'chat') : 0;
|
||||
|
||||
const createdAt = formatCreatedAt(time, variant === 'chat');
|
||||
const svgURI =
|
||||
'data:image/svg+xml;utf8,' + encodeURIComponent(minidenticon(pubkey, 90, 50));
|
||||
|
||||
if (status === 'loading') {
|
||||
if (variant === 'avatar') {
|
||||
@@ -51,8 +55,8 @@ export const User = memo(function User({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative flex items-start gap-3">
|
||||
<div className="relative z-10 h-10 w-10 shrink-0 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="h-10 w-10 shrink-0 animate-pulse overflow-hidden rounded-lg bg-white/10 backdrop-blur-xl" />
|
||||
<div className="h-3.5 w-36 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||
</div>
|
||||
);
|
||||
@@ -60,14 +64,20 @@ export const User = memo(function User({
|
||||
|
||||
if (variant === 'mention') {
|
||||
return (
|
||||
<div className="relative z-10 flex items-center gap-2">
|
||||
<button type="button" className="relative z-40 h-6 w-6 shrink-0 overflow-hidden">
|
||||
<Image
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-6 w-6 rounded object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-6 w-6 rounded"
|
||||
/>
|
||||
</button>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img src={svgURI} alt={pubkey} className="h-6 w-6 rounded bg-black" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div className="flex flex-1 items-baseline gap-2">
|
||||
<h5 className="max-w-[10rem] truncate font-semibold leading-none text-white">
|
||||
{user?.name ||
|
||||
@@ -85,11 +95,19 @@ export const User = memo(function User({
|
||||
if (variant === 'large') {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col gap-2.5">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-14 w-14 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-14 w-14 rounded-lg"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img src={svgURI} alt={pubkey} className="h-14 w-14 rounded-lg bg-black" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div className="flex h-full flex-col items-start justify-between">
|
||||
<div className="flex flex-col items-start gap-1 text-start">
|
||||
<p className="max-w-[15rem] truncate text-lg font-semibold leading-none text-white">
|
||||
@@ -125,11 +143,19 @@ export const User = memo(function User({
|
||||
if (variant === 'simple') {
|
||||
return (
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-12 w-12 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-12 w-12 rounded-lg"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img src={svgURI} alt={pubkey} className="h-12 w-12 rounded-lg bg-black" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div className="flex w-full flex-col items-start gap-1">
|
||||
<h3 className="max-w-[15rem] truncate font-medium leading-none text-white">
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
@@ -144,11 +170,19 @@ export const User = memo(function User({
|
||||
|
||||
if (variant === 'avatar') {
|
||||
return (
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-12 w-12 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<Avatar.Root>
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-12 w-12 rounded-lg"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img src={svgURI} alt={pubkey} className="h-12 w-12 rounded-lg bg-black" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -159,11 +193,19 @@ export const User = memo(function User({
|
||||
<RepostIcon className="h-6 w-6 text-blue-500" />
|
||||
</div>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="relative z-20 inline-block h-6 w-6 rounded"
|
||||
/>
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-6 w-6 rounded"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img src={svgURI} alt={pubkey} className="h-6 w-6 rounded bg-black" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div className="inline-flex items-baseline gap-1">
|
||||
<h5 className="max-w-[10rem] truncate font-medium leading-none text-white/80">
|
||||
{user?.name ||
|
||||
@@ -181,14 +223,22 @@ export const User = memo(function User({
|
||||
if (variant === 'thread') {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="relative z-20 inline-block h-10 w-10 rounded-lg"
|
||||
/>
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-10 w-10 rounded-lg"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img src={svgURI} alt={pubkey} className="h-10 w-10 rounded-lg bg-black" />
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
{user?.name || user?.display_name || user?.displayName || 'Anon'}
|
||||
</h5>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<span className="leading-none text-white/50">{createdAt}</span>
|
||||
@@ -204,16 +254,23 @@ export const User = memo(function User({
|
||||
<HoverCard.Root>
|
||||
<div className="relative z-10 flex items-start gap-3">
|
||||
<HoverCard.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="relative z-40 h-10 w-10 shrink-0 overflow-hidden"
|
||||
>
|
||||
<Image
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-10 w-10 rounded-lg object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-10 w-10 rounded-lg border border-white/5"
|
||||
/>
|
||||
</button>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img
|
||||
src={svgURI}
|
||||
alt={pubkey}
|
||||
className="h-10 w-10 rounded-lg border border-white/5 bg-black"
|
||||
/>
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
</HoverCard.Trigger>
|
||||
<div className="flex flex-1 items-baseline gap-2">
|
||||
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
||||
@@ -232,11 +289,23 @@ export const User = memo(function User({
|
||||
sideOffset={5}
|
||||
>
|
||||
<div className="flex gap-2.5 border-b border-white/5 px-3 py-3">
|
||||
<Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
className="h-10 w-10 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<Avatar.Root className="shrink-0">
|
||||
<Avatar.Image
|
||||
src={user?.picture || user?.image}
|
||||
alt={pubkey}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
style={{ contentVisibility: 'auto' }}
|
||||
className="h-10 w-10 rounded-lg border border-white/5"
|
||||
/>
|
||||
<Avatar.Fallback delayMs={300}>
|
||||
<img
|
||||
src={svgURI}
|
||||
alt={pubkey}
|
||||
className="h-10 w-10 rounded-lg border border-white/5 bg-black"
|
||||
/>
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<div className="inline-flex flex-col gap-1">
|
||||
<h5 className="text-sm font-semibold leading-none">
|
||||
|
||||
@@ -19,6 +19,8 @@ export function EventLoader({ firstTime }: { firstTime: boolean }) {
|
||||
useEffect(() => {
|
||||
async function getEvents() {
|
||||
const events = await getAllEventsSinceLastLogin();
|
||||
console.log('total event found: ', events.data.length);
|
||||
|
||||
const promises = await Promise.all(
|
||||
events.data.map(async (event) => await db.createEvent(event))
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user