feat: add option to add client tag
This commit is contained in:
23
package-lock.json
generated
23
package-lock.json
generated
@@ -15,6 +15,7 @@
|
|||||||
"@radix-ui/react-dialog": "^1.1.4",
|
"@radix-ui/react-dialog": "^1.1.4",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||||
"@radix-ui/react-hover-card": "^1.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-popover": "^1.1.4",
|
||||||
"@radix-ui/react-scroll-area": "1.2.0",
|
"@radix-ui/react-scroll-area": "1.2.0",
|
||||||
"@radix-ui/react-separator": "^1.1.1",
|
"@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": {
|
"node_modules/@radix-ui/react-menu": {
|
||||||
"version": "2.1.4",
|
"version": "2.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.4.tgz",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"@radix-ui/react-dialog": "^1.1.4",
|
"@radix-ui/react-dialog": "^1.1.4",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||||
"@radix-ui/react-hover-card": "^1.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-popover": "^1.1.4",
|
||||||
"@radix-ui/react-scroll-area": "1.2.0",
|
"@radix-ui/react-scroll-area": "1.2.0",
|
||||||
"@radix-ui/react-separator": "^1.1.1",
|
"@radix-ui/react-separator": "^1.1.1",
|
||||||
|
|||||||
@@ -6,20 +6,23 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle
|
DialogTitle
|
||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
|
import { Label } from '@/components/ui/label'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
|
import { Switch } from '@/components/ui/switch'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
|
import { StorageKey } from '@/constants'
|
||||||
import { useToast } from '@/hooks/use-toast'
|
import { useToast } from '@/hooks/use-toast'
|
||||||
import { createShortTextNoteDraftEvent } from '@/lib/draft-event'
|
import { createShortTextNoteDraftEvent } from '@/lib/draft-event'
|
||||||
import { useNostr } from '@/providers/NostrProvider'
|
import { useNostr } from '@/providers/NostrProvider'
|
||||||
import client from '@/services/client.service'
|
import client from '@/services/client.service'
|
||||||
import { LoaderCircle } from 'lucide-react'
|
import { ChevronDown, LoaderCircle } from 'lucide-react'
|
||||||
import { Event } from 'nostr-tools'
|
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 UserAvatar from '../UserAvatar'
|
||||||
import Mentions from './Metions'
|
import Mentions from './Metions'
|
||||||
import Preview from './Preview'
|
import Preview from './Preview'
|
||||||
import Uploader from './Uploader'
|
import Uploader from './Uploader'
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export default function PostDialog({
|
export default function PostDialog({
|
||||||
defaultContent = '',
|
defaultContent = '',
|
||||||
@@ -37,8 +40,14 @@ export default function PostDialog({
|
|||||||
const { publish, checkLogin } = useNostr()
|
const { publish, checkLogin } = useNostr()
|
||||||
const [content, setContent] = useState(defaultContent)
|
const [content, setContent] = useState(defaultContent)
|
||||||
const [posting, setPosting] = useState(false)
|
const [posting, setPosting] = useState(false)
|
||||||
|
const [showMoreOptions, setShowMoreOptions] = useState(false)
|
||||||
|
const [addClientTag, setAddClientTag] = useState(false)
|
||||||
const canPost = !!content && !posting
|
const canPost = !!content && !posting
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAddClientTag(window.localStorage.getItem(StorageKey.ADD_CLIENT_TAG) === 'true')
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
setContent(e.target.value)
|
setContent(e.target.value)
|
||||||
}
|
}
|
||||||
@@ -58,7 +67,10 @@ export default function PostDialog({
|
|||||||
const relayList = await client.fetchRelayList(parentEvent.pubkey)
|
const relayList = await client.fetchRelayList(parentEvent.pubkey)
|
||||||
additionalRelayUrls.push(...relayList.read.slice(0, 5))
|
additionalRelayUrls.push(...relayList.read.slice(0, 5))
|
||||||
}
|
}
|
||||||
const draftEvent = await createShortTextNoteDraftEvent(content, parentEvent)
|
const draftEvent = await createShortTextNoteDraftEvent(content, {
|
||||||
|
parentEvent,
|
||||||
|
addClientTag
|
||||||
|
})
|
||||||
await publish(draftEvent, additionalRelayUrls)
|
await publish(draftEvent, additionalRelayUrls)
|
||||||
setContent('')
|
setContent('')
|
||||||
setOpen(false)
|
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 (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogContent className="p-0" withoutClose>
|
<DialogContent className="p-0" withoutClose>
|
||||||
@@ -117,8 +134,20 @@ export default function PostDialog({
|
|||||||
/>
|
/>
|
||||||
{content && <Preview content={content} />}
|
{content && <Preview content={content} />}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
<Uploader setContent={setContent} />
|
<Uploader setContent={setContent} />
|
||||||
<div className="flex gap-2">
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-foreground gap-0 px-0"
|
||||||
|
onClick={() => setShowMoreOptions((pre) => !pre)}
|
||||||
|
>
|
||||||
|
{t('More options')}
|
||||||
|
<ChevronDown
|
||||||
|
className={`transition-transform ${showMoreOptions ? 'rotate-180' : ''}`}
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
<Mentions content={content} parentEvent={parentEvent} />
|
<Mentions content={content} parentEvent={parentEvent} />
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
@@ -135,6 +164,21 @@ export default function PostDialog({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{showMoreOptions && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Label htmlFor="add-client-tag">{t('Add client tag')}</Label>
|
||||||
|
<Switch
|
||||||
|
id="add-client-tag"
|
||||||
|
checked={addClientTag}
|
||||||
|
onCheckedChange={onAddClientTagChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-muted-foreground text-xs">
|
||||||
|
{t('Show others this was sent via Jumble')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
24
src/components/ui/label.tsx
Normal file
24
src/components/ui/label.tsx
Normal file
@@ -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<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Label }
|
||||||
@@ -2,7 +2,8 @@ export const StorageKey = {
|
|||||||
THEME_SETTING: 'themeSetting',
|
THEME_SETTING: 'themeSetting',
|
||||||
RELAY_GROUPS: 'relayGroups',
|
RELAY_GROUPS: 'relayGroups',
|
||||||
ACCOUNTS: 'accounts',
|
ACCOUNTS: 'accounts',
|
||||||
CURRENT_ACCOUNT: 'currentAccount'
|
CURRENT_ACCOUNT: 'currentAccount',
|
||||||
|
ADD_CLIENT_TAG: 'addClientTag'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BIG_RELAY_URLS = [
|
export const BIG_RELAY_URLS = [
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ export default {
|
|||||||
'reload notes': 'reload notes',
|
'reload notes': 'reload notes',
|
||||||
'Logged in Accounts': 'Logged in Accounts',
|
'Logged in Accounts': 'Logged in Accounts',
|
||||||
'Add an Account': 'Add an Account',
|
'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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ export default {
|
|||||||
'reload notes': '重新加载笔记',
|
'reload notes': '重新加载笔记',
|
||||||
'Logged in Accounts': '已登录账户',
|
'Logged in Accounts': '已登录账户',
|
||||||
'Add an Account': '添加账户',
|
'Add an Account': '添加账户',
|
||||||
Accounts: '多帐户'
|
Accounts: '多帐户',
|
||||||
|
'More options': '更多选项',
|
||||||
|
'Add client tag': '添加客户端标签',
|
||||||
|
'Show others this was sent via Jumble': '告诉别人这是通过 Jumble 发送的'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,10 +39,13 @@ export function createRepostDraftEvent(event: Event): TDraftEvent {
|
|||||||
|
|
||||||
export async function createShortTextNoteDraftEvent(
|
export async function createShortTextNoteDraftEvent(
|
||||||
content: string,
|
content: string,
|
||||||
|
options: {
|
||||||
parentEvent?: Event
|
parentEvent?: Event
|
||||||
|
addClientTag?: boolean
|
||||||
|
} = {}
|
||||||
): Promise<TDraftEvent> {
|
): Promise<TDraftEvent> {
|
||||||
const { pubkeys, otherRelatedEventIds, quoteEventIds, rootEventId, parentEventId } =
|
const { pubkeys, otherRelatedEventIds, quoteEventIds, rootEventId, parentEventId } =
|
||||||
await extractMentions(content, parentEvent)
|
await extractMentions(content, options.parentEvent)
|
||||||
const hashtags = extractHashtags(content)
|
const hashtags = extractHashtags(content)
|
||||||
|
|
||||||
const tags = pubkeys
|
const tags = pubkeys
|
||||||
@@ -50,7 +53,6 @@ export async function createShortTextNoteDraftEvent(
|
|||||||
.concat(otherRelatedEventIds.map((eventId) => ['e', eventId]))
|
.concat(otherRelatedEventIds.map((eventId) => ['e', eventId]))
|
||||||
.concat(quoteEventIds.map((eventId) => ['q', eventId]))
|
.concat(quoteEventIds.map((eventId) => ['q', eventId]))
|
||||||
.concat(hashtags.map((hashtag) => ['t', hashtag]))
|
.concat(hashtags.map((hashtag) => ['t', hashtag]))
|
||||||
.concat([['client', 'jumble']])
|
|
||||||
|
|
||||||
if (rootEventId) {
|
if (rootEventId) {
|
||||||
tags.push(['e', rootEventId, '', 'root'])
|
tags.push(['e', rootEventId, '', 'root'])
|
||||||
@@ -60,6 +62,10 @@ export async function createShortTextNoteDraftEvent(
|
|||||||
tags.push(['e', parentEventId, '', 'reply'])
|
tags.push(['e', parentEventId, '', 'reply'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.addClientTag) {
|
||||||
|
tags.push(['client', 'jumble'])
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: kinds.ShortTextNote,
|
kind: kinds.ShortTextNote,
|
||||||
content,
|
content,
|
||||||
|
|||||||
Reference in New Issue
Block a user