From 18bec8a28b9b119bf65335ef7b4eb94e14a31cb2 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 10 Apr 2026 21:14:39 +0200 Subject: [PATCH] reduce repo size some more --- knip.json | 7 + package.json | 1 + .../FavoriteRelaysSetting/provider.tsx | 2 +- .../KeyboardShortcutsHelp/index.tsx | 2 - .../LatestFromFollowsSection/index.tsx | 2 +- .../Note/MarkdownArticle/preprocessMarkup.ts | 28 -- .../Mention/NeventNaddrPickerDialog.tsx | 2 +- .../PostTextarea/Mention/suggestion.ts | 2 - .../ScheduleInPersonMeetingSingleDialog.tsx | 383 ------------------ .../ScheduleVideoCallSingleDialog.tsx | 294 -------------- .../ScheduleVideoCallDialog/index.tsx | 2 - 11 files changed, 11 insertions(+), 714 deletions(-) create mode 100644 knip.json delete mode 100644 src/components/ScheduleVideoCallDialog/ScheduleInPersonMeetingSingleDialog.tsx delete mode 100644 src/components/ScheduleVideoCallDialog/ScheduleVideoCallSingleDialog.tsx diff --git a/knip.json b/knip.json new file mode 100644 index 00000000..0c43e3c6 --- /dev/null +++ b/knip.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://unpkg.com/knip@5/schema.json", + "entry": ["electron/preload.cjs!", "nip66-cron/index.mjs!"], + "ignore": ["src/global-polyfill-types.d.ts", "src/types/**/*.d.ts"], + "ignoreBinaries": ["tsx", "electron", "electron-builder"], + "ignoreDependencies": ["ws"] +} diff --git a/package.json b/package.json index 957b16e1..8d70fa0c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "dev:refresh": "rm -rf node_modules/.vite && vite --host", "build": "tsc -b && vite build", "lint": "eslint .", + "knip": "npx --yes knip@5", "format": "prettier --write .", "preview": "vite preview", "test": "vitest", diff --git a/src/components/FavoriteRelaysSetting/provider.tsx b/src/components/FavoriteRelaysSetting/provider.tsx index bd22de96..fc79e44c 100644 --- a/src/components/FavoriteRelaysSetting/provider.tsx +++ b/src/components/FavoriteRelaysSetting/provider.tsx @@ -7,7 +7,7 @@ type TRelaySetsSettingComponentContext = { setExpandedRelaySetId: React.Dispatch> } -export const RelaySetsSettingComponentContext = createContext< +const RelaySetsSettingComponentContext = createContext< TRelaySetsSettingComponentContext | undefined >(undefined) diff --git a/src/components/KeyboardShortcutsHelp/index.tsx b/src/components/KeyboardShortcutsHelp/index.tsx index 8d7c3b80..9fc0942d 100644 --- a/src/components/KeyboardShortcutsHelp/index.tsx +++ b/src/components/KeyboardShortcutsHelp/index.tsx @@ -24,8 +24,6 @@ import { import { useTranslation } from 'react-i18next' import readmeMarkdown from '../../../README.md?raw' -export { useKeyboardShortcutsHelp } from '@/contexts/keyboard-shortcuts-help-context' - function Kbd({ children }: { children: ReactNode }) { return ( diff --git a/src/components/LatestFromFollowsSection/index.tsx b/src/components/LatestFromFollowsSection/index.tsx index 6511535c..d53d2064 100644 --- a/src/components/LatestFromFollowsSection/index.tsx +++ b/src/components/LatestFromFollowsSection/index.tsx @@ -35,7 +35,7 @@ import UserAvatar from '../UserAvatar' import Username from '../Username' /** Curated follow list for guests (hex from npub). */ -export const RECOMMENDED_FOLLOW_CURATOR_NPUB = +const RECOMMENDED_FOLLOW_CURATOR_NPUB = 'npub1m4ny6hjqzepn4rxknuq94c2gpqzr29ufkkw7ttcxyak7v43n6vvsajc2jl' as const const MAX_FOLLOWS = 1000 diff --git a/src/components/Note/MarkdownArticle/preprocessMarkup.ts b/src/components/Note/MarkdownArticle/preprocessMarkup.ts index 825e1968..061e1977 100644 --- a/src/components/Note/MarkdownArticle/preprocessMarkup.ts +++ b/src/components/Note/MarkdownArticle/preprocessMarkup.ts @@ -1,4 +1,3 @@ -import { NOSTR_URI_INLINE_REGEX } from '@/lib/content-patterns' import { isImage, isVideo, isAudio } from '@/lib/url' import { URL_REGEX, YOUTUBE_URL_REGEX } from '@/constants' import { isSpotifyOpenUrl } from '@/lib/spotify-url' @@ -231,30 +230,3 @@ export function preprocessAsciidocMediaLinks(content: string): string { return processed } -/** - * Post-process content to convert nostr: links and hashtags - * This should be applied AFTER markup processing - */ -export function postProcessNostrLinks(content: string): string { - let processed = content - - // Convert nostr: prefixed links to embedded format - // nostr:npub1... -> [nostr:npub1...] - // nostr:note1... -> [nostr:note1...] - // etc. - const nostrRegex = new RegExp(NOSTR_URI_INLINE_REGEX.source, NOSTR_URI_INLINE_REGEX.flags) - processed = processed.replace(nostrRegex, (match) => { - // Already in a link? Don't double-wrap - // Check if it's already in markdown link syntax [text](nostr:...) - // or AsciiDoc link syntax link:nostr:...[text] - return match // Keep as is for now, will be processed by the parser - }) - - // Convert hashtags to links - // #tag -> link:/notes?t=tag[#tag] (for AsciiDoc) or [#tag](/notes?t=tag) (for Markdown) - // But only if not already in a link - // We'll handle this in the rendering phase to avoid breaking markup - - return processed -} - diff --git a/src/components/PostEditor/PostTextarea/Mention/NeventNaddrPickerDialog.tsx b/src/components/PostEditor/PostTextarea/Mention/NeventNaddrPickerDialog.tsx index a815af2d..301ea75a 100644 --- a/src/components/PostEditor/PostTextarea/Mention/NeventNaddrPickerDialog.tsx +++ b/src/components/PostEditor/PostTextarea/Mention/NeventNaddrPickerDialog.tsx @@ -30,7 +30,7 @@ type NeventNaddrPickerDialogProps = { initialMode?: PickerSearchMode } -export function NeventNaddrPickerDialog({ +function NeventNaddrPickerDialog({ open, onOpenChange, onSelect, diff --git a/src/components/PostEditor/PostTextarea/Mention/suggestion.ts b/src/components/PostEditor/PostTextarea/Mention/suggestion.ts index 32150d76..51cebd06 100644 --- a/src/components/PostEditor/PostTextarea/Mention/suggestion.ts +++ b/src/components/PostEditor/PostTextarea/Mention/suggestion.ts @@ -10,8 +10,6 @@ import tippy, { GetReferenceClientRect, Instance, Props } from 'tippy.js' import MentionList, { MentionListHandle, MentionListProps, type MentionListItem } from './MentionList' import { NEVENT_NADDR_PICKER_ID } from './constants' -export { NEVENT_NADDR_PICKER_ID } from './constants' - export type { PickerSearchMode } const MENTION_EXTENSION_NAME = 'mention' diff --git a/src/components/ScheduleVideoCallDialog/ScheduleInPersonMeetingSingleDialog.tsx b/src/components/ScheduleVideoCallDialog/ScheduleInPersonMeetingSingleDialog.tsx deleted file mode 100644 index a5b88cf8..00000000 --- a/src/components/ScheduleVideoCallDialog/ScheduleInPersonMeetingSingleDialog.tsx +++ /dev/null @@ -1,383 +0,0 @@ -import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle -} from '@/components/ui/dialog' -import { DateTimePicker } from '@/components/ui/DateTimePicker' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' -import { Textarea } from '@/components/ui/textarea' -import { - createInPersonCalendarEventDraftEvent, - createInPersonDateBasedCalendarEventDraftEvent, - createPublicMessageDraftEvent -} from '@/lib/draft-event' -import { getNoteBech32Id } from '@/lib/event' -import { randomString } from '@/lib/random' -import { useNostr } from '@/providers/NostrProvider' -import { MapPin } from 'lucide-react' -import { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { toast } from 'sonner' -import { CalendarEventPreview } from './CalendarEventPreview' - -function parseTopicTags(value: string): string[] { - return [ - ...new Set( - value - .trim() - .split(/[\s,]+/) - .map((s) => s.replace(/^#+/, '').trim()) - .filter(Boolean) - ) - ] -} - -export function ScheduleInPersonMeetingSingleDialog({ - inviteePubkey, - open, - onOpenChange -}: { - inviteePubkey: string - open: boolean - onOpenChange: (open: boolean) => void -}) { - const { t } = useTranslation() - const { publish } = useNostr() - - const [eventType, setEventType] = useState<'time' | 'date'>('time') - const [title, setTitle] = useState('') - const [startDatetime, setStartDatetime] = useState('') - const [endDatetime, setEndDatetime] = useState('') - const [startDateStr, setStartDateStr] = useState('') - const [endDateStr, setEndDateStr] = useState('') - const [location, setLocation] = useState('') - const [summary, setSummary] = useState('') - const [topics, setTopics] = useState('') - const [image, setImage] = useState('') - const [submitting, setSubmitting] = useState(false) - - const formValid = useMemo(() => { - if (eventType === 'date') { - if (!startDateStr.trim()) return false - if (endDateStr.trim() && endDateStr <= startDateStr) return false - return true - } - if (!startDatetime.trim()) return false - const startUnix = Math.floor(new Date(startDatetime).getTime() / 1000) - const endUnix = endDatetime.trim() - ? Math.floor(new Date(endDatetime).getTime() / 1000) - : undefined - if (endUnix != null && endUnix <= startUnix) return false - return true - }, [eventType, startDatetime, endDatetime, startDateStr, endDateStr]) - - const previewDraft = useMemo(() => { - if (!formValid) return null - const d = 'preview' - if (eventType === 'date') { - if (!startDateStr.trim()) return null - if (endDateStr.trim() && endDateStr <= startDateStr) return null - return createInPersonDateBasedCalendarEventDraftEvent({ - d, - title: title.trim() || t('In-person meeting'), - start: startDateStr, - end: endDateStr.trim() || undefined, - location: location.trim() || undefined, - summary: summary.trim() || undefined, - image: image.trim() || undefined, - topics: parseTopicTags(topics), - participants: [inviteePubkey] - }) - } - if (!startDatetime.trim()) return null - const startDate = new Date(startDatetime) - const startUnix = Math.floor(startDate.getTime() / 1000) - const endUnix = endDatetime.trim() - ? Math.floor(new Date(endDatetime).getTime() / 1000) - : undefined - if (endUnix != null && endUnix <= startUnix) return null - return createInPersonCalendarEventDraftEvent({ - d, - title: title.trim() || t('In-person meeting'), - start: startUnix, - end: endUnix, - location: location.trim() || undefined, - summary: summary.trim() || undefined, - image: image.trim() || undefined, - topics: parseTopicTags(topics), - participants: [inviteePubkey] - }) - }, [ - eventType, - title, - startDatetime, - endDatetime, - startDateStr, - endDateStr, - location, - summary, - topics, - image, - inviteePubkey, - t, - formValid - ]) - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!formValid) return - if (eventType === 'date') { - if (!startDateStr.trim()) { - toast.error(t('Please set a start date')) - return - } - if (endDateStr.trim() && endDateStr <= startDateStr) { - toast.error(t('End date must be after start date')) - return - } - } else { - if (!startDatetime.trim()) { - toast.error(t('Please set a start time')) - return - } - const startDate = new Date(startDatetime) - const startUnix = Math.floor(startDate.getTime() / 1000) - const endUnix = endDatetime.trim() - ? Math.floor(new Date(endDatetime).getTime() / 1000) - : undefined - if (endUnix != null && endUnix <= startUnix) { - toast.error(t('End time must be after start time')) - return - } - } - - setSubmitting(true) - try { - const d = `jumble-inperson-${randomString(12)}` - const calendarDraft = - eventType === 'date' - ? createInPersonDateBasedCalendarEventDraftEvent({ - d, - title: title.trim() || t('In-person meeting'), - start: startDateStr, - end: endDateStr.trim() || undefined, - location: location.trim() || undefined, - summary: summary.trim() || undefined, - image: image.trim() || undefined, - topics: parseTopicTags(topics), - participants: [inviteePubkey] - }) - : createInPersonCalendarEventDraftEvent({ - d, - title: title.trim() || t('In-person meeting'), - start: Math.floor(new Date(startDatetime).getTime() / 1000), - end: endDatetime.trim() - ? Math.floor(new Date(endDatetime).getTime() / 1000) - : undefined, - location: location.trim() || undefined, - summary: summary.trim() || undefined, - image: image.trim() || undefined, - topics: parseTopicTags(topics), - participants: [inviteePubkey] - }) - - const calendarEvent = await publish(calendarDraft) - const naddr = getNoteBech32Id(calendarEvent) - const messageContent = `${t("You're invited to an in-person meeting.")} nostr:${naddr}` - - const pmDraft = await createPublicMessageDraftEvent( - messageContent, - [inviteePubkey], - { addClientTag: true } - ) - await publish(pmDraft) - - toast.success(t('Meeting created and invite sent')) - onOpenChange(false) - setEventType('time') - setTitle('') - setStartDatetime('') - setEndDatetime('') - setStartDateStr('') - setEndDateStr('') - setLocation('') - setSummary('') - setTopics('') - setImage('') - } catch (err) { - toast.error(err instanceof Error ? err.message : t('Failed to create meeting')) - } finally { - setSubmitting(false) - } - } - - return ( - - - - - - {t('Schedule in-person meeting')} - - - {t('Required: start time or start date. Optional: title, end, location, summary, topics, image.')} - - -
-
-
- - setEventType(v as 'time' | 'date')} - className="mt-2 flex gap-4" - > - - - -
-
- - setTitle(e.target.value)} - placeholder={t('In-person meeting')} - className="mt-1" - /> -
- {eventType === 'date' ? ( - <> -
- - setStartDateStr(e.target.value)} - className="mt-1" - required={eventType === 'date'} - /> -
-
- - setEndDateStr(e.target.value)} - className="mt-1" - /> -
- - ) : ( - <> - - ({t('optional')}) - } - /> - - )} -
- - setLocation(e.target.value)} - placeholder={t('Address, venue, or place')} - className="mt-1" - /> -
-
- -