diff --git a/.eslintrc.js b/.eslintrc.js index 08dd486..38699fe 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,6 +34,21 @@ module.exports = { }, ], 'prettier/prettier': 'error', + 'no-console': ['off'], + 'no-alert': ['off'], + 'import/order': [ + 'warn', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + pathGroupsExcludedImportTypes: ['builtin'], + alphabetize: { order: 'asc', caseInsensitive: true }, + pathGroups: [ + { pattern: 'solid-js*', group: 'external', position: 'before' }, + { pattern: '@/', group: 'internal', position: 'before' }, + ], + }, + ], }, settings: { linkComponents: ['Link'], @@ -45,23 +60,6 @@ module.exports = { extensions: ['.ts', '.tsx'], }, }, - 'import/order': [ - 'warn', - { - groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], - 'newlines-between': 'always', - pathGroupsExcludedImportTypes: ['builtin'], - alphabetize: { order: 'asc', caseInsensitive: true }, - pathGroups: [ - { pattern: 'src/types/**', group: 'internal', position: 'before' }, - { - pattern: 'src/repositories/**', - group: 'internal', - position: 'before', - }, - ], - }, - ], tailwindcss: { whitelist: [ 'h-fill-available', diff --git a/package.json b/package.json index 9170c87..80020bd 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,12 @@ "dev": "npm run generatePackageInfo && vite", "build": "npm run generatePackageInfo && vite build", "serve": "npm run generatePackageInfo && vite preview", - "lint": "eslint .", + "lint": "eslint --cache .", + "fix": "eslint --cache --fix .", "tsc": "tsc --noEmit --skipLibCheck", "test": "vitest run --no-watch", "watch-test": "vitest --watch", "cover": "vitest run --coverage", - "fix": "eslint --fix .", "prepare": "husky install", "generatePackageInfo": "node -e 'import(\"./scripts/generatePackageInfo.mjs\").then((m) => m.default())'", "checkLicense": "node -e 'import(\"./scripts/checkLicense.mjs\").then((m) => m.default())'" diff --git a/src/App.tsx b/src/App.tsx index 8387515..ee33745 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,9 @@ import { createEffect, onCleanup, lazy, type Component } from 'solid-js'; + import { Routes, Route } from '@solidjs/router'; -import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'; -import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'; import { persistQueryClient } from '@tanstack/query-persist-client-core'; +import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'; +import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'; const Home = lazy(() => import('@/pages/Home')); const Hello = lazy(() => import('@/pages/Hello')); diff --git a/src/components/Column.tsx b/src/components/Column.tsx index e84074e..df0b5c7 100644 --- a/src/components/Column.tsx +++ b/src/components/Column.tsx @@ -1,9 +1,10 @@ import { Show, type JSX, type Component } from 'solid-js'; + import ArrowLeft from 'heroicons/24/outline/arrow-left.svg'; -import { useHandleCommand } from '@/hooks/useCommandBus'; -import { TimelineContext, useTimelineState } from '@/components/TimelineContext'; import TimelineContentDisplay from '@/components/TimelineContentDisplay'; +import { TimelineContext, useTimelineState } from '@/components/TimelineContext'; +import { useHandleCommand } from '@/hooks/useCommandBus'; export type ColumnProps = { name: string; diff --git a/src/components/Config.tsx b/src/components/Config.tsx index 63653ff..1de4434 100644 --- a/src/components/Config.tsx +++ b/src/components/Config.tsx @@ -1,8 +1,10 @@ -import useConfig, { type Config } from '@/nostr/useConfig'; import { createSignal, For, type JSX } from 'solid-js'; + import XMark from 'heroicons/24/outline/x-mark.svg'; import Modal from '@/components/Modal'; +import useConfig, { type Config } from '@/nostr/useConfig'; + import UserNameDisplay from './UserDisplayName'; type ConfigProps = { diff --git a/src/components/DeprecatedRepost.tsx b/src/components/DeprecatedRepost.tsx index 2a44ec6..db298a1 100644 --- a/src/components/DeprecatedRepost.tsx +++ b/src/components/DeprecatedRepost.tsx @@ -1,13 +1,15 @@ // NIP-18 (DEPRECATED) import { type Component, createMemo } from 'solid-js'; -import { Event as NostrEvent } from 'nostr-tools'; + import ArrowPathRoundedSquare from 'heroicons/24/outline/arrow-path-rounded-square.svg'; +import { Event as NostrEvent } from 'nostr-tools'; import ColumnItem from '@/components/ColumnItem'; import UserDisplayName from '@/components/UserDisplayName'; import eventWrapper from '@/core/event'; import useFormatDate from '@/hooks/useFormatDate'; import useModalState from '@/hooks/useModalState'; + import TextNoteDisplayById from './textNote/TextNoteDisplayById'; export type DeprecatedRepostProps = { diff --git a/src/components/EventLink.tsx b/src/components/EventLink.tsx index 672e7a7..a43c102 100644 --- a/src/components/EventLink.tsx +++ b/src/components/EventLink.tsx @@ -1,4 +1,5 @@ import { Component } from 'solid-js'; + import { nip19 } from 'nostr-tools'; const { noteEncode } = nip19; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 7176d43..8650a63 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -15,6 +15,7 @@ const Modal: Component = (props) => { }; return ( + /* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */
{ const event = () => eventWrapper(rawEvent); diff --git a/src/components/TimelineContext.tsx b/src/components/TimelineContext.tsx index 874d036..62d1a3d 100644 --- a/src/components/TimelineContext.tsx +++ b/src/components/TimelineContext.tsx @@ -1,6 +1,7 @@ import { createContext, useContext } from 'solid-js'; -import { createStore } from 'solid-js/store'; + import { Event as NostrEvent } from 'nostr-tools'; +import { createStore } from 'solid-js/store'; export type TimelineContent = { type: 'Replies'; diff --git a/src/components/notification/Reaction.tsx b/src/components/notification/Reaction.tsx index b71147c..5828ce1 100644 --- a/src/components/notification/Reaction.tsx +++ b/src/components/notification/Reaction.tsx @@ -1,15 +1,15 @@ import { Switch, Match, type Component, Show } from 'solid-js'; -import { type Event as NostrEvent } from 'nostr-tools'; + import HeartSolid from 'heroicons/24/solid/heart.svg'; +import { type Event as NostrEvent } from 'nostr-tools'; import ColumnItem from '@/components/ColumnItem'; import TextNoteDisplay from '@/components/textNote/TextNoteDisplay'; import UserDisplayName from '@/components/UserDisplayName'; - -import useProfile from '@/nostr/useProfile'; -import useEvent from '@/nostr/useEvent'; import eventWrapper from '@/core/event'; import useModalState from '@/hooks/useModalState'; +import useEvent from '@/nostr/useEvent'; +import useProfile from '@/nostr/useProfile'; type ReactionProps = { event: NostrEvent; diff --git a/src/components/textNote/ContentWarningDisplay.tsx b/src/components/textNote/ContentWarningDisplay.tsx index 53250c1..df184f3 100644 --- a/src/components/textNote/ContentWarningDisplay.tsx +++ b/src/components/textNote/ContentWarningDisplay.tsx @@ -1,4 +1,5 @@ import { createSignal, type Component, type JSX, Show } from 'solid-js'; + import { ContentWarning } from '@/core/event'; export type ContentWarningDisplayProps = { diff --git a/src/components/textNote/ImageDisplay.tsx b/src/components/textNote/ImageDisplay.tsx index e0e27f8..30d188d 100644 --- a/src/components/textNote/ImageDisplay.tsx +++ b/src/components/textNote/ImageDisplay.tsx @@ -1,5 +1,7 @@ -import { Component, createEffect, createSignal, onMount, Show, JSX } from 'solid-js'; +import { Component, createSignal, Show } from 'solid-js'; + import { fixUrl } from '@/utils/imageUrl'; + import SafeLink from '../utils/SafeLink'; type ImageDisplayProps = { @@ -14,12 +16,13 @@ const ImageDisplay: Component = (props) => { const [hidden, setHidden] = createSignal(props.initialHidden); const [playing, setPlaying] = createSignal(true); - const isGIF = () => props.url.match(/\.gif/i); + // const isGIF = () => props.url.match(/\.gif/i); const play = () => { setPlaying(true); }; + /* const stop = () => { if (canvasRef == null || imageRef == null) return; canvasRef.width = imageRef.width; @@ -39,6 +42,7 @@ const ImageDisplay: Component = (props) => { ); setPlaying(false); }; + */ return ( = (props) => { eventId: props.event.id, })); - const { reposts, isRepostedBy, invalidateDeprecatedReposts } = useDeprecatedReposts(() => ({ + const { reposts, isRepostedBy, invalidateReposts } = useReposts(() => ({ eventId: props.event.id, })); @@ -105,9 +102,9 @@ const TextNoteDisplay: Component = (props) => { }, }); - const publishDeprecatedRepostMutation = createMutation({ - mutationKey: ['publishDeprecatedRepost', event().id], - mutationFn: commands.publishDeprecatedRepost.bind(commands), + const publishRepostMutation = createMutation({ + mutationKey: ['publishRepost', event().id], + mutationFn: commands.publishRepost.bind(commands), onSuccess: () => { console.log('succeeded to publish reposts'); }, @@ -115,7 +112,7 @@ const TextNoteDisplay: Component = (props) => { console.error('failed to publish repost: ', err); }, onSettled: () => { - invalidateDeprecatedReposts().catch((err) => console.error('failed to refetch reposts', err)); + invalidateReposts().catch((err) => console.error('failed to refetch reposts', err)); }, }); @@ -151,7 +148,7 @@ const TextNoteDisplay: Component = (props) => { } ensureNonNull([pubkey(), props.event.id] as const)(([pubkeyNonNull, eventIdNonNull]) => { - publishDeprecatedRepostMutation.mutate({ + publishRepostMutation.mutate({ relayUrls: config().relayUrls, pubkey: pubkeyNonNull, eventId: eventIdNonNull, @@ -306,13 +303,13 @@ const TextNoteDisplay: Component = (props) => { class="flex shrink-0 items-center gap-1" classList={{ 'text-zinc-400': !isRepostedByMe(), - 'text-green-400': isRepostedByMe() || publishDeprecatedRepostMutation.isLoading, + 'text-green-400': isRepostedByMe() || publishRepostMutation.isLoading, }} > diff --git a/src/components/textNote/TextNoteDisplayById.tsx b/src/components/textNote/TextNoteDisplayById.tsx index 570f640..bf6914f 100644 --- a/src/components/textNote/TextNoteDisplayById.tsx +++ b/src/components/textNote/TextNoteDisplayById.tsx @@ -2,11 +2,10 @@ import { Switch, Match, type Component } from 'solid-js'; // eslint-disable-next-line import/no-cycle import TextNoteDisplay, { type TextNoteDisplayProps } from '@/components/textNote/TextNoteDisplay'; - import useConfig from '@/nostr/useConfig'; import useEvent from '@/nostr/useEvent'; - import ensureNonNull from '@/utils/ensureNonNull'; + import EventLink from '../EventLink'; type TextNoteDisplayByIdProps = Omit & { diff --git a/src/core/column.ts b/src/core/column.ts index d02b6e3..e166ff2 100644 --- a/src/core/column.ts +++ b/src/core/column.ts @@ -1,5 +1,6 @@ // import { z } from 'zod'; import { type Filter } from 'nostr-tools'; + import { type ColumnProps } from '@/components/Column'; export type NotificationType = diff --git a/src/core/event.ts b/src/core/event.ts index 6db662c..70d82f9 100644 --- a/src/core/event.ts +++ b/src/core/event.ts @@ -1,6 +1,7 @@ -import type { Event as NostrEvent } from 'nostr-tools'; import uniq from 'lodash/uniq'; +import type { Event as NostrEvent } from 'nostr-tools'; + export type EventMarker = 'reply' | 'root' | 'mention'; export type TaggedEvent = { diff --git a/src/core/parseTextNote.test.ts b/src/core/parseTextNote.test.ts index 20ffdba..bcae40d 100644 --- a/src/core/parseTextNote.test.ts +++ b/src/core/parseTextNote.test.ts @@ -1,6 +1,7 @@ import assert from 'assert'; -import { describe, it } from 'vitest'; + import { type Event as NostrEvent } from 'nostr-tools'; +import { describe, it } from 'vitest'; import parseTextNote, { resolveTagReference, diff --git a/src/core/parseTextNote.ts b/src/core/parseTextNote.ts index ab85884..c520638 100644 --- a/src/core/parseTextNote.ts +++ b/src/core/parseTextNote.ts @@ -1,4 +1,5 @@ import { nip19, type Event as NostrEvent } from 'nostr-tools'; + import eventWrapper from './event'; type ProfilePointer = nip19.ProfilePointer; diff --git a/src/hooks/createSignalWithStorage.ts b/src/hooks/createSignalWithStorage.ts index 8cfcc5b..aaa122f 100644 --- a/src/hooks/createSignalWithStorage.ts +++ b/src/hooks/createSignalWithStorage.ts @@ -1,4 +1,5 @@ import { createSignal, createEffect, onMount, type Signal } from 'solid-js'; + import { createStore, SetStoreFunction, type Store, type StoreNode } from 'solid-js/store'; type GenericStorage = { diff --git a/src/hooks/useFormatDate.ts b/src/hooks/useFormatDate.ts index d7f938d..150df75 100644 --- a/src/hooks/useFormatDate.ts +++ b/src/hooks/useFormatDate.ts @@ -1,7 +1,5 @@ -import useConfig from '@/nostr/useConfig'; - import useDatePulser from '@/hooks/useDatePulser'; - +import useConfig from '@/nostr/useConfig'; import { formatRelative, formatAbsoluteLong, formatAbsoluteShort } from '@/utils/formatDate'; // 7 seconds is used here so that the last digit of relative time is changed. diff --git a/src/hooks/usePersistStatus.ts b/src/hooks/usePersistStatus.ts index ec34343..ece7413 100644 --- a/src/hooks/usePersistStatus.ts +++ b/src/hooks/usePersistStatus.ts @@ -1,4 +1,5 @@ import { Accessor } from 'solid-js'; + import { createSignalWithStorage, createStorageWithSerializer, diff --git a/src/hooks/useResizedImage.ts b/src/hooks/useResizedImage.ts index 5caffc7..dc32cf5 100644 --- a/src/hooks/useResizedImage.ts +++ b/src/hooks/useResizedImage.ts @@ -1,5 +1,5 @@ -import type { Accessor } from 'solid-js'; import { createSignal, createEffect } from 'solid-js'; +import type { Accessor } from 'solid-js'; export type UseResizedImageProps = { imageUrl: Accessor; diff --git a/src/hooks/useShortcutKeys.ts b/src/hooks/useShortcutKeys.ts index 41b4256..a78645f 100644 --- a/src/hooks/useShortcutKeys.ts +++ b/src/hooks/useShortcutKeys.ts @@ -2,6 +2,7 @@ // type Commands = (typeof commands)[number]; import { onMount, onCleanup, type JSX } from 'solid-js'; + import throttle from 'lodash/throttle'; import { useRequestCommand, type Command } from '@/hooks/useCommandBus'; diff --git a/src/index.tsx b/src/index.tsx index 6a3ee63..bfc9c6b 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,6 @@ /* @refresh reload */ -import { render } from 'solid-js/web'; import { Router } from '@solidjs/router'; +import { render } from 'solid-js/web'; import './index.css'; import App from './App'; diff --git a/src/nostr/useBatch.ts b/src/nostr/useBatch.ts index adf4811..a6b4dc5 100644 --- a/src/nostr/useBatch.ts +++ b/src/nostr/useBatch.ts @@ -4,6 +4,7 @@ export type Task = { id: number; args: TaskArgs; resolve: (result: TaskResult) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any reject: (error: any) => void; }; @@ -16,11 +17,13 @@ export type UseBatchProps = { export type PromiseWithCallbacks = { promise: Promise; resolve: (e: T) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any reject: (e: any) => void; }; const promiseWithCallbacks = (): PromiseWithCallbacks => { let resolve: ((e: T) => void) | undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let reject: ((e: any) => void) | undefined; const promise = new Promise((resolveFn, rejectFn) => { diff --git a/src/nostr/useBatchedEvents.ts b/src/nostr/useBatchedEvents.ts index a1fd62d..6a7e435 100644 --- a/src/nostr/useBatchedEvents.ts +++ b/src/nostr/useBatchedEvents.ts @@ -6,23 +6,23 @@ import { type Accessor, type Signal, } from 'solid-js'; -import { type Event as NostrEvent, type Filter, Kind } from 'nostr-tools'; + import { createQuery, useQueryClient, type CreateQueryResult } from '@tanstack/solid-query'; +import { type Event as NostrEvent, type Filter, Kind } from 'nostr-tools'; import eventWrapper from '@/core/event'; - import useBatch, { type Task } from '@/nostr/useBatch'; -import useStats from '@/nostr/useStats'; import useConfig from '@/nostr/useConfig'; import usePool from '@/nostr/usePool'; - +import useStats from '@/nostr/useStats'; import timeout from '@/utils/timeout'; type TaskArg = | { type: 'Profile'; pubkey: string } | { type: 'TextNote'; eventId: string } | { type: 'Reactions'; mentionedEventId: string } - | { type: 'DeprecatedReposts'; mentionedEventId: string } + | { type: 'ZapReceipts'; mentionedEventId: string } + | { type: 'Reposts'; mentionedEventId: string } | { type: 'Followings'; pubkey: string }; type BatchedEvents = { completed: boolean; events: NostrEvent[] }; @@ -83,15 +83,15 @@ export type UseReactions = { query: CreateQueryResult; }; -// DeprecatedReposts -export type UseDeprecatedRepostsProps = { +// Reposts +export type UseRepostsProps = { eventId: string; }; -export type UseDeprecatedReposts = { +export type UseReposts = { reposts: () => NostrEvent[]; isRepostedBy: (pubkey: string) => boolean; - invalidateDeprecatedReposts: () => Promise; + invalidateReposts: () => Promise; query: CreateQueryResult; }; @@ -121,7 +121,7 @@ setInterval(() => { setActiveBatchSubscriptions(count); }, 1000); -const EmptyBatchedEvents = Object.freeze({ events: Object.freeze([]), completed: true }); +const EmptyBatchedEvents = { events: [], completed: true }; const emptyBatchedEvents = () => EmptyBatchedEvents; const { exec } = useBatch(() => ({ @@ -132,6 +132,7 @@ const { exec } = useBatch(() => ({ const textNoteTasks = new Map[]>(); const reactionsTasks = new Map[]>(); const repostsTasks = new Map[]>(); + const zapReceiptsTasks = new Map[]>(); const followingsTasks = new Map[]>(); tasks.forEach((task) => { @@ -144,9 +145,12 @@ const { exec } = useBatch(() => ({ } else if (task.args.type === 'Reactions') { const current = reactionsTasks.get(task.args.mentionedEventId) ?? []; reactionsTasks.set(task.args.mentionedEventId, [...current, task]); - } else if (task.args.type === 'DeprecatedReposts') { + } else if (task.args.type === 'Reposts') { const current = repostsTasks.get(task.args.mentionedEventId) ?? []; repostsTasks.set(task.args.mentionedEventId, [...current, task]); + } else if (task.args.type === 'ZapReceipts') { + const current = zapReceiptsTasks.get(task.args.mentionedEventId) ?? []; + repostsTasks.set(task.args.mentionedEventId, [...current, task]); } else if (task.args.type === 'Followings') { const current = followingsTasks.get(task.args.pubkey) ?? []; followingsTasks.set(task.args.pubkey, [...current, task]); @@ -157,6 +161,7 @@ const { exec } = useBatch(() => ({ const textNoteIds = [...textNoteTasks.keys()]; const reactionsIds = [...reactionsTasks.keys()]; const repostsIds = [...repostsTasks.keys()]; + const zapReceiptsIds = [...zapReceiptsTasks.keys()]; const followingsIds = [...followingsTasks.keys()]; const filters: Filter[] = []; @@ -173,6 +178,9 @@ const { exec } = useBatch(() => ({ if (repostsIds.length > 0) { filters.push({ kinds: [6], '#e': repostsIds }); } + if (zapReceiptsIds.length > 0) { + filters.push({ kinds: [9735], '#e': zapReceiptsIds }); + } if (followingsIds.length > 0) { filters.push({ kinds: [Kind.Contacts], authors: followingsIds }); } @@ -239,6 +247,13 @@ const { exec } = useBatch(() => ({ const registeredTasks = repostsTasks.get(taggedEventId) ?? []; resolveTasks(registeredTasks, event); }); + } else if (event.kind === Kind.Zap) { + const eventTags = eventWrapper(event).taggedEvents(); + eventTags.forEach((eventTag) => { + const taggedEventId = eventTag.id; + const registeredTasks = repostsTasks.get(taggedEventId) ?? []; + resolveTasks(registeredTasks, event); + }); } else if (event.kind === Kind.Contacts) { const registeredTasks = followingsTasks.get(event.pubkey) ?? []; resolveTasks(registeredTasks, event); @@ -393,12 +408,10 @@ export const useReactions = (propsProvider: () => UseReactionsProps | null): Use return { reactions, reactionsGroupedByContent, isReactedBy, invalidateReactions, query }; }; -export const useDeprecatedReposts = ( - propsProvider: () => UseDeprecatedRepostsProps, -): UseDeprecatedReposts => { +export const useReposts = (propsProvider: () => UseRepostsProps): UseReposts => { const queryClient = useQueryClient(); const props = createMemo(propsProvider); - const genQueryKey = createMemo(() => ['useDeprecatedReposts', props()] as const); + const genQueryKey = createMemo(() => ['useReposts', props()] as const); const query = createQuery( genQueryKey, @@ -406,16 +419,14 @@ export const useDeprecatedReposts = ( const [, currentProps] = queryKey; if (currentProps == null) return []; const { eventId: mentionedEventId } = currentProps; - const promise = exec({ type: 'DeprecatedReposts', mentionedEventId }, signal).then( - (batchedEvents) => { - const events = () => batchedEvents().events; - observable(batchedEvents).subscribe(() => { - queryClient.setQueryData(queryKey, events()); - }); - return events(); - }, - ); - return timeout(15000, `useDeprecatedReposts: ${mentionedEventId}`)(promise); + const promise = exec({ type: 'Reposts', mentionedEventId }, signal).then((batchedEvents) => { + const events = () => batchedEvents().events; + observable(batchedEvents).subscribe(() => { + queryClient.setQueryData(queryKey, events()); + }); + return events(); + }); + return timeout(15000, `useReposts: ${mentionedEventId}`)(promise); }, { staleTime: 1 * 60 * 1000, // 1 min @@ -429,10 +440,9 @@ export const useDeprecatedReposts = ( const isRepostedBy = (pubkey: string): boolean => reposts().findIndex((event) => event.pubkey === pubkey) !== -1; - const invalidateDeprecatedReposts = (): Promise => - queryClient.invalidateQueries(genQueryKey()); + const invalidateReposts = (): Promise => queryClient.invalidateQueries(genQueryKey()); - return { reposts, isRepostedBy, invalidateDeprecatedReposts, query }; + return { reposts, isRepostedBy, invalidateReposts, query }; }; export const useFollowings = (propsProvider: () => UseFollowingsProps | null): UseFollowings => { diff --git a/src/nostr/useCommands.test.ts b/src/nostr/useCommands.test.ts index 25eb1b3..d8333fd 100644 --- a/src/nostr/useCommands.test.ts +++ b/src/nostr/useCommands.test.ts @@ -1,5 +1,7 @@ import assert from 'assert'; + import { describe, it } from 'vitest'; + import { buildTags } from './useCommands'; describe('buildTags', () => { diff --git a/src/nostr/useCommands.ts b/src/nostr/useCommands.ts index 7deb553..f0b70bf 100644 --- a/src/nostr/useCommands.ts +++ b/src/nostr/useCommands.ts @@ -2,7 +2,6 @@ import { getEventHash, Kind, type UnsignedEvent, type Pub } from 'nostr-tools'; // import '@/types/nostr.d'; import usePool from '@/nostr/usePool'; - import epoch from '@/utils/epoch'; export type TagParams = { @@ -148,7 +147,7 @@ const useCommands = () => { return publishEvent(relayUrls, preSignedEvent); }, // NIP-18 - async publishDeprecatedRepost({ + async publishRepost({ relayUrls, pubkey, eventId, diff --git a/src/nostr/useConfig.ts b/src/nostr/useConfig.ts index 429cfdb..db65261 100644 --- a/src/nostr/useConfig.ts +++ b/src/nostr/useConfig.ts @@ -1,10 +1,12 @@ import { type Accessor, type Setter } from 'solid-js'; + import { Kind, type Event as NostrEvent } from 'nostr-tools'; + +import { ColumnConfig } from '@/core/column'; import { createStorageWithSerializer, createStoreWithStorage, } from '@/hooks/createSignalWithStorage'; -import { ColumnConfig } from '@/core/column'; export type Config = { relayUrls: string[]; @@ -111,7 +113,8 @@ const useConfig = (): UseConfig => { return false; }; - const shouldMuteEvent = (event: NostrEvent) => isPubkeyMuted(event.pubkey) || hasMutedKeyword(event); + const shouldMuteEvent = (event: NostrEvent) => + isPubkeyMuted(event.pubkey) || hasMutedKeyword(event); const initializeColumns = ({ pubkey }: { pubkey: string }) => { // すでに設定されている場合は終了 diff --git a/src/nostr/useDeprecatedReposts.ts b/src/nostr/useDeprecatedReposts.ts deleted file mode 100644 index 4548c54..0000000 --- a/src/nostr/useDeprecatedReposts.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { useDeprecatedReposts } from '@/nostr/useBatchedEvents'; - -export default useDeprecatedReposts; diff --git a/src/nostr/useFollowers.ts b/src/nostr/useFollowers.ts index eea3191..39fb119 100644 --- a/src/nostr/useFollowers.ts +++ b/src/nostr/useFollowers.ts @@ -1,6 +1,7 @@ import { createMemo, createSignal } from 'solid-js'; -import { Kind } from 'nostr-tools'; + import uniq from 'lodash/uniq'; +import { Kind } from 'nostr-tools'; import useConfig from '@/nostr/useConfig'; import useSubscription from '@/nostr/useSubscription'; diff --git a/src/nostr/usePool.ts b/src/nostr/usePool.ts index b937daf..069f9b1 100644 --- a/src/nostr/usePool.ts +++ b/src/nostr/usePool.ts @@ -1,4 +1,5 @@ import { createSignal } from 'solid-js'; + import { SimplePool } from 'nostr-tools'; const [pool] = createSignal(new SimplePool()); diff --git a/src/nostr/useReposts.ts b/src/nostr/useReposts.ts new file mode 100644 index 0000000..56985fd --- /dev/null +++ b/src/nostr/useReposts.ts @@ -0,0 +1,3 @@ +import { useReposts } from '@/nostr/useBatchedEvents'; + +export default useReposts; diff --git a/src/nostr/useSubscription.ts b/src/nostr/useSubscription.ts index fa03443..d835ea6 100644 --- a/src/nostr/useSubscription.ts +++ b/src/nostr/useSubscription.ts @@ -1,9 +1,13 @@ import { createSignal, createEffect, onCleanup, on } from 'solid-js'; -import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools'; + import uniqBy from 'lodash/uniqBy'; + import usePool from '@/nostr/usePool'; -import useStats from './useStats'; + import useConfig from './useConfig'; +import useStats from './useStats'; + +import type { Event as NostrEvent, Filter, SubscriptionOptions } from 'nostr-tools'; export type UseSubscriptionProps = { relayUrls: string[]; diff --git a/src/nostr/useVerification.ts b/src/nostr/useVerification.ts index 7e22ee7..cdeb9c6 100644 --- a/src/nostr/useVerification.ts +++ b/src/nostr/useVerification.ts @@ -1,4 +1,5 @@ import { createMemo, type Accessor } from 'solid-js'; + import { createQuery, type CreateQueryResult } from '@tanstack/solid-query'; import { nip05, nip19 } from 'nostr-tools'; diff --git a/src/pages/Hello.tsx b/src/pages/Hello.tsx index 0e05e99..15a2472 100644 --- a/src/pages/Hello.tsx +++ b/src/pages/Hello.tsx @@ -1,5 +1,7 @@ import { createSignal, onMount, Switch, Match, type Component } from 'solid-js'; + import { useNavigate } from '@solidjs/router'; + import usePersistStatus from '@/hooks/usePersistStatus'; type SignerStatus = 'checking' | 'available' | 'unavailable'; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index b4c821a..fc352b2 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -8,26 +8,24 @@ import { Match, type Component, } from 'solid-js'; + import { useNavigate } from '@solidjs/router'; import { createVirtualizer } from '@tanstack/solid-virtual'; import uniq from 'lodash/uniq'; import Column from '@/components/Column'; -import SideBar from '@/components/SideBar'; -import Timeline from '@/components/Timeline'; import Notification from '@/components/Notification'; import ProfileDisplay from '@/components/ProfileDisplay'; - -import usePool from '@/nostr/usePool'; -import useConfig from '@/nostr/useConfig'; -import useSubscription from '@/nostr/useSubscription'; -import useFollowings from '@/nostr/useFollowings'; -import usePubkey from '@/nostr/usePubkey'; - -import { useMountShortcutKeys } from '@/hooks/useShortcutKeys'; -import usePersistStatus from '@/hooks/usePersistStatus'; +import SideBar from '@/components/SideBar'; +import Timeline from '@/components/Timeline'; import useModalState from '@/hooks/useModalState'; - +import usePersistStatus from '@/hooks/usePersistStatus'; +import { useMountShortcutKeys } from '@/hooks/useShortcutKeys'; +import useConfig from '@/nostr/useConfig'; +import useFollowings from '@/nostr/useFollowings'; +import usePool from '@/nostr/usePool'; +import usePubkey from '@/nostr/usePubkey'; +import useSubscription from '@/nostr/useSubscription'; import ensureNonNull from '@/utils/ensureNonNull'; import epoch from '@/utils/epoch'; diff --git a/src/utils/ensureNonNull.ts b/src/utils/ensureNonNull.ts index 7efc9a6..460afe0 100644 --- a/src/utils/ensureNonNull.ts +++ b/src/utils/ensureNonNull.ts @@ -1,14 +1,18 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type TupleNonNull = { [P in keyof T]: NonNullable; }; const ensureNonNull = - (tuple: T) => - (f: (tupleNonNull: TupleNonNull) => R): R | null => { - if (tuple.some((e) => e == null)) { - return null; - } - return f(tuple as TupleNonNull); - }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + + + (tuple: T) => + (f: (tupleNonNull: TupleNonNull) => R): R | null => { + if (tuple.some((e) => e == null)) { + return null; + } + return f(tuple as TupleNonNull); + }; export default ensureNonNull; diff --git a/src/utils/imageUrl.test.ts b/src/utils/imageUrl.test.ts index 3c2fa50..753e330 100644 --- a/src/utils/imageUrl.test.ts +++ b/src/utils/imageUrl.test.ts @@ -1,5 +1,7 @@ import assert from 'assert'; + import { describe, it } from 'vitest'; + import { fixUrl } from './imageUrl'; describe('fixUrl', () => { diff --git a/tailwind.config.js b/tailwind.config.js index 2c4625a..7e45e53 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,4 +1,4 @@ -/* eslint global-require: "off" */ +/* eslint global-require: "off", @typescript-eslint/no-var-requires: "off" */ const colors = require('tailwindcss/colors'); module.exports = { diff --git a/vitest.config.ts b/vitest.config.ts index 22cbb6a..1dcfece 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,4 +1,5 @@ import path from 'path'; + // eslint-disable-next-line import/no-extraneous-dependencies import { defineConfig } from 'vitest/config';