mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-17 22:14:26 +01:00
update
This commit is contained in:
50
src/components/column/BookmarkColumn.tsx
Normal file
50
src/components/column/BookmarkColumn.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { Component, Show, createEffect, onCleanup, onMount } from 'solid-js';
|
||||||
|
|
||||||
|
import BookmarkIcon from 'heroicons/24/outline/bookmark.svg';
|
||||||
|
|
||||||
|
import BasicColumnHeader from '@/components/column/BasicColumnHeader';
|
||||||
|
import Column from '@/components/column/Column';
|
||||||
|
import ColumnSettings from '@/components/column/ColumnSettings';
|
||||||
|
import Bookmark from '@/components/timeline/Bookmark';
|
||||||
|
import { BookmarkColumnType } from '@/core/column';
|
||||||
|
import useConfig from '@/core/useConfig';
|
||||||
|
import useDecrypt from '@/nostr/useDecrypt';
|
||||||
|
import useParameterizedReplaceableEvent from '@/nostr/useParameterizedReplaceableEvent';
|
||||||
|
|
||||||
|
type BookmarkColumnDisplayProps = {
|
||||||
|
columnIndex: number;
|
||||||
|
lastColumn: boolean;
|
||||||
|
column: BookmarkColumnType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BookmarkColumn: Component<BookmarkColumnDisplayProps> = (props) => {
|
||||||
|
const { removeColumn } = useConfig();
|
||||||
|
|
||||||
|
const { event } = useParameterizedReplaceableEvent(() => ({
|
||||||
|
kind: 30001,
|
||||||
|
author: props.column.pubkey,
|
||||||
|
identifier: props.column.identifier,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column
|
||||||
|
header={
|
||||||
|
<BasicColumnHeader
|
||||||
|
name={props.column.name ?? 'ブックマーク'}
|
||||||
|
icon={<BookmarkIcon />}
|
||||||
|
settings={() => <ColumnSettings column={props.column} columnIndex={props.columnIndex} />}
|
||||||
|
onClose={() => removeColumn(props.column.id)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
width={props.column.width}
|
||||||
|
columnIndex={props.columnIndex}
|
||||||
|
lastColumn={props.lastColumn}
|
||||||
|
>
|
||||||
|
<Show when={event()} keyed>
|
||||||
|
{(ev) => <Bookmark event={ev} />}
|
||||||
|
</Show>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BookmarkColumn;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { For, Switch, Match } from 'solid-js';
|
import { For, Switch, Match } from 'solid-js';
|
||||||
|
|
||||||
|
import BookmarkColumn from '@/components/column/BookmarkColumn';
|
||||||
import FollowingColumn from '@/components/column/FollwingColumn';
|
import FollowingColumn from '@/components/column/FollwingColumn';
|
||||||
import NotificationColumn from '@/components/column/NotificationColumn';
|
import NotificationColumn from '@/components/column/NotificationColumn';
|
||||||
import PostsColumn from '@/components/column/PostsColumn';
|
import PostsColumn from '@/components/column/PostsColumn';
|
||||||
@@ -64,6 +65,15 @@ const Columns = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
|
<Match when={column.columnType === 'Bookmark' && column} keyed>
|
||||||
|
{(bookmarkColumn) => (
|
||||||
|
<BookmarkColumn
|
||||||
|
column={bookmarkColumn}
|
||||||
|
columnIndex={columnIndex()}
|
||||||
|
lastColumn={lastColumn()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Match>
|
||||||
<Match when={column.columnType === 'Search' && column} keyed>
|
<Match when={column.columnType === 'Search' && column} keyed>
|
||||||
{(reactionsColumn) => (
|
{(reactionsColumn) => (
|
||||||
<SearchColumn
|
<SearchColumn
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { createSearchColumn } from '@/core/column';
|
|||||||
import useConfig from '@/core/useConfig';
|
import useConfig from '@/core/useConfig';
|
||||||
import { useRequestCommand } from '@/hooks/useCommandBus';
|
import { useRequestCommand } from '@/hooks/useCommandBus';
|
||||||
import { textNote } from '@/nostr/event';
|
import { textNote } from '@/nostr/event';
|
||||||
import parseTextNote, { type ParsedTextNoteNode } from '@/nostr/parseTextNote';
|
import { type ParsedTextNoteNode } from '@/nostr/parseTextNote';
|
||||||
import { isImageUrl } from '@/utils/imageUrl';
|
import { isImageUrl } from '@/utils/imageUrl';
|
||||||
|
|
||||||
export type TextNoteContentDisplayProps = {
|
export type TextNoteContentDisplayProps = {
|
||||||
@@ -112,6 +112,7 @@ const TextNoteContentDisplay = (props: TextNoteContentDisplayProps) => {
|
|||||||
class="inline-block h-8 max-w-[128px] align-middle"
|
class="inline-block h-8 max-w-[128px] align-middle"
|
||||||
src={emojiUrl}
|
src={emojiUrl}
|
||||||
alt={item.content}
|
alt={item.content}
|
||||||
|
title={item.shortcode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Component } from 'solid-js';
|
import { Component } from 'solid-js';
|
||||||
|
|
||||||
import Bell from 'heroicons/24/outline/bell.svg';
|
import Bell from 'heroicons/24/outline/bell.svg';
|
||||||
|
import BookmarkIcon from 'heroicons/24/outline/bookmark.svg';
|
||||||
import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
|
import ChatBubbleLeftRight from 'heroicons/24/outline/chat-bubble-left-right.svg';
|
||||||
import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
|
import GlobeAlt from 'heroicons/24/outline/globe-alt.svg';
|
||||||
import Heart from 'heroicons/24/outline/heart.svg';
|
import Heart from 'heroicons/24/outline/heart.svg';
|
||||||
@@ -115,6 +116,17 @@ const AddColumn: Component<AddColumnProps> = (props) => {
|
|||||||
チャンネル
|
チャンネル
|
||||||
</button>
|
</button>
|
||||||
*/}
|
*/}
|
||||||
|
{/*
|
||||||
|
<button
|
||||||
|
class="flex basis-1/2 flex-col items-center gap-2 py-8 sm:basis-1/4"
|
||||||
|
onClick={() => addBookmarkColumn()}
|
||||||
|
>
|
||||||
|
<span class="inline-block h-8 w-8">
|
||||||
|
<BookmarkIcon />
|
||||||
|
</span>
|
||||||
|
ブックマーク
|
||||||
|
</button>
|
||||||
|
*/}
|
||||||
<button
|
<button
|
||||||
class="flex basis-1/2 flex-col items-center gap-2 py-8 sm:basis-1/4"
|
class="flex basis-1/2 flex-col items-center gap-2 py-8 sm:basis-1/4"
|
||||||
onClick={() => addSearchColumn()}
|
onClick={() => addSearchColumn()}
|
||||||
|
|||||||
50
src/components/timeline/Bookmark.tsx
Normal file
50
src/components/timeline/Bookmark.tsx
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { For, type Component, createMemo } from 'solid-js';
|
||||||
|
|
||||||
|
import { Kind, type Event as NostrEvent } from 'nostr-tools';
|
||||||
|
|
||||||
|
import ColumnItem from '@/components/ColumnItem';
|
||||||
|
import EventDisplayById from '@/components/event/EventDisplayById';
|
||||||
|
import { genericEvent } from '@/nostr/event';
|
||||||
|
import { parseTags } from '@/nostr/event/Tags';
|
||||||
|
import useDecrypt from '@/nostr/useDecrypt';
|
||||||
|
|
||||||
|
export type BookmarkProps = {
|
||||||
|
event: NostrEvent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Bookmark: Component<BookmarkProps> = (props) => {
|
||||||
|
const bookmark = createMemo(() => genericEvent(props.event));
|
||||||
|
|
||||||
|
const decrypted = useDecrypt(() => {
|
||||||
|
const { content } = bookmark();
|
||||||
|
if (content == null || content.length === 0) return null;
|
||||||
|
return { id: bookmark().id, encrypted: content };
|
||||||
|
});
|
||||||
|
|
||||||
|
const bookmarkedEventIdsPrivate = () => {
|
||||||
|
const json = decrypted();
|
||||||
|
if (json == null) return [];
|
||||||
|
console.log(json);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return parseTags(json).taggedEventIds();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const bookmarkedEventIds = () => bookmark().taggedEventIds();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<For each={[...bookmarkedEventIds(), ...bookmarkedEventIdsPrivate()]}>
|
||||||
|
{(eventId) => (
|
||||||
|
<ColumnItem>
|
||||||
|
<EventDisplayById eventId={eventId} ensureKinds={[Kind.Text]} />
|
||||||
|
</ColumnItem>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Bookmark;
|
||||||
@@ -95,6 +95,13 @@ export type SearchColumnType = BaseColumn & {
|
|||||||
query: string;
|
query: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** A column which shows events in the bookmark */
|
||||||
|
export type BookmarkColumnType = BaseColumn & {
|
||||||
|
columnType: 'Bookmark';
|
||||||
|
pubkey: string;
|
||||||
|
identifier: string;
|
||||||
|
};
|
||||||
|
|
||||||
/** A column which shows text notes and reposts posted to the specific relays */
|
/** A column which shows text notes and reposts posted to the specific relays */
|
||||||
export type CustomFilterColumnType = BaseColumn & {
|
export type CustomFilterColumnType = BaseColumn & {
|
||||||
columnType: 'CustomFilter';
|
columnType: 'CustomFilter';
|
||||||
@@ -109,6 +116,7 @@ export type ColumnType =
|
|||||||
| ChannelColumnType
|
| ChannelColumnType
|
||||||
| RelaysColumnType
|
| RelaysColumnType
|
||||||
| SearchColumnType
|
| SearchColumnType
|
||||||
|
| BookmarkColumnType
|
||||||
| CustomFilterColumnType;
|
| CustomFilterColumnType;
|
||||||
|
|
||||||
type CreateParams<T extends BaseColumn> = Omit<T, keyof BaseColumn | 'columnType'> &
|
type CreateParams<T extends BaseColumn> = Omit<T, keyof BaseColumn | 'columnType'> &
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import uniq from 'lodash/uniq';
|
|
||||||
import { Kind, Event as NostrEvent } from 'nostr-tools';
|
import { Kind, Event as NostrEvent } from 'nostr-tools';
|
||||||
|
|
||||||
import isValidId from '@/nostr/event/isValidId';
|
import TagsBase from '@/nostr/event/TagsBase';
|
||||||
|
|
||||||
export default class GenericEvent {
|
export default class GenericEvent extends TagsBase {
|
||||||
constructor(readonly rawEvent: NostrEvent) {}
|
constructor(readonly rawEvent: NostrEvent) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
get id(): string {
|
get id(): string {
|
||||||
return this.rawEvent.id;
|
return this.rawEvent.id;
|
||||||
@@ -37,39 +38,4 @@ export default class GenericEvent {
|
|||||||
createdAtAsDate(): Date {
|
createdAtAsDate(): Date {
|
||||||
return new Date(this.rawEvent.created_at * 1000);
|
return new Date(this.rawEvent.created_at * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
findTagsByName(name: string): string[][] {
|
|
||||||
return this.rawEvent.tags.filter(([tagName]) => tagName === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
findFirstTagByName(name: string): string[] | undefined {
|
|
||||||
return this.rawEvent.tags.find(([tagName]) => tagName === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
findLastTagByName(name: string): string[] | undefined {
|
|
||||||
return this.rawEvent.tags.findLast(([tagName]) => tagName === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
pTags(): string[][] {
|
|
||||||
return this.rawEvent.tags.filter(([tagName, pubkey]) => tagName === 'p' && isValidId(pubkey));
|
|
||||||
}
|
|
||||||
|
|
||||||
eTags(): string[][] {
|
|
||||||
return this.rawEvent.tags.filter(([tagName, eventId]) => tagName === 'e' && isValidId(eventId));
|
|
||||||
}
|
|
||||||
|
|
||||||
taggedPubkeys(): string[] {
|
|
||||||
return uniq(this.pTags().map(([, pubkey]) => pubkey));
|
|
||||||
}
|
|
||||||
|
|
||||||
taggedEventIds(): string[] {
|
|
||||||
return this.eTags().map(([, eventId]) => eventId);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTaggedEventId(): string | undefined {
|
|
||||||
// for compatibility. some clients include additional event ids for reaction (kind:7).
|
|
||||||
const ids = this.taggedEventIds();
|
|
||||||
if (ids.length === 0) return undefined;
|
|
||||||
return ids[ids.length - 1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/nostr/event/Tags.ts
Normal file
20
src/nostr/event/Tags.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import TagsBase from '@/nostr/event/TagsBase';
|
||||||
|
|
||||||
|
const TagsSchema = z.array(z.array(z.string()));
|
||||||
|
|
||||||
|
export default class Tags extends TagsBase {
|
||||||
|
constructor(readonly tags: string[][]) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseTags = (content: string): Tags => {
|
||||||
|
try {
|
||||||
|
const tags = TagsSchema.parse(JSON.parse(content));
|
||||||
|
return new Tags(tags);
|
||||||
|
} catch (err) {
|
||||||
|
throw new TypeError('failed to parse tags schema: ', { cause: err });
|
||||||
|
}
|
||||||
|
};
|
||||||
42
src/nostr/event/TagsBase.ts
Normal file
42
src/nostr/event/TagsBase.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import uniq from 'lodash/uniq';
|
||||||
|
|
||||||
|
import isValidId from '@/nostr/event/isValidId';
|
||||||
|
|
||||||
|
export default abstract class TagsBase {
|
||||||
|
abstract get tags(): string[][];
|
||||||
|
|
||||||
|
findTagsByName(name: string): string[][] {
|
||||||
|
return this.tags.filter(([tagName]) => tagName === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
findFirstTagByName(name: string): string[] | undefined {
|
||||||
|
return this.tags.find(([tagName]) => tagName === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
findLastTagByName(name: string): string[] | undefined {
|
||||||
|
return this.tags.findLast(([tagName]) => tagName === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
pTags(): string[][] {
|
||||||
|
return this.tags.filter(([tagName, pubkey]) => tagName === 'p' && isValidId(pubkey));
|
||||||
|
}
|
||||||
|
|
||||||
|
eTags(): string[][] {
|
||||||
|
return this.tags.filter(([tagName, eventId]) => tagName === 'e' && isValidId(eventId));
|
||||||
|
}
|
||||||
|
|
||||||
|
taggedPubkeys(): string[] {
|
||||||
|
return uniq(this.pTags().map(([, pubkey]) => pubkey));
|
||||||
|
}
|
||||||
|
|
||||||
|
taggedEventIds(): string[] {
|
||||||
|
return this.eTags().map(([, eventId]) => eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTaggedEventId(): string | undefined {
|
||||||
|
// for compatibility. some clients include additional event ids for reaction (kind:7).
|
||||||
|
const ids = this.taggedEventIds();
|
||||||
|
if (ids.length === 0) return undefined;
|
||||||
|
return ids[ids.length - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,7 +125,7 @@ export const { exec } = useBatch<TaskArg, TaskRes>(() => ({
|
|||||||
const {
|
const {
|
||||||
args: { kind, author, identifier },
|
args: { kind, author, identifier },
|
||||||
} = firstTask;
|
} = firstTask;
|
||||||
filters.push({ kinds: [Kind.Contacts], authors: [author], '#d': [identifier] });
|
filters.push({ kinds: [kind], authors: [author], '#d': [identifier] });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/nostr/useBookMarks.ts
Normal file
23
src/nostr/useBookMarks.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { createMemo } from 'solid-js';
|
||||||
|
|
||||||
|
import { Kind } from 'nostr-tools';
|
||||||
|
|
||||||
|
import useConfig from '@/core/useConfig';
|
||||||
|
import useSubscription from '@/nostr/useSubscription';
|
||||||
|
|
||||||
|
export type UseBookmarksProps = {
|
||||||
|
pubkey: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function useBookmarks(propsProvider: () => UseBookmarksProps) {
|
||||||
|
const { config } = useConfig();
|
||||||
|
const props = createMemo(propsProvider);
|
||||||
|
|
||||||
|
const { events } = useSubscription(() => ({
|
||||||
|
relayUrls: config().relayUrls,
|
||||||
|
filters: [{ kinds: [30001 as Kind], authors: [props().pubkey] }],
|
||||||
|
continuous: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return { bookmarks: events };
|
||||||
|
}
|
||||||
49
src/nostr/useDecrypt.ts
Normal file
49
src/nostr/useDecrypt.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { createEffect, createRoot, createSignal } from 'solid-js';
|
||||||
|
|
||||||
|
import usePubkey from '@/nostr/usePubkey';
|
||||||
|
|
||||||
|
type UseDecryptProps = {
|
||||||
|
encrypted: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const [memo, setMemo] = createRoot(() => createSignal<Record<string, string>>({}));
|
||||||
|
const [decrypting, setDecrypting] = createRoot(() => createSignal<Record<string, boolean>>({}));
|
||||||
|
|
||||||
|
const useDecrypt = (propsProvider: () => UseDecryptProps | null) => {
|
||||||
|
const pubkey = usePubkey();
|
||||||
|
const [decrypted, setDecrypted] = createSignal<string | null>(null);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const props = propsProvider();
|
||||||
|
if (props == null) return;
|
||||||
|
|
||||||
|
const { encrypted } = props;
|
||||||
|
if (encrypted in memo()) {
|
||||||
|
setDecrypted(memo()[encrypted]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = pubkey();
|
||||||
|
if (p == null) return;
|
||||||
|
|
||||||
|
if (decrypting()[encrypted]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDecrypting((current) => ({ ...current, [encrypted]: true }));
|
||||||
|
|
||||||
|
window.nostr?.nip04
|
||||||
|
?.decrypt?.(p, encrypted)
|
||||||
|
?.then((result) => {
|
||||||
|
setMemo((current) => ({ ...current, [encrypted]: result }));
|
||||||
|
setDecrypted(result);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(`failed to decrypt "${encrypted}"`, err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return decrypted;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDecrypt;
|
||||||
@@ -17,7 +17,7 @@ export default function useFollowers(propsProvider: () => UseFollowersProps) {
|
|||||||
const { events } = useSubscription(() => ({
|
const { events } = useSubscription(() => ({
|
||||||
relayUrls: config().relayUrls,
|
relayUrls: config().relayUrls,
|
||||||
filters: [{ kinds: [Kind.Contacts], '#p': [props().pubkey] }],
|
filters: [{ kinds: [Kind.Contacts], '#p': [props().pubkey] }],
|
||||||
limit: Infinity,
|
limit: Number.MAX_SAFE_INTEGER,
|
||||||
continuous: true,
|
continuous: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export type UseParameterizedReplaceableEvent = {
|
|||||||
query: CreateQueryResult<NostrEvent | null>;
|
query: CreateQueryResult<NostrEvent | null>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useParameterizedReplaceableEvent = (
|
const useParameterizedReplaceableEvent = (
|
||||||
propsProvider: () => UseParameterizedReplaceableEventProps | null,
|
propsProvider: () => UseParameterizedReplaceableEventProps | null,
|
||||||
): UseParameterizedReplaceableEvent => {
|
): UseParameterizedReplaceableEvent => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createMemo, observable } from 'solid-js';
|
|||||||
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
||||||
import { Event as NostrEvent } from 'nostr-tools';
|
import { Event as NostrEvent } from 'nostr-tools';
|
||||||
|
|
||||||
|
import useConfig from '@/core/useConfig';
|
||||||
import { exec } from '@/nostr/useBatchedEvents';
|
import { exec } from '@/nostr/useBatchedEvents';
|
||||||
import timeout from '@/utils/timeout';
|
import timeout from '@/utils/timeout';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export type UseReactions = {
|
|||||||
const EmojiRegex = /\p{Emoji_Presentation}/u;
|
const EmojiRegex = /\p{Emoji_Presentation}/u;
|
||||||
|
|
||||||
const useReactions = (propsProvider: () => UseReactionsProps | null): UseReactions => {
|
const useReactions = (propsProvider: () => UseReactionsProps | null): UseReactions => {
|
||||||
|
const { shouldMuteEvent } = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const props = createMemo(propsProvider);
|
const props = createMemo(propsProvider);
|
||||||
const genQueryKey = createMemo(() => ['useReactions', props()] as const);
|
const genQueryKey = createMemo(() => ['useReactions', props()] as const);
|
||||||
@@ -51,7 +53,10 @@ const useReactions = (propsProvider: () => UseReactionsProps | null): UseReactio
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const reactions = () => query.data ?? [];
|
const reactions = () => {
|
||||||
|
const data = query.data ?? [];
|
||||||
|
return data.filter((ev) => !shouldMuteEvent(ev));
|
||||||
|
};
|
||||||
|
|
||||||
const reactionsGroupedByContent = () => {
|
const reactionsGroupedByContent = () => {
|
||||||
const result = new Map<string, NostrEvent[]>();
|
const result = new Map<string, NostrEvent[]>();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { createMemo, observable } from 'solid-js';
|
|||||||
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query';
|
||||||
import { Event as NostrEvent } from 'nostr-tools';
|
import { Event as NostrEvent } from 'nostr-tools';
|
||||||
|
|
||||||
|
import useConfig from '@/core/useConfig';
|
||||||
import { exec } from '@/nostr/useBatchedEvents';
|
import { exec } from '@/nostr/useBatchedEvents';
|
||||||
import timeout from '@/utils/timeout';
|
import timeout from '@/utils/timeout';
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ export type UseReposts = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useReposts = (propsProvider: () => UseRepostsProps): UseReposts => {
|
const useReposts = (propsProvider: () => UseRepostsProps): UseReposts => {
|
||||||
|
const { shouldMuteEvent } = useConfig();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const props = createMemo(propsProvider);
|
const props = createMemo(propsProvider);
|
||||||
const genQueryKey = createMemo(() => ['useReposts', props()] as const);
|
const genQueryKey = createMemo(() => ['useReposts', props()] as const);
|
||||||
@@ -44,7 +46,10 @@ const useReposts = (propsProvider: () => UseRepostsProps): UseReposts => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const reposts = () => query.data ?? [];
|
const reposts = () => {
|
||||||
|
const data = query.data ?? [];
|
||||||
|
return data.filter((ev) => !shouldMuteEvent(ev));
|
||||||
|
};
|
||||||
|
|
||||||
const isRepostedBy = (pubkey: string): boolean =>
|
const isRepostedBy = (pubkey: string): boolean =>
|
||||||
reposts().findIndex((event) => event.pubkey === pubkey) !== -1;
|
reposts().findIndex((event) => event.pubkey === pubkey) !== -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user