diff --git a/package-lock.json b/package-lock.json index 586f2c2..5f8ded6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-hover-card": "^1.1.4", + "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-scroll-area": "1.2.0", "@radix-ui/react-separator": "^1.1.1", @@ -2832,6 +2833,28 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz", + "integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-menu": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.4.tgz", diff --git a/package.json b/package.json index 44b8615..57b3d41 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-dropdown-menu": "^2.1.4", "@radix-ui/react-hover-card": "^1.1.4", + "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.4", "@radix-ui/react-scroll-area": "1.2.0", "@radix-ui/react-separator": "^1.1.1", diff --git a/src/components/PostDialog/index.tsx b/src/components/PostDialog/index.tsx index 2d7b6d9..5c7e146 100644 --- a/src/components/PostDialog/index.tsx +++ b/src/components/PostDialog/index.tsx @@ -6,20 +6,23 @@ import { DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { Label } from '@/components/ui/label' import { ScrollArea } from '@/components/ui/scroll-area' +import { Switch } from '@/components/ui/switch' import { Textarea } from '@/components/ui/textarea' +import { StorageKey } from '@/constants' import { useToast } from '@/hooks/use-toast' import { createShortTextNoteDraftEvent } from '@/lib/draft-event' import { useNostr } from '@/providers/NostrProvider' import client from '@/services/client.service' -import { LoaderCircle } from 'lucide-react' +import { ChevronDown, LoaderCircle } from 'lucide-react' import { Event } from 'nostr-tools' -import { Dispatch, useState } from 'react' +import { Dispatch, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import UserAvatar from '../UserAvatar' import Mentions from './Metions' import Preview from './Preview' import Uploader from './Uploader' -import { useTranslation } from 'react-i18next' export default function PostDialog({ defaultContent = '', @@ -37,8 +40,14 @@ export default function PostDialog({ const { publish, checkLogin } = useNostr() const [content, setContent] = useState(defaultContent) const [posting, setPosting] = useState(false) + const [showMoreOptions, setShowMoreOptions] = useState(false) + const [addClientTag, setAddClientTag] = useState(false) const canPost = !!content && !posting + useEffect(() => { + setAddClientTag(window.localStorage.getItem(StorageKey.ADD_CLIENT_TAG) === 'true') + }, []) + const handleTextareaChange = (e: React.ChangeEvent) => { setContent(e.target.value) } @@ -58,7 +67,10 @@ export default function PostDialog({ const relayList = await client.fetchRelayList(parentEvent.pubkey) additionalRelayUrls.push(...relayList.read.slice(0, 5)) } - const draftEvent = await createShortTextNoteDraftEvent(content, parentEvent) + const draftEvent = await createShortTextNoteDraftEvent(content, { + parentEvent, + addClientTag + }) await publish(draftEvent, additionalRelayUrls) setContent('') setOpen(false) @@ -90,6 +102,11 @@ export default function PostDialog({ }) } + const onAddClientTagChange = (checked: boolean) => { + setAddClientTag(checked) + window.localStorage.setItem(StorageKey.ADD_CLIENT_TAG, checked.toString()) + } + return ( @@ -117,8 +134,20 @@ export default function PostDialog({ /> {content && }
- -
+
+ + +
+
+ {showMoreOptions && ( +
+
+ + +
+
+ {t('Show others this was sent via Jumble')} +
+
+ )}
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..683faa7 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,24 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/src/constants.ts b/src/constants.ts index 058581f..a886bc9 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,7 +2,8 @@ export const StorageKey = { THEME_SETTING: 'themeSetting', RELAY_GROUPS: 'relayGroups', ACCOUNTS: 'accounts', - CURRENT_ACCOUNT: 'currentAccount' + CURRENT_ACCOUNT: 'currentAccount', + ADD_CLIENT_TAG: 'addClientTag' } export const BIG_RELAY_URLS = [ diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 4211d8f..73f1efa 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -87,6 +87,9 @@ export default { 'reload notes': 'reload notes', 'Logged in Accounts': 'Logged in Accounts', 'Add an Account': 'Add an Account', - Accounts: 'Accounts' + Accounts: 'Accounts', + 'More options': 'More options', + 'Add client tag': 'Add client tag', + 'Show others this was sent via Jumble': 'Show others this was sent via Jumble' } } diff --git a/src/i18n/zh.ts b/src/i18n/zh.ts index 770d04d..c4a06d7 100644 --- a/src/i18n/zh.ts +++ b/src/i18n/zh.ts @@ -86,6 +86,9 @@ export default { 'reload notes': '重新加载笔记', 'Logged in Accounts': '已登录账户', 'Add an Account': '添加账户', - Accounts: '多帐户' + Accounts: '多帐户', + 'More options': '更多选项', + 'Add client tag': '添加客户端标签', + 'Show others this was sent via Jumble': '告诉别人这是通过 Jumble 发送的' } } diff --git a/src/lib/draft-event.ts b/src/lib/draft-event.ts index 52e1984..62400e0 100644 --- a/src/lib/draft-event.ts +++ b/src/lib/draft-event.ts @@ -39,10 +39,13 @@ export function createRepostDraftEvent(event: Event): TDraftEvent { export async function createShortTextNoteDraftEvent( content: string, - parentEvent?: Event + options: { + parentEvent?: Event + addClientTag?: boolean + } = {} ): Promise { const { pubkeys, otherRelatedEventIds, quoteEventIds, rootEventId, parentEventId } = - await extractMentions(content, parentEvent) + await extractMentions(content, options.parentEvent) const hashtags = extractHashtags(content) const tags = pubkeys @@ -50,7 +53,6 @@ export async function createShortTextNoteDraftEvent( .concat(otherRelatedEventIds.map((eventId) => ['e', eventId])) .concat(quoteEventIds.map((eventId) => ['q', eventId])) .concat(hashtags.map((hashtag) => ['t', hashtag])) - .concat([['client', 'jumble']]) if (rootEventId) { tags.push(['e', rootEventId, '', 'root']) @@ -60,6 +62,10 @@ export async function createShortTextNoteDraftEvent( tags.push(['e', parentEventId, '', 'reply']) } + if (options.addClientTag) { + tags.push(['client', 'jumble']) + } + return { kind: kinds.ShortTextNote, content,