mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 14:04:21 +01:00
update
This commit is contained in:
26
package-lock.json
generated
26
package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "nostiger",
|
"name": "rabbit",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "nostiger",
|
"name": "rabbit",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -16,7 +16,9 @@
|
|||||||
"@tanstack/react-query-persist-client": "^4.24.10",
|
"@tanstack/react-query-persist-client": "^4.24.10",
|
||||||
"@tanstack/solid-query": "^4.24.10",
|
"@tanstack/solid-query": "^4.24.10",
|
||||||
"@thisbeyond/solid-dnd": "^0.7.3",
|
"@thisbeyond/solid-dnd": "^0.7.3",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
"heroicons": "^2.0.15",
|
"heroicons": "^2.0.15",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"nostr-tools": "^1.3.2",
|
"nostr-tools": "^1.3.2",
|
||||||
"solid-js": "^1.6.9",
|
"solid-js": "^1.6.9",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
@@ -1492,6 +1494,11 @@
|
|||||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.14.191",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
|
||||||
|
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
|
||||||
|
},
|
||||||
"node_modules/@types/mocha": {
|
"node_modules/@types/mocha": {
|
||||||
"version": "10.0.1",
|
"version": "10.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
|
||||||
@@ -5281,6 +5288,11 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
"node_modules/lodash.flattendeep": {
|
"node_modules/lodash.flattendeep": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
||||||
@@ -9532,6 +9544,11 @@
|
|||||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/lodash": {
|
||||||
|
"version": "4.14.191",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
|
||||||
|
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
|
||||||
|
},
|
||||||
"@types/mocha": {
|
"@types/mocha": {
|
||||||
"version": "10.0.1",
|
"version": "10.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
|
||||||
@@ -12263,6 +12280,11 @@
|
|||||||
"p-locate": "^5.0.0"
|
"p-locate": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
"lodash.flattendeep": {
|
"lodash.flattendeep": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
|
||||||
|
|||||||
@@ -54,7 +54,9 @@
|
|||||||
"@tanstack/react-query-persist-client": "^4.24.10",
|
"@tanstack/react-query-persist-client": "^4.24.10",
|
||||||
"@tanstack/solid-query": "^4.24.10",
|
"@tanstack/solid-query": "^4.24.10",
|
||||||
"@thisbeyond/solid-dnd": "^0.7.3",
|
"@thisbeyond/solid-dnd": "^0.7.3",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
"heroicons": "^2.0.15",
|
"heroicons": "^2.0.15",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"nostr-tools": "^1.3.2",
|
"nostr-tools": "^1.3.2",
|
||||||
"solid-js": "^1.6.9",
|
"solid-js": "^1.6.9",
|
||||||
"tailwindcss": "^3.2.4",
|
"tailwindcss": "^3.2.4",
|
||||||
|
|||||||
@@ -46,16 +46,33 @@ const useCommands = () => {
|
|||||||
relayUrls,
|
relayUrls,
|
||||||
pubkey,
|
pubkey,
|
||||||
content,
|
content,
|
||||||
|
notifyPubkeys,
|
||||||
|
rootEventId,
|
||||||
|
mentionEventIds,
|
||||||
|
replyEventId,
|
||||||
}: {
|
}: {
|
||||||
relayUrls: string[];
|
relayUrls: string[];
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
notifyPubkeys?: string[];
|
||||||
|
rootEventId?: string;
|
||||||
|
mentionEventIds?: string[];
|
||||||
|
replyEventId?: string;
|
||||||
}): Promise<Promise<void>[]> {
|
}): Promise<Promise<void>[]> {
|
||||||
|
const pTags = notifyPubkeys?.map((p) => ['p', p]) ?? [];
|
||||||
|
const eTags = [];
|
||||||
|
if (rootEventId != null) eTags.push(['e', rootEventId, '', 'root']);
|
||||||
|
if (mentionEventIds != null)
|
||||||
|
mentionEventIds.forEach((id) => eTags.push(['e', id, '', 'mention']));
|
||||||
|
if (replyEventId != null) eTags.push(['e', replyEventId, '', 'reply']);
|
||||||
|
|
||||||
|
const tags = [...pTags, ...eTags];
|
||||||
|
|
||||||
const preSignedEvent: NostrEvent = {
|
const preSignedEvent: NostrEvent = {
|
||||||
kind: 1,
|
kind: 1,
|
||||||
pubkey,
|
pubkey,
|
||||||
created_at: currentDate(),
|
created_at: currentDate(),
|
||||||
tags: [],
|
tags,
|
||||||
content,
|
content,
|
||||||
};
|
};
|
||||||
return publishEvent(relayUrls, preSignedEvent);
|
return publishEvent(relayUrls, preSignedEvent);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const { exec } = useBatchedEvents<UseDeprecatedRepostsProps>(() => ({
|
|||||||
const useDeprecatedReposts = (
|
const useDeprecatedReposts = (
|
||||||
propsProvider: () => UseDeprecatedRepostsProps,
|
propsProvider: () => UseDeprecatedRepostsProps,
|
||||||
): UseDeprecatedReposts => {
|
): UseDeprecatedReposts => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const props = createMemo(propsProvider);
|
const props = createMemo(propsProvider);
|
||||||
const queryKey = createMemo(() => ['useDeprecatedReposts', props()] as const);
|
const queryKey = createMemo(() => ['useDeprecatedReposts', props()] as const);
|
||||||
|
|
||||||
@@ -55,10 +56,8 @@ const useDeprecatedReposts = (
|
|||||||
const isRepostedBy = (pubkey: string): boolean =>
|
const isRepostedBy = (pubkey: string): boolean =>
|
||||||
reposts().findIndex((event) => event.pubkey === pubkey) !== -1;
|
reposts().findIndex((event) => event.pubkey === pubkey) !== -1;
|
||||||
|
|
||||||
const invalidateDeprecatedReposts = (): Promise<void> => {
|
const invalidateDeprecatedReposts = (): Promise<void> =>
|
||||||
const queryClient = useQueryClient();
|
queryClient.invalidateQueries(queryKey());
|
||||||
return queryClient.invalidateQueries(queryKey());
|
|
||||||
};
|
|
||||||
|
|
||||||
return { reposts, isRepostedBy, invalidateDeprecatedReposts, query };
|
return { reposts, isRepostedBy, invalidateDeprecatedReposts, query };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const { exec } = useBatchedEvents<UseReactionsProps>(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const useReactions = (propsProvider: () => UseReactionsProps): UseReactions => {
|
const useReactions = (propsProvider: () => UseReactionsProps): UseReactions => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const props = createMemo(propsProvider);
|
const props = createMemo(propsProvider);
|
||||||
const queryKey = createMemo(() => ['useReactions', props()] as const);
|
const queryKey = createMemo(() => ['useReactions', props()] as const);
|
||||||
|
|
||||||
@@ -61,10 +62,7 @@ const useReactions = (propsProvider: () => UseReactionsProps): UseReactions => {
|
|||||||
const isReactedBy = (pubkey: string): boolean =>
|
const isReactedBy = (pubkey: string): boolean =>
|
||||||
reactions().findIndex((event) => event.pubkey === pubkey) !== -1;
|
reactions().findIndex((event) => event.pubkey === pubkey) !== -1;
|
||||||
|
|
||||||
const invalidateReactions = (): Promise<void> => {
|
const invalidateReactions = (): Promise<void> => queryClient.invalidateQueries(queryKey());
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return queryClient.invalidateQueries(queryKey());
|
|
||||||
};
|
|
||||||
|
|
||||||
return { reactions, reactionsGroupedByContent, isReactedBy, invalidateReactions, query };
|
return { reactions, reactionsGroupedByContent, isReactedBy, invalidateReactions, query };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ type ColumnItemProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ColumnItem: Component<ColumnItemProps> = (props) => {
|
const ColumnItem: Component<ColumnItemProps> = (props) => {
|
||||||
return (
|
return <div class="overflow-hidden border-b p-1">{props.children}</div>;
|
||||||
<div class="flex w-full flex-row gap-1 overflow-hidden border-b p-1">{props.children}</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ColumnItem;
|
export default ColumnItem;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import useConfig from '@/clients/useConfig';
|
|||||||
import useEvent from '@/clients/useEvent';
|
import useEvent from '@/clients/useEvent';
|
||||||
import useProfile from '@/clients/useProfile';
|
import useProfile from '@/clients/useProfile';
|
||||||
|
|
||||||
import UserNameDisplay from '@/components/UserNameDisplay';
|
import UserDisplayName from '@/components/UserDisplayName';
|
||||||
import TextNote from '@/components/TextNote';
|
import TextNote from '@/components/TextNote';
|
||||||
|
|
||||||
export type DeprecatedRepostProps = {
|
export type DeprecatedRepostProps = {
|
||||||
@@ -32,7 +32,7 @@ const DeprecatedRepost: Component<DeprecatedRepostProps> = (props) => {
|
|||||||
<ArrowPathRoundedSquare />
|
<ArrowPathRoundedSquare />
|
||||||
</div>
|
</div>
|
||||||
<div class="truncate break-all">
|
<div class="truncate break-all">
|
||||||
<UserNameDisplay pubkey={props.event.pubkey} />
|
<UserDisplayName pubkey={props.event.pubkey} />
|
||||||
{' Reposted'}
|
{' Reposted'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const NotePostForm: Component<NotePostFormProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="p-1">
|
<div class="p-1">
|
||||||
<form class="grid w-64 gap-1" onSubmit={handleSubmit}>
|
<form class="flex flex-col gap-1" onSubmit={handleSubmit}>
|
||||||
<textarea
|
<textarea
|
||||||
name="text"
|
name="text"
|
||||||
class="rounded border-none"
|
class="rounded border-none"
|
||||||
|
|||||||
@@ -1,6 +1,62 @@
|
|||||||
import { type Component } from 'solid-js';
|
import { createSignal, createMemo, type Component, type JSX } from 'solid-js';
|
||||||
|
import type { Event as NostrEvent } from 'nostr-tools/event';
|
||||||
|
|
||||||
const ReplyPostForm = () => {
|
import PaperAirplane from 'heroicons/24/solid/paper-airplane.svg';
|
||||||
|
|
||||||
|
type ReplyPostFormProps = {
|
||||||
|
replyTo: NostrEvent;
|
||||||
|
onPost: (textNote: { content: string }) => void;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReplyPostForm: Component<ReplyPostFormProps> = (props: ReplyPostFormProps) => {
|
||||||
|
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() });
|
||||||
|
clearText();
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitDisabled = createMemo(() => text().trim().length === 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="p-1">
|
||||||
|
<div>
|
||||||
|
{'Replying to '}
|
||||||
|
{props.replyTo.pubkey}
|
||||||
|
</div>
|
||||||
|
<form class="grid w-full gap-1" onSubmit={handleSubmit}>
|
||||||
|
<textarea
|
||||||
|
name="text"
|
||||||
|
class="rounded border-none"
|
||||||
|
rows={4}
|
||||||
|
placeholder="返信を投稿"
|
||||||
|
onInput={handleChangeText}
|
||||||
|
value={text()}
|
||||||
|
/>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
{/* TODO あとでちゃんとアイコンにする */}
|
||||||
|
<button onClick={() => props.onClose()}>X</button>
|
||||||
|
<button
|
||||||
|
class="h-7 w-7 rounded bg-primary p-2 font-bold text-white"
|
||||||
|
classList={{ 'bg-primary-disabled': submitDisabled(), 'bg-primary': !submitDisabled() }}
|
||||||
|
type="submit"
|
||||||
|
disabled={submitDisabled()}
|
||||||
|
>
|
||||||
|
<PaperAirplane />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ReplyPostForm;
|
export default ReplyPostForm;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const SideBar: Component<SideBarProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex shrink-0 flex-row border-r bg-sidebar-bg">
|
<div class="flex shrink-0 flex-row border-r bg-sidebar-bg">
|
||||||
<div class="flex w-14 flex-auto flex-col items-center gap-3 border-r py-5">
|
<div class="flex w-14 flex-auto flex-col items-center gap-3 border-r border-rose-200 py-5">
|
||||||
<button
|
<button
|
||||||
class={`h-9 w-9 rounded-full border border-primary bg-primary p-2 text-2xl font-bold text-white`}
|
class={`h-9 w-9 rounded-full border border-primary bg-primary p-2 text-2xl font-bold text-white`}
|
||||||
onClick={() => setFormOpened((current) => !current)}
|
onClick={() => setFormOpened((current) => !current)}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Show, For, createMemo, type JSX, type Component } from 'solid-js';
|
import { Show, For, createSignal, createMemo, type JSX, type Component } from 'solid-js';
|
||||||
import type { Event as NostrEvent } from 'nostr-tools/event';
|
import type { Event as NostrEvent } from 'nostr-tools/event';
|
||||||
|
import uniq from 'lodash/uniq';
|
||||||
|
|
||||||
import HeartOutlined from 'heroicons/24/outline/heart.svg';
|
import HeartOutlined from 'heroicons/24/outline/heart.svg';
|
||||||
import HeartSolid from 'heroicons/24/solid/heart.svg';
|
import HeartSolid from 'heroicons/24/solid/heart.svg';
|
||||||
@@ -18,6 +19,7 @@ import { formatRelative } from '@/utils/formatDate';
|
|||||||
import ColumnItem from '@/components/ColumnItem';
|
import ColumnItem from '@/components/ColumnItem';
|
||||||
import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionDisplay';
|
import GeneralUserMentionDisplay from '@/components/textNote/GeneralUserMentionDisplay';
|
||||||
import TextNoteContentDisplay from '@/components/textNote/TextNoteContentDisplay';
|
import TextNoteContentDisplay from '@/components/textNote/TextNoteContentDisplay';
|
||||||
|
import ReplyPostForm from '@/components/ReplyPostForm';
|
||||||
|
|
||||||
export type TextNoteProps = {
|
export type TextNoteProps = {
|
||||||
event: NostrEvent;
|
event: NostrEvent;
|
||||||
@@ -28,6 +30,7 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
|||||||
const [config] = useConfig();
|
const [config] = useConfig();
|
||||||
const commands = useCommands();
|
const commands = useCommands();
|
||||||
const pubkey = usePubkey();
|
const pubkey = usePubkey();
|
||||||
|
const [showReplyForm, setShowReplyForm] = createSignal(false);
|
||||||
|
|
||||||
const { profile: author } = useProfile(() => ({
|
const { profile: author } = useProfile(() => ({
|
||||||
relayUrls: config().relayUrls,
|
relayUrls: config().relayUrls,
|
||||||
@@ -48,11 +51,24 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
|||||||
const isRepostedByMe = createMemo(() => isRepostedBy(pubkey()));
|
const isRepostedByMe = createMemo(() => isRepostedBy(pubkey()));
|
||||||
|
|
||||||
const replyingToPubKeys = createMemo(() =>
|
const replyingToPubKeys = createMemo(() =>
|
||||||
props.event.tags.filter((tag) => tag[0] === 'p').map((e) => e[1]),
|
uniq(props.event.tags.filter((tag) => tag[0] === 'p').map((e) => e[1])),
|
||||||
);
|
);
|
||||||
// TODO 日付をいい感じにフォーマットする関数を作る
|
|
||||||
const createdAt = () => formatRelative(new Date(props.event.created_at * 1000), currentDate());
|
const createdAt = () => formatRelative(new Date(props.event.created_at * 1000), currentDate());
|
||||||
|
|
||||||
|
const handleReplyPost = ({ content }: { content: string }) => {
|
||||||
|
commands
|
||||||
|
.publishTextNote({
|
||||||
|
relayUrls: config().relayUrls,
|
||||||
|
pubkey: pubkey(),
|
||||||
|
content,
|
||||||
|
notifyPubkeys: [props.event.pubkey, ...replyingToPubKeys()],
|
||||||
|
replyEventId: props.event.id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setShowReplyForm(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleRepost: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (ev) => {
|
const handleRepost: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
@@ -87,6 +103,8 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div class="textnote">
|
<div class="textnote">
|
||||||
<ColumnItem>
|
<ColumnItem>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex w-full gap-1">
|
||||||
<div class="author-icon h-10 w-10 shrink-0">
|
<div class="author-icon h-10 w-10 shrink-0">
|
||||||
<Show when={author()?.picture}>
|
<Show when={author()?.picture}>
|
||||||
{/* TODO 画像は脆弱性回避のためにimgじゃない方法で読み込みたい */}
|
{/* TODO 画像は脆弱性回避のためにimgじゃない方法で読み込みたい */}
|
||||||
@@ -129,7 +147,10 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
|||||||
<TextNoteContentDisplay event={props.event} embedding={true} />
|
<TextNoteContentDisplay event={props.event} embedding={true} />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-48 items-center justify-between gap-8 pt-1">
|
<div class="flex w-48 items-center justify-between gap-8 pt-1">
|
||||||
<button class="h-4 w-4 text-zinc-400">
|
<button
|
||||||
|
class="h-4 w-4 text-zinc-400"
|
||||||
|
onClick={() => setShowReplyForm((current) => !current)}
|
||||||
|
>
|
||||||
<ChatBubbleLeft />
|
<ChatBubbleLeft />
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
@@ -167,6 +188,15 @@ const TextNote: Component<TextNoteProps> = (props) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<Show when={showReplyForm()}>
|
||||||
|
<ReplyPostForm
|
||||||
|
replyTo={props.event}
|
||||||
|
onPost={handleReplyPost}
|
||||||
|
onClose={() => setShowReplyForm(false)}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
</ColumnItem>
|
</ColumnItem>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Switch, Match, type Component, Show } from 'solid-js';
|
|||||||
import { type Event as NostrEvent } from 'nostr-tools/event';
|
import { type Event as NostrEvent } from 'nostr-tools/event';
|
||||||
import HeartSolid from 'heroicons/24/solid/heart.svg';
|
import HeartSolid from 'heroicons/24/solid/heart.svg';
|
||||||
|
|
||||||
import UserNameDisplay from '@/components/UserNameDisplay';
|
import UserDisplayName from '@/components/UserDisplayName';
|
||||||
import TextNote from '@/components/TextNote';
|
import TextNote from '@/components/TextNote';
|
||||||
|
|
||||||
import useConfig from '@/clients/useConfig';
|
import useConfig from '@/clients/useConfig';
|
||||||
@@ -54,7 +54,7 @@ const Reaction: Component<ReactionProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="truncate whitespace-pre-wrap break-all font-bold">
|
<span class="truncate whitespace-pre-wrap break-all font-bold">
|
||||||
<UserNameDisplay pubkey={props.event.pubkey} />
|
<UserDisplayName pubkey={props.event.pubkey} />
|
||||||
</span>
|
</span>
|
||||||
{' reacted'}
|
{' reacted'}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user