mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 06:24:25 +01:00
update
This commit is contained in:
@@ -2,14 +2,15 @@ import { Component, JSX, Show, createSignal } from 'solid-js';
|
|||||||
|
|
||||||
import useDetectOverflow from '@/hooks/useDetectOverflow';
|
import useDetectOverflow from '@/hooks/useDetectOverflow';
|
||||||
import useFormatDate from '@/hooks/useFormatDate';
|
import useFormatDate from '@/hooks/useFormatDate';
|
||||||
|
import useProfile from '@/nostr/useProfile';
|
||||||
|
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
||||||
|
|
||||||
export type PostProps = {
|
export type PostProps = {
|
||||||
author: JSX.Element;
|
authorPubkey: string;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
content: JSX.Element;
|
content: JSX.Element;
|
||||||
actions?: JSX.Element;
|
actions?: JSX.Element;
|
||||||
footer?: JSX.Element;
|
footer?: JSX.Element;
|
||||||
authorPictureUrl?: string;
|
|
||||||
onShowProfile?: () => void;
|
onShowProfile?: () => void;
|
||||||
onShowEvent?: () => void;
|
onShowEvent?: () => void;
|
||||||
};
|
};
|
||||||
@@ -21,6 +22,10 @@ const Post: Component<PostProps> = (props) => {
|
|||||||
const [showOverflow, setShowOverflow] = createSignal();
|
const [showOverflow, setShowOverflow] = createSignal();
|
||||||
const createdAt = () => formatDate(props.createdAt);
|
const createdAt = () => formatDate(props.createdAt);
|
||||||
|
|
||||||
|
const { profile: author } = useProfile(() => ({
|
||||||
|
pubkey: props.authorPubkey,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="post flex flex-col">
|
<div class="post flex flex-col">
|
||||||
<div class="flex w-full gap-1">
|
<div class="flex w-full gap-1">
|
||||||
@@ -32,7 +37,7 @@ const Post: Component<PostProps> = (props) => {
|
|||||||
props.onShowProfile?.();
|
props.onShowProfile?.();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={props.authorPictureUrl} keyed>
|
<Show when={author()?.picture} keyed>
|
||||||
{(url) => <img src={url} alt="icon" class="h-full w-full rounded object-cover" />}
|
{(url) => <img src={url} alt="icon" class="h-full w-full rounded object-cover" />}
|
||||||
</Show>
|
</Show>
|
||||||
</button>
|
</button>
|
||||||
@@ -46,7 +51,22 @@ const Post: Component<PostProps> = (props) => {
|
|||||||
props?.onShowProfile?.();
|
props?.onShowProfile?.();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.author}
|
<span class="author flex min-w-0 truncate hover:text-blue-500">
|
||||||
|
<Show when={(author()?.display_name?.length ?? 0) > 0}>
|
||||||
|
<div class="author-name truncate pr-1 font-bold hover:underline">
|
||||||
|
{author()?.display_name}
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<div class="author-username truncate text-zinc-600">
|
||||||
|
<Show
|
||||||
|
when={author()?.name != null}
|
||||||
|
fallback={`@${npubEncodeFallback(props.authorPubkey)}`}
|
||||||
|
>
|
||||||
|
@{author()?.name}
|
||||||
|
</Show>
|
||||||
|
{/* TODO <Match when={author()?.nip05 != null}>@{author()?.nip05}</Match> */}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="created-at shrink-0">
|
<div class="created-at shrink-0">
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const Column: Component<ColumnProps> = (props) => {
|
|||||||
<div>ホームに戻る</div>
|
<div>ホームに戻る</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ul class="scrollbar flex h-full flex-col overflow-y-scroll scroll-smooth">
|
<ul class="scrollbar flex h-full flex-col overflow-y-scroll scroll-smooth pb-8">
|
||||||
<TimelineContentDisplay timelineContent={timeline} />
|
<TimelineContentDisplay timelineContent={timeline} />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ const Reaction: Component<ReactionProps> = (props) => {
|
|||||||
// if the reacted event is not found, it should be a removed event
|
// if the reacted event is not found, it should be a removed event
|
||||||
<Show when={!isRemoved() || shouldMuteEvent(props.event)}>
|
<Show when={!isRemoved() || shouldMuteEvent(props.event)}>
|
||||||
<div class="flex gap-1 px-1 text-sm">
|
<div class="flex gap-1 px-1 text-sm">
|
||||||
<div class="notification-icon flex place-items-center">
|
<div class="notification-icon flex max-w-[64px] place-items-center">
|
||||||
<Switch fallback={props.event.content}>
|
<Switch fallback={<span class="truncate">{props.event.content}</span>}>
|
||||||
<Match when={props.event.content === '+'}>
|
<Match when={props.event.content === '+'}>
|
||||||
<span class="h-4 w-4 pt-[1px] text-rose-400">
|
<span class="h-4 w-4 pt-[1px] text-rose-400">
|
||||||
<HeartSolid />
|
<HeartSolid />
|
||||||
|
|||||||
45
src/components/event/ZapReceipt.tsx
Normal file
45
src/components/event/ZapReceipt.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Component, Show, createMemo } from 'solid-js';
|
||||||
|
|
||||||
|
import { type Event as NostrEvent } from 'nostr-tools';
|
||||||
|
|
||||||
|
import GeneralUserMentionDisplay from '@/components/event/textNote/GeneralUserMentionDisplay';
|
||||||
|
import UserNameDisplay from '@/components/UserDisplayName';
|
||||||
|
import useConfig from '@/core/useConfig';
|
||||||
|
import { genericEvent } from '@/nostr/event';
|
||||||
|
|
||||||
|
export type ZapReceiptProps = {
|
||||||
|
event: NostrEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ZapReceipt: Component<ZapReceiptProps> = (props) => {
|
||||||
|
const { shouldMuteEvent } = useConfig();
|
||||||
|
|
||||||
|
const event = createMemo(() => genericEvent(props.event));
|
||||||
|
|
||||||
|
const zapRequest = () => {
|
||||||
|
const description = event().findFirstTagByName('description');
|
||||||
|
if (description == null) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO verify that this is event
|
||||||
|
return JSON.parse(description[1]) as NostrEvent;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('failed to parse zap receipt', description);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const amount = () => {
|
||||||
|
return event().findFirstTagByName('amount');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Show when={!shouldMuteEvent(props.event)}>
|
||||||
|
⚡
|
||||||
|
<UserNameDisplay pubkey={zapRequest().pubkey} />
|
||||||
|
<pre>{JSON.stringify(props.event, null, 2)}</pre>
|
||||||
|
</Show>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ZapReceipt;
|
||||||
@@ -16,6 +16,7 @@ import EventDisplayById from '@/components/event/EventDisplayById';
|
|||||||
import ContentWarningDisplay from '@/components/event/textNote/ContentWarningDisplay';
|
import ContentWarningDisplay from '@/components/event/textNote/ContentWarningDisplay';
|
||||||
import GeneralUserMentionDisplay from '@/components/event/textNote/GeneralUserMentionDisplay';
|
import GeneralUserMentionDisplay from '@/components/event/textNote/GeneralUserMentionDisplay';
|
||||||
import TextNoteContentDisplay from '@/components/event/textNote/TextNoteContentDisplay';
|
import TextNoteContentDisplay from '@/components/event/textNote/TextNoteContentDisplay';
|
||||||
|
import EventDebugModal from '@/components/modal/EventDebugModal';
|
||||||
import NotePostForm from '@/components/NotePostForm';
|
import NotePostForm from '@/components/NotePostForm';
|
||||||
import Post from '@/components/Post';
|
import Post from '@/components/Post';
|
||||||
import { useTimelineContext } from '@/components/timeline/TimelineContext';
|
import { useTimelineContext } from '@/components/timeline/TimelineContext';
|
||||||
@@ -23,12 +24,10 @@ import useConfig from '@/core/useConfig';
|
|||||||
import useModalState from '@/hooks/useModalState';
|
import useModalState from '@/hooks/useModalState';
|
||||||
import { textNote } from '@/nostr/event';
|
import { textNote } from '@/nostr/event';
|
||||||
import useCommands from '@/nostr/useCommands';
|
import useCommands from '@/nostr/useCommands';
|
||||||
import useProfile from '@/nostr/useProfile';
|
|
||||||
import usePubkey from '@/nostr/usePubkey';
|
import usePubkey from '@/nostr/usePubkey';
|
||||||
import useReactions from '@/nostr/useReactions';
|
import useReactions from '@/nostr/useReactions';
|
||||||
import useReposts from '@/nostr/useReposts';
|
import useReposts from '@/nostr/useReposts';
|
||||||
import ensureNonNull from '@/utils/ensureNonNull';
|
import ensureNonNull from '@/utils/ensureNonNull';
|
||||||
import npubEncodeFallback from '@/utils/npubEncodeFallback';
|
|
||||||
import timeout from '@/utils/timeout';
|
import timeout from '@/utils/timeout';
|
||||||
|
|
||||||
export type TextNoteDisplayProps = {
|
export type TextNoteDisplayProps = {
|
||||||
@@ -49,7 +48,7 @@ const EmojiReactions: Component<EmojiReactionsProps> = (props) => {
|
|||||||
const pubkey = usePubkey();
|
const pubkey = usePubkey();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex gap-2 py-1">
|
<div class="flex gap-2 overflow-x-auto py-1">
|
||||||
<For each={[...props.reactionsGroupedByContent.entries()]}>
|
<For each={[...props.reactionsGroupedByContent.entries()]}>
|
||||||
{([content, events]) => {
|
{([content, events]) => {
|
||||||
const isReactedByMeWithThisContent =
|
const isReactedByMeWithThisContent =
|
||||||
@@ -57,7 +56,7 @@ const EmojiReactions: Component<EmojiReactionsProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class="flex h-6 items-center rounded border px-1"
|
class="flex h-6 max-w-[128px] items-center rounded border px-1"
|
||||||
classList={{
|
classList={{
|
||||||
'text-zinc-400': !isReactedByMeWithThisContent,
|
'text-zinc-400': !isReactedByMeWithThisContent,
|
||||||
'hover:bg-zinc-50': !isReactedByMeWithThisContent,
|
'hover:bg-zinc-50': !isReactedByMeWithThisContent,
|
||||||
@@ -68,7 +67,10 @@ const EmojiReactions: Component<EmojiReactionsProps> = (props) => {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => props.onReaction(content)}
|
onClick={() => props.onReaction(content)}
|
||||||
>
|
>
|
||||||
<Show when={content === '+'} fallback={<span class="text-base">{content}</span>}>
|
<Show
|
||||||
|
when={content === '+'}
|
||||||
|
fallback={<span class="truncate text-base">{content}</span>}
|
||||||
|
>
|
||||||
<span class="inline-block h-3 w-3 pt-[1px] text-rose-400">
|
<span class="inline-block h-3 w-3 pt-[1px] text-rose-400">
|
||||||
<HeartSolid />
|
<HeartSolid />
|
||||||
</span>
|
</span>
|
||||||
@@ -93,6 +95,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
|||||||
const [reacted, setReacted] = createSignal(false);
|
const [reacted, setReacted] = createSignal(false);
|
||||||
const [reposted, setReposted] = createSignal(false);
|
const [reposted, setReposted] = createSignal(false);
|
||||||
const [showReplyForm, setShowReplyForm] = createSignal(false);
|
const [showReplyForm, setShowReplyForm] = createSignal(false);
|
||||||
|
const [showEventDebug, setShowEventDebug] = createSignal(false);
|
||||||
const closeReplyForm = () => setShowReplyForm(false);
|
const closeReplyForm = () => setShowReplyForm(false);
|
||||||
|
|
||||||
const event = createMemo(() => textNote(props.event));
|
const event = createMemo(() => textNote(props.event));
|
||||||
@@ -100,10 +103,6 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
|||||||
const embedding = () => props.embedding ?? true;
|
const embedding = () => props.embedding ?? true;
|
||||||
const actions = () => props.actions ?? true;
|
const actions = () => props.actions ?? true;
|
||||||
|
|
||||||
const { profile: author } = useProfile(() => ({
|
|
||||||
pubkey: props.event.pubkey,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
reactions,
|
reactions,
|
||||||
reactionsGroupedByContent,
|
reactionsGroupedByContent,
|
||||||
@@ -189,11 +188,9 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: () => 'JSONとしてコピー',
|
content: () => 'JSONを確認',
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
navigator.clipboard
|
setShowEventDebug(true);
|
||||||
.writeText(JSON.stringify(props.event, null, 2))
|
|
||||||
.catch((err) => window.alert(err));
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -292,25 +289,7 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div class="nostr-textnote">
|
<div class="nostr-textnote">
|
||||||
<Post
|
<Post
|
||||||
author={
|
authorPubkey={event().pubkey}
|
||||||
<span class="author flex min-w-0 truncate hover:text-blue-500">
|
|
||||||
<Show when={(author()?.display_name?.length ?? 0) > 0}>
|
|
||||||
<div class="author-name truncate pr-1 font-bold hover:underline">
|
|
||||||
{author()?.display_name}
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
<div class="author-username truncate text-zinc-600">
|
|
||||||
<Show
|
|
||||||
when={author()?.name != null}
|
|
||||||
fallback={`@${npubEncodeFallback(event().pubkey)}`}
|
|
||||||
>
|
|
||||||
@{author()?.name}
|
|
||||||
</Show>
|
|
||||||
{/* TODO <Match when={author()?.nip05 != null}>@{author()?.nip05}</Match> */}
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
authorPictureUrl={author()?.picture}
|
|
||||||
createdAt={event().createdAtAsDate()}
|
createdAt={event().createdAtAsDate()}
|
||||||
content={
|
content={
|
||||||
<div class="textnote-content">
|
<div class="textnote-content">
|
||||||
@@ -458,6 +437,9 @@ const TextNoteDisplay: Component<TextNoteDisplayProps> = (props) => {
|
|||||||
timelineContext?.setTimeline({ type: 'Replies', event: props.event });
|
timelineContext?.setTimeline({ type: 'Replies', event: props.event });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Show when={showEventDebug()}>
|
||||||
|
<EventDebugModal event={props.event} onClose={() => setShowEventDebug(false)} />
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
28
src/components/modal/EventDebugModal.tsx
Normal file
28
src/components/modal/EventDebugModal.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Component, For, createMemo } from 'solid-js';
|
||||||
|
|
||||||
|
import { type Event as NostrEvent } from 'nostr-tools';
|
||||||
|
|
||||||
|
import BasicModal from '@/components/modal/BasicModal';
|
||||||
|
import Copy from '@/components/utils/Copy';
|
||||||
|
|
||||||
|
export type EventDebugModalProps = {
|
||||||
|
event: NostrEvent;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EventDebugModal: Component<EventDebugModalProps> = (props) => {
|
||||||
|
const json = createMemo(() => JSON.stringify(props.event, null, 2));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BasicModal onClose={props.onClose}>
|
||||||
|
<div class="p-2">
|
||||||
|
<pre class="whitespace-pre-wrap break-all rounded border p-4 text-xs">{json()}</pre>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<Copy class="h-4 w-4" text={json()} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</BasicModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventDebugModal;
|
||||||
@@ -77,6 +77,7 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
|||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
const following = () => myFollowingPubkeys().includes(props.pubkey);
|
const following = () => myFollowingPubkeys().includes(props.pubkey);
|
||||||
|
const refetchMyFollowing = () => myFollowingQuery.refetch();
|
||||||
|
|
||||||
const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings(
|
const { followingPubkeys: userFollowingPubkeys, query: userFollowingQuery } = useFollowings(
|
||||||
() => ({ pubkey: props.pubkey }),
|
() => ({ pubkey: props.pubkey }),
|
||||||
@@ -116,35 +117,58 @@ const ProfileDisplay: Component<ProfileDisplayProps> = (props) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const follow = () => {
|
const handlePromise =
|
||||||
|
<Params extends any[], T>(f: (...params: Params) => Promise<T>) =>
|
||||||
|
(onError: (err: any) => void) =>
|
||||||
|
(...params: Params) => {
|
||||||
|
f(...params).catch((err) => {
|
||||||
|
onError(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const follow = handlePromise(async () => {
|
||||||
const p = myPubkey();
|
const p = myPubkey();
|
||||||
if (p == null) return;
|
if (p == null) return;
|
||||||
if (!myFollowingQuery.isFetched) return;
|
if (!myFollowingQuery.isFetched) return;
|
||||||
|
|
||||||
|
await refetchMyFollowing();
|
||||||
updateContactsMutation.mutate({
|
updateContactsMutation.mutate({
|
||||||
relayUrls: config().relayUrls,
|
relayUrls: config().relayUrls,
|
||||||
pubkey: p,
|
pubkey: p,
|
||||||
content: myFollowingQuery.data?.content ?? '',
|
content: myFollowingQuery.data?.content ?? '',
|
||||||
followingPubkeys: uniq([...myFollowingPubkeys(), props.pubkey]),
|
followingPubkeys: uniq([...myFollowingPubkeys(), props.pubkey]),
|
||||||
});
|
});
|
||||||
};
|
})((err) => {
|
||||||
|
console.log('failed to follow', err);
|
||||||
|
});
|
||||||
|
|
||||||
const unfollow = () => {
|
const unfollow = handlePromise(async () => {
|
||||||
const p = myPubkey();
|
const p = myPubkey();
|
||||||
if (p == null) return;
|
if (p == null) return;
|
||||||
if (!myFollowingQuery.isFetched) return;
|
if (!myFollowingQuery.isFetched) return;
|
||||||
|
|
||||||
if (!window.confirm('本当にフォロー解除しますか?')) return;
|
if (!window.confirm('本当にフォロー解除しますか?')) return;
|
||||||
|
|
||||||
|
await refetchMyFollowing();
|
||||||
updateContactsMutation.mutate({
|
updateContactsMutation.mutate({
|
||||||
relayUrls: config().relayUrls,
|
relayUrls: config().relayUrls,
|
||||||
pubkey: p,
|
pubkey: p,
|
||||||
content: myFollowingQuery.data?.content ?? '',
|
content: myFollowingQuery.data?.content ?? '',
|
||||||
followingPubkeys: myFollowingPubkeys().filter((k) => k !== props.pubkey),
|
followingPubkeys: myFollowingPubkeys().filter((k) => k !== props.pubkey),
|
||||||
});
|
});
|
||||||
};
|
})((err) => {
|
||||||
|
console.log('failed to unfollow', err);
|
||||||
|
});
|
||||||
|
|
||||||
const menu: MenuItem[] = [
|
const menu: MenuItem[] = [
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
content: () => 'ユーザ宛に投稿',
|
||||||
|
onSelect: () => {
|
||||||
|
navigator.clipboard.writeText(npub()).catch((err) => window.alert(err));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
content: () => 'IDをコピー',
|
content: () => 'IDをコピー',
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ const ProfileEdit: Component<ProfileEditProps> = (props) => {
|
|||||||
const loading = () => query.isLoading || mutation.isLoading;
|
const loading = () => query.isLoading || mutation.isLoading;
|
||||||
const disabled = () => loading();
|
const disabled = () => loading();
|
||||||
|
|
||||||
|
setInterval(() => console.log(query.isLoading, mutation.isLoading), 1000);
|
||||||
|
|
||||||
const otherProperties = () =>
|
const otherProperties = () =>
|
||||||
omit(profile(), [
|
omit(profile(), [
|
||||||
'picture',
|
'picture',
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import ColumnItem from '@/components/ColumnItem';
|
|||||||
import Reaction from '@/components/event/Reaction';
|
import Reaction from '@/components/event/Reaction';
|
||||||
import Repost from '@/components/event/Repost';
|
import Repost from '@/components/event/Repost';
|
||||||
import TextNote from '@/components/event/TextNote';
|
import TextNote from '@/components/event/TextNote';
|
||||||
|
import ZapReceipt from '@/components/event/ZapReceipt';
|
||||||
import useConfig from '@/core/useConfig';
|
import useConfig from '@/core/useConfig';
|
||||||
|
|
||||||
export type NotificationProps = {
|
export type NotificationProps = {
|
||||||
@@ -36,6 +37,13 @@ const Notification: Component<NotificationProps> = (props) => {
|
|||||||
<Repost event={event} />
|
<Repost event={event} />
|
||||||
</ColumnItem>
|
</ColumnItem>
|
||||||
</Match>
|
</Match>
|
||||||
|
{/*
|
||||||
|
<Match when={event.kind === Kind.Zap}>
|
||||||
|
<ColumnItem>
|
||||||
|
<ZapReceipt event={event} />
|
||||||
|
</ColumnItem>
|
||||||
|
</Match>
|
||||||
|
*/}
|
||||||
</Switch>
|
</Switch>
|
||||||
</Show>
|
</Show>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createSignal, Show, type Component } from 'solid-js';
|
import { createSignal, Show, type Component, type JSX } from 'solid-js';
|
||||||
|
|
||||||
import ClipboardDocument from 'heroicons/24/outline/clipboard-document.svg';
|
import ClipboardDocument from 'heroicons/24/outline/clipboard-document.svg';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ type UseHandleCommandProps = {
|
|||||||
|
|
||||||
type CommandBase<T> = { command: T };
|
type CommandBase<T> = { command: T };
|
||||||
|
|
||||||
export type OpenPostForm = CommandBase<'openPostForm'>;
|
export type OpenPostForm = CommandBase<'openPostForm'> & { content?: string };
|
||||||
export type ClosePostForm = CommandBase<'closePostForm'>;
|
export type ClosePostForm = CommandBase<'closePostForm'>;
|
||||||
export type MoveToNextItem = CommandBase<'moveToNextItem'>;
|
export type MoveToNextItem = CommandBase<'moveToNextItem'>;
|
||||||
export type MoveToPrevItem = CommandBase<'moveToPrevItem'>;
|
export type MoveToPrevItem = CommandBase<'moveToPrevItem'>;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { createSignal, onMount } from 'solid-js';
|
import { createSignal, onMount } from 'solid-js';
|
||||||
|
|
||||||
|
// TODO Find a better way to solve this. Firefox on Windows can cause 2px gap.
|
||||||
|
const Offset = 2;
|
||||||
|
|
||||||
const useDetectOverflow = () => {
|
const useDetectOverflow = () => {
|
||||||
let elementRef: HTMLElement | undefined;
|
let elementRef: HTMLElement | undefined;
|
||||||
const [overflow, setOverflow] = createSignal(false);
|
const [overflow, setOverflow] = createSignal(false);
|
||||||
@@ -10,7 +13,7 @@ const useDetectOverflow = () => {
|
|||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (elementRef != null) {
|
if (elementRef != null) {
|
||||||
setOverflow(elementRef.scrollHeight > elementRef.clientHeight);
|
setOverflow(elementRef.scrollHeight > elementRef.clientHeight + Offset);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export type UseFollowings = {
|
|||||||
query: CreateQueryResult<NostrEvent | null>;
|
query: CreateQueryResult<NostrEvent | null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useFollowings = (propsProvider: () => UseFollowingsProps | null): UseFollowings => {
|
const useFollowings = (propsProvider: () => UseFollowingsProps | null): UseFollowings => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const props = createMemo(propsProvider);
|
const props = createMemo(propsProvider);
|
||||||
const genQueryKey = () => ['useFollowings', props()] as const;
|
const genQueryKey = () => ['useFollowings', props()] as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user