mirror of
https://github.com/lumehq/lume.git
synced 2025-03-18 05:41:53 +01:00
polish
This commit is contained in:
parent
a66770989b
commit
236131087a
30
package.json
30
package.json
@ -19,14 +19,15 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@getalby/sdk": "^2.4.0",
|
||||
"@nostr-dev-kit/ndk": "^1.2.1",
|
||||
"@nostr-dev-kit/ndk": "^1.3.0",
|
||||
"@nostr-fetch/adapter-ndk": "^0.12.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.4",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
||||
"@radix-ui/react-popover": "^1.0.6",
|
||||
"@radix-ui/react-tooltip": "^1.0.6",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-hover-card": "^1.0.7",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@tanstack/react-query": "^4.35.3",
|
||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||
"@tauri-apps/api": "^1.4.0",
|
||||
@ -50,20 +51,19 @@
|
||||
"react": "^18.2.0",
|
||||
"react-currency-input-field": "^3.6.11",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.46.1",
|
||||
"react-hook-form": "^7.46.2",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-organizational-chart": "^2.2.1",
|
||||
"react-player": "^2.13.0",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"react-virtuoso": "^4.6.0",
|
||||
"react-zoom-pan-pinch": "^3.1.0",
|
||||
"reactflow": "^11.8.3",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1",
|
||||
"tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store#v1",
|
||||
"tauri-plugin-stronghold-api": "github:tauri-apps/tauri-plugin-stronghold#v1",
|
||||
"tauri-plugin-upload-api": "github:tauri-apps/tauri-plugin-upload#v1",
|
||||
"tippy.js": "^6.3.7",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -71,19 +71,19 @@
|
||||
"@tauri-apps/cli": "^1.4.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
||||
"@types/html-to-text": "^9.0.2",
|
||||
"@types/node": "^20.6.3",
|
||||
"@types/node": "^20.7.0",
|
||||
"@types/react": "^18.2.22",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@types/youtube-player": "^5.5.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
||||
"@typescript-eslint/parser": "^6.7.2",
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"@types/youtube-player": "^5.5.8",
|
||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||
"@typescript-eslint/parser": "^6.7.3",
|
||||
"@vitejs/plugin-react-swc": "^3.4.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"clsx": "^2.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"csstype": "^3.1.2",
|
||||
"encoding": "^0.1.13",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint": "^8.50.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
|
755
pnpm-lock.yaml
generated
755
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -90,7 +90,7 @@ export const UserWithDrawer = memo(function UserWithDrawer({
|
||||
{displayNpub(pubkey, 16)}
|
||||
</span>
|
||||
)}
|
||||
{user.about ? <TextNote content={user.about} /> : null}
|
||||
{user?.about ? <TextNote content={user?.about} /> : null}
|
||||
</div>
|
||||
<div className="mt-3 inline-flex items-center gap-2">
|
||||
{followed ? (
|
||||
|
@ -38,7 +38,10 @@ export function ChatsListItem({ pubkey }: { pubkey: string }) {
|
||||
/>
|
||||
<div className="inline-flex w-full flex-1 items-center justify-between">
|
||||
<h5 className="max-w-[10rem] truncate">
|
||||
{user?.name || user?.display_name || displayNpub(pubkey, 16)}
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
displayNpub(pubkey, 16)}
|
||||
</h5>
|
||||
</div>
|
||||
</NavLink>
|
||||
|
@ -22,7 +22,7 @@ export function ChatSidebar({ pubkey }: { pubkey: string }) {
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-lg font-semibold leading-none">
|
||||
{user?.display_name || user?.name}
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
</h3>
|
||||
{user?.nip05 ? (
|
||||
<NIP05
|
||||
|
@ -25,7 +25,7 @@ export function NotiUser({ pubkey }: { pubkey: string }) {
|
||||
className="h-8 w-8 shrink-0 rounded-md object-cover"
|
||||
/>
|
||||
<span className="max-w-[10rem] truncate font-medium leading-none text-white">
|
||||
{user?.name || user?.display_name || displayNpub(pubkey, 16)}
|
||||
{user?.name || user?.display_name || user?.displayName || displayNpub(pubkey, 16)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
@ -71,7 +71,7 @@ export function UserProfile({ pubkey }: { pubkey: string }) {
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<div className="inline-flex flex-col items-center gap-1.5">
|
||||
<h5 className="text-center text-xl font-semibold leading-none">
|
||||
{user.display_name || user.displayName || user.name || 'No name'}
|
||||
{user.name || user.display_name || user.displayName || 'No name'}
|
||||
</h5>
|
||||
{user.nip05 ? (
|
||||
<NIP05
|
||||
|
@ -5,6 +5,7 @@ import { fetch } from '@tauri-apps/api/http';
|
||||
import { NostrFetcher } from 'nostr-fetch';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import TauriAdapter from '@libs/ndk/cache';
|
||||
import { useStorage } from '@libs/storage/provider';
|
||||
|
||||
export const NDKInstance = () => {
|
||||
@ -13,6 +14,7 @@ export const NDKInstance = () => {
|
||||
const [ndk, setNDK] = useState<NDK | undefined>(undefined);
|
||||
const [relayUrls, setRelayUrls] = useState<string[]>([]);
|
||||
|
||||
const cacheAdapter = useMemo(() => new TauriAdapter(), []);
|
||||
const fetcher = useMemo(
|
||||
() => (ndk ? NostrFetcher.withCustomPool(ndkAdapter(ndk)) : null),
|
||||
[ndk]
|
||||
@ -58,6 +60,7 @@ export const NDKInstance = () => {
|
||||
const explicitRelayUrls = await getExplicitRelays();
|
||||
const instance = new NDK({
|
||||
explicitRelayUrls,
|
||||
cacheAdapter,
|
||||
});
|
||||
|
||||
try {
|
||||
@ -75,6 +78,10 @@ export const NDKInstance = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!ndk) initNDK();
|
||||
|
||||
return () => {
|
||||
cacheAdapter.saveCache();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||
import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { BaseDirectory, removeFile } from '@tauri-apps/api/fs';
|
||||
import Database from 'tauri-plugin-sql-api';
|
||||
import { Stronghold } from 'tauri-plugin-stronghold-api';
|
||||
@ -302,6 +302,13 @@ export class LumeStorage {
|
||||
);
|
||||
}
|
||||
|
||||
public async createProfile(pubkey: string, profile: NDKUserProfile) {
|
||||
return await this.db.execute(
|
||||
'INSERT OR REPLACE INTO metadata (id, event, author, kind, created_at) VALUES ($1, $2, $3, $4, $5);',
|
||||
[pubkey, JSON.stringify(profile), pubkey, 0, Math.round(Date.now() / 1000)]
|
||||
);
|
||||
}
|
||||
|
||||
public async getMetadataByPubkey(pubkey: string) {
|
||||
const results: DBEvent[] = await this.db.select(
|
||||
'SELECT * FROM metadata WHERE author = $1 AND kind = "0" LIMIT 1;',
|
||||
|
@ -35,24 +35,28 @@ export function ActiveAccount() {
|
||||
since: Math.floor(Date.now() / 1000),
|
||||
};
|
||||
|
||||
sub(filter, async (event) => {
|
||||
addActivity(event);
|
||||
sub(
|
||||
filter,
|
||||
async (event) => {
|
||||
addActivity(event);
|
||||
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return await sendNativeNotification('Mention');
|
||||
case NDKKind.Contacts:
|
||||
return await sendNativeNotification("You've a new follower");
|
||||
case NDKKind.Repost:
|
||||
return await sendNativeNotification('Repost');
|
||||
case NDKKind.Reaction:
|
||||
return await sendNativeNotification('Reaction');
|
||||
case NDKKind.Zap:
|
||||
return await sendNativeNotification('Zap');
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return await sendNativeNotification('Mention');
|
||||
case NDKKind.Contacts:
|
||||
return await sendNativeNotification("You've a new follower");
|
||||
case NDKKind.Repost:
|
||||
return await sendNativeNotification('Repost');
|
||||
case NDKKind.Reaction:
|
||||
return await sendNativeNotification('Reaction');
|
||||
case NDKKind.Zap:
|
||||
return await sendNativeNotification('Zap');
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
}, []);
|
||||
|
||||
if (status === 'loading') {
|
||||
@ -74,7 +78,7 @@ export function ActiveAccount() {
|
||||
/>
|
||||
<div className="flex w-full flex-1 flex-col items-start gap-0.5">
|
||||
<p className="max-w-[10rem] truncate font-semibold leading-none text-white">
|
||||
{user?.name || user?.display_name}
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
</p>
|
||||
<span className="max-w-[7rem] truncate text-sm leading-none text-white/50">
|
||||
{user?.nip05 || displayNpub(db.account.pubkey, 12)}
|
||||
|
@ -3,4 +3,6 @@ export * from './modal';
|
||||
export * from './composer';
|
||||
export * from './mention/item';
|
||||
export * from './mention/popup';
|
||||
export * from './mention/suggestion';
|
||||
export * from './mention/inlineList';
|
||||
export * from './mediaUploader';
|
||||
|
83
src/shared/composer/mention/inlineList.tsx
Normal file
83
src/shared/composer/mention/inlineList.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { type SuggestionProps } from '@tiptap/suggestion';
|
||||
import {
|
||||
ForwardedRef,
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { MentionItem } from '@shared/composer';
|
||||
|
||||
export const MentionInlineList = forwardRef(
|
||||
(props: SuggestionProps, ref: ForwardedRef<unknown>) => {
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
|
||||
const selectItem = (index) => {
|
||||
const item = props.items[index];
|
||||
|
||||
if (item) {
|
||||
props.command({ id: item });
|
||||
}
|
||||
};
|
||||
|
||||
const upHandler = () => {
|
||||
setSelectedIndex((selectedIndex + props.items.length - 1) % props.items.length);
|
||||
};
|
||||
|
||||
const downHandler = () => {
|
||||
setSelectedIndex((selectedIndex + 1) % props.items.length);
|
||||
};
|
||||
|
||||
const enterHandler = () => {
|
||||
selectItem(selectedIndex);
|
||||
};
|
||||
|
||||
useEffect(() => setSelectedIndex(0), [props.items]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
onKeyDown: ({ event }) => {
|
||||
if (event.key === 'ArrowUp') {
|
||||
upHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'ArrowDown') {
|
||||
downHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
enterHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="flex w-[250px] flex-col rounded-xl bg-white/10 px-3 py-3 backdrop-blur-xl">
|
||||
{props.items.length ? (
|
||||
props.items.map((item: string, index: number) => (
|
||||
<button
|
||||
className={twMerge(
|
||||
'h-11 w-full rounded-lg px-2 text-start text-sm font-medium hover:bg-white/10',
|
||||
`${index === selectedIndex ? 'is-selected' : ''}`
|
||||
)}
|
||||
key={index}
|
||||
onClick={() => selectItem(index)}
|
||||
>
|
||||
<MentionItem embed={item} />
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<div>No result</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
MentionInlineList.displayName = 'MentionList';
|
@ -3,12 +3,12 @@ import { Image } from '@shared/image';
|
||||
import { useProfile } from '@utils/hooks/useProfile';
|
||||
import { displayNpub } from '@utils/shortenKey';
|
||||
|
||||
export function MentionItem({ pubkey }: { pubkey: string }) {
|
||||
const { status, user } = useProfile(pubkey);
|
||||
export function MentionItem({ pubkey, embed }: { pubkey: string; embed?: string }) {
|
||||
const { status, user } = useProfile(pubkey, embed);
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2.5 px-2">
|
||||
<div className="relative h-8 w-8 shrink-0 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||
<div className="flex w-full flex-1 flex-col items-start gap-1 text-start">
|
||||
<span className="h-4 w-1/2 animate-pulse rounded bg-white/10 backdrop-blur-xl" />
|
||||
|
68
src/shared/composer/mention/suggestion.tsx
Normal file
68
src/shared/composer/mention/suggestion.tsx
Normal file
@ -0,0 +1,68 @@
|
||||
import { ReactRenderer } from '@tiptap/react';
|
||||
import tippy from 'tippy.js';
|
||||
|
||||
import { MentionInlineList } from '@shared/composer';
|
||||
|
||||
export const Suggestion = {
|
||||
items: async ({ query }) => {
|
||||
const users = [];
|
||||
return users
|
||||
.filter((item) => item.ident.toLowerCase().startsWith(query.toLowerCase()))
|
||||
.slice(0, 5);
|
||||
},
|
||||
|
||||
render: () => {
|
||||
let component;
|
||||
let popup;
|
||||
|
||||
return {
|
||||
onStart: (props) => {
|
||||
component = new ReactRenderer(MentionInlineList, {
|
||||
props,
|
||||
editor: props.editor,
|
||||
});
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup = tippy('body', {
|
||||
getReferenceClientRect: props.clientRect,
|
||||
appendTo: () => document.body,
|
||||
content: component.element,
|
||||
showOnCreate: true,
|
||||
interactive: true,
|
||||
trigger: 'manual',
|
||||
placement: 'bottom-start',
|
||||
});
|
||||
},
|
||||
|
||||
onUpdate(props) {
|
||||
component.updateProps(props);
|
||||
|
||||
if (!props.clientRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
popup[0].setProps({
|
||||
getReferenceClientRect: props.clientRect,
|
||||
});
|
||||
},
|
||||
|
||||
onKeyDown(props) {
|
||||
if (props.event.key === 'Escape') {
|
||||
popup[0].hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return component.ref?.onKeyDown(props);
|
||||
},
|
||||
|
||||
onExit() {
|
||||
popup[0].destroy();
|
||||
component.destroy();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
@ -14,7 +14,7 @@ export function ComposerUser({ pubkey }: { pubkey: string }) {
|
||||
className="h-10 w-10 shrink-0 rounded-lg"
|
||||
/>
|
||||
<h5 className="text-base font-semibold leading-none text-white">
|
||||
{user?.name || user?.display_name || displayNpub(pubkey, 16)}
|
||||
{user?.name || user?.display_name || user?.displayName || displayNpub(pubkey, 16)}
|
||||
</h5>
|
||||
</div>
|
||||
);
|
||||
|
@ -60,7 +60,7 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
||||
if (send) {
|
||||
await sendNativeNotification(
|
||||
`You've tipped ${compactNumber.format(send.amount)} sats to ${
|
||||
user?.display_name || user?.name
|
||||
user?.name || user?.display_name || user?.displayName
|
||||
}`
|
||||
);
|
||||
|
||||
@ -106,7 +106,7 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
||||
<div className="inline-flex w-full shrink-0 items-center justify-between px-5 py-3">
|
||||
<div className="w-6" />
|
||||
<Dialog.Title className="text-center text-sm font-semibold leading-none text-white">
|
||||
Send tip to {user?.display_name || user?.name}
|
||||
Send tip to {user?.name || user?.display_name || user?.displayName}
|
||||
</Dialog.Title>
|
||||
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10">
|
||||
<CancelIcon className="h-4 w-4 text-white/50" />
|
||||
|
@ -18,20 +18,24 @@ export function MentionUser({ pubkey }: { pubkey: string }) {
|
||||
onClick={() =>
|
||||
setWidget(db, {
|
||||
kind: WidgetKinds.local.user,
|
||||
title: user?.name || user?.display_name,
|
||||
title: user?.name || user?.display_name || user?.displayName,
|
||||
content: pubkey,
|
||||
})
|
||||
}
|
||||
onKeyDown={() =>
|
||||
setWidget(db, {
|
||||
kind: WidgetKinds.local.user,
|
||||
title: user?.name || user?.display_name,
|
||||
title: user?.name || user?.display_name || user?.displayName,
|
||||
content: pubkey,
|
||||
})
|
||||
}
|
||||
className="break-words text-fuchsia-400 hover:text-fuchsia-500"
|
||||
>
|
||||
{user?.name || user?.display_name || user?.username || displayNpub(pubkey, 16)}
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
user?.username ||
|
||||
displayNpub(pubkey, 16)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as Popover from '@radix-ui/react-popover';
|
||||
import * as HoverCard from '@radix-ui/react-hover-card';
|
||||
import { memo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -70,7 +70,10 @@ export const User = memo(function User({
|
||||
</button>
|
||||
<div className="flex flex-1 items-baseline gap-2">
|
||||
<h5 className="max-w-[10rem] truncate font-semibold leading-none text-white">
|
||||
{user?.display_name || user?.name || displayNpub(pubkey, 16)}
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
displayNpub(pubkey, 16)}
|
||||
</h5>
|
||||
<span className="leading-none text-white/50">·</span>
|
||||
<span className="leading-none text-white/50">{createdAt}</span>
|
||||
@ -90,7 +93,7 @@ export const User = memo(function User({
|
||||
<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">
|
||||
{user?.name || user?.display_name}
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
</p>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
@ -110,7 +113,7 @@ export const User = memo(function User({
|
||||
className="inline-flex items-center gap-2 text-sm text-white/70"
|
||||
>
|
||||
<WorldIcon className="h-4 w-4" />
|
||||
<p className="max-w-[10rem] truncate">{user.website}</p>
|
||||
<p className="max-w-[10rem] truncate">{user?.website}</p>
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
@ -129,7 +132,7 @@ export const User = memo(function User({
|
||||
/>
|
||||
<div className="flex w-full flex-col gap-1">
|
||||
<h3 className="max-w-[15rem] truncate font-medium leading-none text-white">
|
||||
{user?.name || user?.display_name}
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
</h3>
|
||||
<p className="text-sm leading-none text-white/70">
|
||||
{user?.nip05 || user?.username || displayNpub(pubkey, 16)}
|
||||
@ -163,7 +166,10 @@ export const User = memo(function User({
|
||||
/>
|
||||
<div className="inline-flex items-baseline gap-1">
|
||||
<h5 className="max-w-[10rem] truncate font-medium leading-none text-white/80">
|
||||
{user?.display_name || user?.name || displayNpub(pubkey, 16)}
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
displayNpub(pubkey, 16)}
|
||||
</h5>
|
||||
<span className="text-blue-500">reposted</span>
|
||||
</div>
|
||||
@ -182,7 +188,7 @@ export const User = memo(function User({
|
||||
/>
|
||||
<div className="flex flex-1 flex-col gap-2">
|
||||
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
||||
{user?.display_name || user?.name}
|
||||
{user?.name || user?.display_name || user?.displayName}
|
||||
</h5>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<span className="leading-none text-white/50">{createdAt}</span>
|
||||
@ -195,9 +201,9 @@ export const User = memo(function User({
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover.Root>
|
||||
<HoverCard.Root>
|
||||
<div className="relative z-10 flex items-start gap-3">
|
||||
<Popover.Trigger asChild>
|
||||
<HoverCard.Trigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="relative z-40 h-10 w-10 shrink-0 overflow-hidden"
|
||||
@ -208,18 +214,21 @@ export const User = memo(function User({
|
||||
className="h-10 w-10 rounded-lg object-cover"
|
||||
/>
|
||||
</button>
|
||||
</Popover.Trigger>
|
||||
</HoverCard.Trigger>
|
||||
<div className="flex flex-1 items-baseline gap-2">
|
||||
<h5 className="max-w-[15rem] truncate font-semibold leading-none text-white">
|
||||
{user?.display_name || user?.name || displayNpub(pubkey, 16)}
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
displayNpub(pubkey, 16)}
|
||||
</h5>
|
||||
<span className="leading-none text-white/50">·</span>
|
||||
<span className="leading-none text-white/50">{createdAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Popover.Portal>
|
||||
<Popover.Content
|
||||
className="w-[300px] overflow-hidden rounded-xl border border-white/10 bg-white/10 backdrop-blur-3xl focus:outline-none"
|
||||
<HoverCard.Portal>
|
||||
<HoverCard.Content
|
||||
className="w-[300px] overflow-hidden rounded-xl border border-white/10 bg-white/10 backdrop-blur-3xl focus:outline-none data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=top]:animate-slideDownAndFade data-[state=open]:transition-all"
|
||||
sideOffset={5}
|
||||
>
|
||||
<div className="flex gap-2.5 border-b border-white/5 px-3 py-3">
|
||||
@ -231,12 +240,15 @@ export const User = memo(function User({
|
||||
<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">
|
||||
{user?.display_name || user?.name || user?.username}
|
||||
{user?.name ||
|
||||
user?.display_name ||
|
||||
user?.displayName ||
|
||||
user?.username}
|
||||
</h5>
|
||||
{user.nip05 ? (
|
||||
{user?.nip05 ? (
|
||||
<NIP05
|
||||
pubkey={pubkey}
|
||||
nip05={user.nip05}
|
||||
nip05={user?.nip05}
|
||||
className="max-w-[15rem] truncate text-sm leading-none text-white/50"
|
||||
/>
|
||||
) : (
|
||||
@ -266,8 +278,8 @@ export const User = memo(function User({
|
||||
Message
|
||||
</Link>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Portal>
|
||||
</Popover.Root>
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Portal>
|
||||
</HoverCard.Root>
|
||||
);
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ export const useSidebar = create<SidebarState>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
feeds: true,
|
||||
chats: true,
|
||||
chats: false,
|
||||
integrations: true,
|
||||
toggleFeeds: () => set((state) => ({ feeds: !state.feeds })),
|
||||
toggleChats: () => set((state) => ({ chats: !state.chats })),
|
||||
|
@ -36,10 +36,17 @@ export function useNostr() {
|
||||
[]
|
||||
);
|
||||
|
||||
const sub = async (filter: NDKFilter, callback: (event: NDKEvent) => void) => {
|
||||
const sub = async (
|
||||
filter: NDKFilter,
|
||||
callback: (event: NDKEvent) => void,
|
||||
groupable?: boolean
|
||||
) => {
|
||||
if (!ndk) throw new Error('NDK instance not found');
|
||||
|
||||
const subEvent = ndk.subscribe(filter, { closeOnEose: false });
|
||||
const subEvent = ndk.subscribe(filter, {
|
||||
closeOnEose: false,
|
||||
groupable: groupable ?? true,
|
||||
});
|
||||
subManager.set(JSON.stringify(filter), subEvent);
|
||||
|
||||
subEvent.addListener('event', (event: NDKEvent) => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NDKKind, NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useNDK } from '@libs/ndk/provider';
|
||||
@ -14,30 +14,20 @@ export function useProfile(pubkey: string, embed?: string) {
|
||||
} = useQuery(
|
||||
['user', pubkey],
|
||||
async () => {
|
||||
if (!embed) {
|
||||
const cleanPubkey = pubkey.replace('-', '');
|
||||
const dbEvent = await db.getMetadataByPubkey(cleanPubkey);
|
||||
if (dbEvent) {
|
||||
return JSON.parse(dbEvent.content) as NDKUserProfile;
|
||||
}
|
||||
|
||||
const events = await ndk.fetchEvents(
|
||||
{
|
||||
kinds: [NDKKind.Metadata],
|
||||
authors: [cleanPubkey],
|
||||
limit: 100,
|
||||
},
|
||||
{ closeOnEose: true }
|
||||
);
|
||||
if (!events) throw new Error(`User not found: ${pubkey}`);
|
||||
const latestEvent = [...events].sort((a, b) => b.created_at - a.created_at)[0];
|
||||
await db.createMetadata(latestEvent);
|
||||
|
||||
return JSON.parse(latestEvent.content) as NDKUserProfile;
|
||||
} else {
|
||||
if (embed) {
|
||||
const profile: NDKUserProfile = JSON.parse(embed);
|
||||
return profile;
|
||||
}
|
||||
|
||||
const cleanPubkey = pubkey.replace('-', '');
|
||||
const user = ndk.getUser({ hexpubkey: cleanPubkey });
|
||||
|
||||
const profile = await user.fetchProfile({ closeOnEose: true });
|
||||
if (!user.profile) return Promise.reject(new Error('profile not found'));
|
||||
|
||||
await db.createProfile(cleanPubkey, profile);
|
||||
|
||||
return user.profile;
|
||||
},
|
||||
{
|
||||
enabled: !!ndk,
|
||||
|
Loading…
x
Reference in New Issue
Block a user