mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 22:44:26 +01:00
update
This commit is contained in:
@@ -19,17 +19,25 @@ const DeprecatedRepost: Component<DeprecatedRepostProps> = (props) => {
|
||||
const eventId = () => props.event.tags.find(([tagName]) => tagName === 'e')?.[1];
|
||||
|
||||
const { profile } = useProfile(() => ({ relayUrls: config().relayUrls, pubkey: pubkey() }));
|
||||
const { event } = useEvent(() => ({ relayUrls: config().relayUrls, eventId: eventId() }));
|
||||
const { event, query: eventQuery } = useEvent(() => ({
|
||||
relayUrls: config().relayUrls,
|
||||
eventId: eventId(),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div class="flex content-center px-1 text-xs">
|
||||
<div class="h-5 w-5 pr-1 text-green-500" aria-hidden="true">
|
||||
<div class="flex content-center px-1 text-xs">
|
||||
<div class="h-5 w-5 shrink-0 pr-1 text-green-500" aria-hidden="true">
|
||||
<ArrowPathRoundedSquare />
|
||||
</div>
|
||||
<div>{profile()?.display_name} Reposted</div>
|
||||
<div class="truncate">
|
||||
<Show when={(profile()?.display_name?.length ?? 0) > 0} fallback={props.event.pubkey}>
|
||||
{profile()?.display_name}
|
||||
</Show>
|
||||
{' Reposted'}
|
||||
</div>
|
||||
</div>
|
||||
<Show when={event() != null} fallback={'loading'}>
|
||||
<Show when={event() != null} fallback={<Show when={eventQuery.isLoading}>loading</Show>}>
|
||||
<TextNote event={event()} />
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
@@ -8,14 +8,17 @@ type NotePostFormProps = {
|
||||
const NotePostForm: Component<NotePostFormProps> = (props) => {
|
||||
const [text, setText] = createSignal<string>('');
|
||||
|
||||
const clearText = () => setText('');
|
||||
|
||||
const handleChangeText: JSX.EventHandler<HTMLTextAreaElement, Event> = (ev) => {
|
||||
setText(ev.currentTarget.value);
|
||||
};
|
||||
|
||||
const handleSubmit: JSX.EventHandler<HTMLFormElement, Event> = (ev) => {
|
||||
ev.preventDefault();
|
||||
// TODO 投稿完了したかどうかの検知をしたい
|
||||
props.onPost({ content: text() });
|
||||
// TODO 投稿完了したらなんかする
|
||||
clearText();
|
||||
};
|
||||
|
||||
const submitDisabled = createMemo(() => text().trim().length === 0);
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import {
|
||||
Show,
|
||||
For,
|
||||
createSignal,
|
||||
createMemo,
|
||||
onMount,
|
||||
onCleanup,
|
||||
type JSX,
|
||||
Component,
|
||||
} from 'solid-js';
|
||||
import { Show, For, createMemo, type JSX, type Component } from 'solid-js';
|
||||
import type { Event as NostrEvent } from 'nostr-tools/event';
|
||||
|
||||
import HeartOutlined from 'heroicons/24/outline/heart.svg';
|
||||
import HeartSolid from 'heroicons/24/solid/heart.svg';
|
||||
import ArrowPathRoundedSquare from 'heroicons/24/outline/arrow-path-rounded-square.svg';
|
||||
import ChatBubbleLeft from 'heroicons/24/outline/chat-bubble-left.svg';
|
||||
import EllipsisHorizontal from 'heroicons/24/outline/ellipsis-horizontal.svg';
|
||||
|
||||
import useProfile from '@/clients/useProfile';
|
||||
import useConfig from '@/clients/useConfig';
|
||||
import usePubkey from '@/clients/usePubkey';
|
||||
import useCommands from '@/clients/useCommands';
|
||||
import useReactions from '@/clients/useReactions';
|
||||
import useDatePulser from '@/hooks/useDatePulser';
|
||||
import { formatRelative } from '@/utils/formatDate';
|
||||
import ColumnItem from '@/components/ColumnItem';
|
||||
@@ -32,11 +26,24 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
||||
const currentDate = useDatePulser();
|
||||
const [config] = useConfig();
|
||||
const commands = useCommands();
|
||||
const pubkey = usePubkey();
|
||||
|
||||
const { profile: author } = useProfile(() => ({
|
||||
relayUrls: config().relayUrls,
|
||||
pubkey: props.event.pubkey,
|
||||
}));
|
||||
|
||||
const {
|
||||
reactions,
|
||||
isReactedBy,
|
||||
query: reactionsQuery,
|
||||
} = useReactions(() => ({
|
||||
relayUrls: config().relayUrls,
|
||||
eventId: props.event.id,
|
||||
}));
|
||||
|
||||
const isReactedByMe = createMemo(() => isReactedBy(pubkey()));
|
||||
|
||||
const replyingToPubKeys = createMemo(() =>
|
||||
props.event.tags.filter((tag) => tag[0] === 'p').map((e) => e[1]),
|
||||
);
|
||||
@@ -45,16 +52,31 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
||||
|
||||
const handleRepost: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (ev) => {
|
||||
ev.preventDefault();
|
||||
commands.publishDeprecatedRepost({});
|
||||
commands.publishDeprecatedRepost({
|
||||
relayUrls: config().relayUrls,
|
||||
pubkey: pubkey(),
|
||||
eventId: props.event.id,
|
||||
notifyPubkey: props.event.pubkey,
|
||||
});
|
||||
};
|
||||
|
||||
const handleReaction: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (ev) => {
|
||||
if (isReactedByMe()) {
|
||||
// TODO remove reaction
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
commands.publishReaction({
|
||||
relayUrls: config().relayUrls,
|
||||
pubkey: pubkeyHex,
|
||||
eventId: props.event.id,
|
||||
});
|
||||
commands
|
||||
.publishReaction({
|
||||
relayUrls: config().relayUrls,
|
||||
pubkey: pubkey(),
|
||||
content: '+',
|
||||
eventId: props.event.id,
|
||||
notifyPubkey: props.event.pubkey,
|
||||
})
|
||||
.then(() => {
|
||||
reactionsQuery.refetch();
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -74,8 +96,8 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
||||
<div class="flex justify-between gap-1 text-xs">
|
||||
<div class="author flex min-w-0 truncate">
|
||||
{/* TODO link to author */}
|
||||
<Show when={author()?.display_name != null && author()?.display_name.length > 0}>
|
||||
<div class="author-name pr-1 font-bold">{author()?.display_name}</div>
|
||||
<Show when={(author()?.display_name?.length ?? 0) > 0}>
|
||||
<div class="author-name truncate pr-1 font-bold">{author()?.display_name}</div>
|
||||
</Show>
|
||||
<div class="author-username truncate text-zinc-600">
|
||||
<Show when={author()?.name} fallback={props.event.pubkey}>
|
||||
@@ -89,9 +111,9 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
||||
<div class="text-xs">
|
||||
{'Replying to '}
|
||||
<For each={replyingToPubKeys()}>
|
||||
{(pubkey: string) => (
|
||||
{(replyToPubkey: string) => (
|
||||
<span class="pr-1 text-blue-500 underline">
|
||||
<GeneralUserMentionDisplay pubkey={pubkey} />
|
||||
<GeneralUserMentionDisplay pubkey={replyToPubkey} />
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
@@ -100,16 +122,27 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
||||
<div class="content whitespace-pre-wrap break-all">
|
||||
<TextNoteContentDisplay event={props.event} embedding={true} />
|
||||
</div>
|
||||
<div class="flex justify-end gap-16">
|
||||
<div class="flex w-48 items-center justify-between gap-8 pt-1">
|
||||
<button class="h-4 w-4 text-zinc-400">
|
||||
<ChatBubbleLeft />
|
||||
</button>
|
||||
<button class="h-4 w-4 text-zinc-400" onClick={handleRepost}>
|
||||
<ArrowPathRoundedSquare />
|
||||
</button>
|
||||
<button class="h-4 w-4 text-zinc-400" onClick={handleReaction}>
|
||||
<HeartOutlined />
|
||||
</button>
|
||||
<div
|
||||
class="flex items-center gap-1"
|
||||
classList={{
|
||||
'text-zinc-400': !isReactedByMe(),
|
||||
'text-rose-400': isReactedByMe(),
|
||||
}}
|
||||
>
|
||||
<button class="h-4 w-4" onClick={handleReaction}>
|
||||
<Show when={isReactedByMe()} fallback={<HeartOutlined />}>
|
||||
<HeartSolid />
|
||||
</Show>
|
||||
</button>
|
||||
<div class="text-sm text-zinc-400">{reactions().length}</div>
|
||||
</div>
|
||||
<button class="h-4 w-4 text-zinc-400">
|
||||
<EllipsisHorizontal />
|
||||
</button>
|
||||
|
||||
@@ -19,31 +19,54 @@ const Reaction: Component<ReactionProps> = (props) => {
|
||||
relayUrls: config().relayUrls,
|
||||
pubkey: props.event.pubkey,
|
||||
}));
|
||||
const { event } = useEvent(() => ({ relayUrls: config().relayUrls, eventId: eventId() }));
|
||||
const { event: reactedEvent, query: reactedEventQuery } = useEvent(() => ({
|
||||
relayUrls: config().relayUrls,
|
||||
eventId: eventId(),
|
||||
}));
|
||||
const isRemoved = () => reactedEventQuery.isSuccess && reactedEvent() == null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div class="flex gap-1 text-sm">
|
||||
<div>
|
||||
<Switch fallback={props.event.content}>
|
||||
<Match when={props.event.content === '+'}>
|
||||
<span class="inline-block h-4 w-4 text-rose-400">
|
||||
<HeartSolid />
|
||||
</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<div>
|
||||
<span class="font-bold">{profile()?.display_name}</span>
|
||||
{' reacted'}
|
||||
</div>
|
||||
</div>
|
||||
// if the reacted event is not found, it should be a removed event
|
||||
<Show when={!isRemoved()}>
|
||||
<div>
|
||||
<Show when={event() != null} fallback={'loading'}>
|
||||
<TextNote event={event()} />
|
||||
</Show>
|
||||
<div class="notification-icon flex gap-1 px-1 text-sm">
|
||||
<div class="flex place-items-center">
|
||||
<Switch fallback={props.event.content}>
|
||||
<Match when={props.event.content === '+'}>
|
||||
<span class="h-4 w-4 pt-[1px] text-rose-400">
|
||||
<HeartSolid />
|
||||
</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<div class="notification-user flex gap-1 pt-1">
|
||||
<div class="author-icon h-5 w-5 shrink-0">
|
||||
<Show when={profile()?.picture != null}>
|
||||
<img
|
||||
src={profile()?.picture}
|
||||
alt="icon"
|
||||
// TODO autofit
|
||||
class="rounded"
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
<div>
|
||||
<span class="truncate whitespace-pre-wrap break-all font-bold">
|
||||
<Show when={profile() != null} fallback={props.event.pubkey}>
|
||||
{profile()?.display_name}
|
||||
</Show>
|
||||
</span>
|
||||
{' reacted'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-event">
|
||||
<Show when={reactedEvent() != null} fallback="loading">
|
||||
<TextNote event={reactedEvent()} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user