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