refactor: restructure the reply list
This commit is contained in:
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { isMentioningMutedUsers } from '@/lib/event'
|
import { isMentioningMutedUsers } from '@/lib/event'
|
||||||
import { toNote } from '@/lib/link'
|
import { toNote } from '@/lib/link'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||||
@@ -15,9 +16,10 @@ import Content from '../Content'
|
|||||||
import { FormattedTimestamp } from '../FormattedTimestamp'
|
import { FormattedTimestamp } from '../FormattedTimestamp'
|
||||||
import Nip05 from '../Nip05'
|
import Nip05 from '../Nip05'
|
||||||
import NoteOptions from '../NoteOptions'
|
import NoteOptions from '../NoteOptions'
|
||||||
import StuffStats from '../StuffStats'
|
|
||||||
import ParentNotePreview from '../ParentNotePreview'
|
import ParentNotePreview from '../ParentNotePreview'
|
||||||
|
import StuffStats from '../StuffStats'
|
||||||
import TranslateButton from '../TranslateButton'
|
import TranslateButton from '../TranslateButton'
|
||||||
|
import TrustScoreBadge from '../TrustScoreBadge'
|
||||||
import UserAvatar from '../UserAvatar'
|
import UserAvatar from '../UserAvatar'
|
||||||
import Username from '../Username'
|
import Username from '../Username'
|
||||||
|
|
||||||
@@ -25,12 +27,14 @@ export default function ReplyNote({
|
|||||||
event,
|
event,
|
||||||
parentEventId,
|
parentEventId,
|
||||||
onClickParent = () => {},
|
onClickParent = () => {},
|
||||||
highlight = false
|
highlight = false,
|
||||||
|
className = ''
|
||||||
}: {
|
}: {
|
||||||
event: Event
|
event: Event
|
||||||
parentEventId?: string
|
parentEventId?: string
|
||||||
onClickParent?: () => void
|
onClickParent?: () => void
|
||||||
highlight?: boolean
|
highlight?: boolean
|
||||||
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { isSmallScreen } = useScreenSize()
|
const { isSmallScreen } = useScreenSize()
|
||||||
@@ -53,7 +57,11 @@ export default function ReplyNote({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`pb-3 border-b transition-colors duration-500 clickable ${highlight ? 'bg-primary/50' : ''}`}
|
className={cn(
|
||||||
|
'pb-3 transition-colors duration-500 clickable',
|
||||||
|
highlight ? 'bg-primary/40' : '',
|
||||||
|
className
|
||||||
|
)}
|
||||||
onClick={() => push(toNote(event))}
|
onClick={() => push(toNote(event))}
|
||||||
>
|
>
|
||||||
<Collapsible>
|
<Collapsible>
|
||||||
@@ -68,6 +76,7 @@ export default function ReplyNote({
|
|||||||
className="text-sm font-semibold text-muted-foreground hover:text-foreground truncate"
|
className="text-sm font-semibold text-muted-foreground hover:text-foreground truncate"
|
||||||
skeletonClassName="h-3"
|
skeletonClassName="h-3"
|
||||||
/>
|
/>
|
||||||
|
<TrustScoreBadge pubkey={event.pubkey} className="!size-3.5" />
|
||||||
<ClientTag event={event} />
|
<ClientTag event={event} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
<div className="flex items-center gap-1 text-sm text-muted-foreground">
|
||||||
|
|||||||
152
src/components/ReplyNoteList/SubReplies.tsx
Normal file
152
src/components/ReplyNoteList/SubReplies.tsx
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
|
import { Separator } from '@/components/ui/separator'
|
||||||
|
import { getEventKey, getKeyFromTag, getParentTag, isMentioningMutedUsers } from '@/lib/event'
|
||||||
|
import { toNote } from '@/lib/link'
|
||||||
|
import { generateBech32IdFromETag } from '@/lib/tag'
|
||||||
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
||||||
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
|
import { useReply } from '@/providers/ReplyProvider'
|
||||||
|
import { useUserTrust } from '@/providers/UserTrustProvider'
|
||||||
|
import { ChevronDown, ChevronUp } from 'lucide-react'
|
||||||
|
import { NostrEvent } from 'nostr-tools'
|
||||||
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import ReplyNote from '../ReplyNote'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
export default function SubReplies({ parentKey }: { parentKey: string }) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { push } = useSecondaryPage()
|
||||||
|
const { repliesMap } = useReply()
|
||||||
|
const { hideUntrustedInteractions, isUserTrusted } = useUserTrust()
|
||||||
|
const { mutePubkeySet } = useMuteList()
|
||||||
|
const { hideContentMentioningMutedUsers } = useContentPolicy()
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false)
|
||||||
|
const replies = useMemo(() => {
|
||||||
|
const replyKeySet = new Set<string>()
|
||||||
|
const replyEvents: NostrEvent[] = []
|
||||||
|
|
||||||
|
let parentKeys = [parentKey]
|
||||||
|
while (parentKeys.length > 0) {
|
||||||
|
const events = parentKeys.flatMap((key) => repliesMap.get(key)?.events || [])
|
||||||
|
events.forEach((evt) => {
|
||||||
|
const key = getEventKey(evt)
|
||||||
|
if (replyKeySet.has(key)) return
|
||||||
|
if (mutePubkeySet.has(evt.pubkey)) return
|
||||||
|
if (hideContentMentioningMutedUsers && isMentioningMutedUsers(evt, mutePubkeySet)) return
|
||||||
|
if (hideUntrustedInteractions && !isUserTrusted(evt.pubkey)) {
|
||||||
|
const replyKey = getEventKey(evt)
|
||||||
|
const repliesForThisReply = repliesMap.get(replyKey)
|
||||||
|
// If the reply is not trusted and there are no trusted replies for this reply, skip rendering
|
||||||
|
if (
|
||||||
|
!repliesForThisReply ||
|
||||||
|
repliesForThisReply.events.every((evt) => !isUserTrusted(evt.pubkey))
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
replyKeySet.add(key)
|
||||||
|
replyEvents.push(evt)
|
||||||
|
})
|
||||||
|
parentKeys = events.map((evt) => getEventKey(evt))
|
||||||
|
}
|
||||||
|
return replyEvents.sort((a, b) => a.created_at - b.created_at)
|
||||||
|
}, [
|
||||||
|
parentKey,
|
||||||
|
repliesMap,
|
||||||
|
mutePubkeySet,
|
||||||
|
hideContentMentioningMutedUsers,
|
||||||
|
hideUntrustedInteractions
|
||||||
|
])
|
||||||
|
const [highlightReplyKey, setHighlightReplyKey] = useState<string | undefined>(undefined)
|
||||||
|
const replyRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
||||||
|
|
||||||
|
const highlightReply = useCallback((key: string, eventId?: string, scrollTo = true) => {
|
||||||
|
let found = false
|
||||||
|
if (scrollTo) {
|
||||||
|
const ref = replyRefs.current[key]
|
||||||
|
if (ref) {
|
||||||
|
found = true
|
||||||
|
ref.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
if (eventId) push(toNote(eventId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setHighlightReplyKey(key)
|
||||||
|
setTimeout(() => {
|
||||||
|
setHighlightReplyKey((pre) => (pre === key ? undefined : pre))
|
||||||
|
}, 1500)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
if (replies.length === 0) return <Separator />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{replies.length > 1 && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setIsExpanded(!isExpanded)
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
'w-full flex items-center gap-1.5 pl-14 py-2 text-sm text-muted-foreground hover:text-foreground transition-colors clickable',
|
||||||
|
!isExpanded && 'border-b'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isExpanded ? (
|
||||||
|
<>
|
||||||
|
<ChevronUp className="size-3.5" />
|
||||||
|
<span>
|
||||||
|
{t('Hide replies')} ({replies.length})
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ChevronDown className="size-3.5" />
|
||||||
|
<span>
|
||||||
|
{t('Show replies')} ({replies.length})
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{(isExpanded || replies.length === 1) && (
|
||||||
|
<div>
|
||||||
|
{replies.map((reply, index) => {
|
||||||
|
const currentReplyKey = getEventKey(reply)
|
||||||
|
const _parentTag = getParentTag(reply)
|
||||||
|
if (_parentTag?.type !== 'e') return null
|
||||||
|
const _parentKey = _parentTag ? getKeyFromTag(_parentTag.tag) : undefined
|
||||||
|
const _parentEventId = generateBech32IdFromETag(_parentTag.tag)
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={(el) => (replyRefs.current[currentReplyKey] = el)}
|
||||||
|
key={currentReplyKey}
|
||||||
|
className="scroll-mt-12 flex"
|
||||||
|
>
|
||||||
|
<div className="w-3 flex-shrink-0 bg-border" />
|
||||||
|
<ReplyNote
|
||||||
|
className={cn(
|
||||||
|
'border-l flex-1 max-w-full border-t',
|
||||||
|
index === replies.length - 1 && 'border-b'
|
||||||
|
)}
|
||||||
|
event={reply}
|
||||||
|
parentEventId={_parentKey !== parentKey ? _parentEventId : undefined}
|
||||||
|
onClickParent={() => {
|
||||||
|
if (!_parentKey) return
|
||||||
|
highlightReply(_parentKey, _parentEventId)
|
||||||
|
}}
|
||||||
|
highlight={highlightReplyKey === currentReplyKey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -2,16 +2,13 @@ import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
|||||||
import { useStuff } from '@/hooks/useStuff'
|
import { useStuff } from '@/hooks/useStuff'
|
||||||
import {
|
import {
|
||||||
getEventKey,
|
getEventKey,
|
||||||
getKeyFromTag,
|
|
||||||
getParentTag,
|
|
||||||
getReplaceableCoordinateFromEvent,
|
getReplaceableCoordinateFromEvent,
|
||||||
getRootTag,
|
getRootTag,
|
||||||
isMentioningMutedUsers,
|
isMentioningMutedUsers,
|
||||||
isProtectedEvent,
|
isProtectedEvent,
|
||||||
isReplaceableEvent
|
isReplaceableEvent
|
||||||
} from '@/lib/event'
|
} from '@/lib/event'
|
||||||
import { toNote } from '@/lib/link'
|
import { generateBech32IdFromETag } from '@/lib/tag'
|
||||||
import { generateBech32IdFromATag, generateBech32IdFromETag } from '@/lib/tag'
|
|
||||||
import { useSecondaryPage } from '@/PageManager'
|
import { useSecondaryPage } from '@/PageManager'
|
||||||
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
@@ -23,6 +20,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { LoadingBar } from '../LoadingBar'
|
import { LoadingBar } from '../LoadingBar'
|
||||||
import ReplyNote, { ReplyNoteSkeleton } from '../ReplyNote'
|
import ReplyNote, { ReplyNoteSkeleton } from '../ReplyNote'
|
||||||
|
import SubReplies from './SubReplies'
|
||||||
|
|
||||||
type TRootInfo =
|
type TRootInfo =
|
||||||
| { type: 'E'; id: string; pubkey: string }
|
| { type: 'E'; id: string; pubkey: string }
|
||||||
@@ -40,7 +38,7 @@ export default function ReplyNoteList({
|
|||||||
index?: number
|
index?: number
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { push, currentIndex } = useSecondaryPage()
|
const { currentIndex } = useSecondaryPage()
|
||||||
const { hideUntrustedInteractions, isUserTrusted } = useUserTrust()
|
const { hideUntrustedInteractions, isUserTrusted } = useUserTrust()
|
||||||
const { mutePubkeySet } = useMuteList()
|
const { mutePubkeySet } = useMuteList()
|
||||||
const { hideContentMentioningMutedUsers } = useContentPolicy()
|
const { hideContentMentioningMutedUsers } = useContentPolicy()
|
||||||
@@ -49,30 +47,40 @@ export default function ReplyNoteList({
|
|||||||
const { event, externalContent, stuffKey } = useStuff(stuff)
|
const { event, externalContent, stuffKey } = useStuff(stuff)
|
||||||
const replies = useMemo(() => {
|
const replies = useMemo(() => {
|
||||||
const replyKeySet = new Set<string>()
|
const replyKeySet = new Set<string>()
|
||||||
const replyEvents: NEvent[] = []
|
const replyEvents = (repliesMap.get(stuffKey)?.events || []).filter((evt) => {
|
||||||
|
|
||||||
let parentKeys = [stuffKey]
|
|
||||||
while (parentKeys.length > 0) {
|
|
||||||
const events = parentKeys.flatMap((key) => repliesMap.get(key)?.events || [])
|
|
||||||
events.forEach((evt) => {
|
|
||||||
const key = getEventKey(evt)
|
const key = getEventKey(evt)
|
||||||
if (replyKeySet.has(key)) return
|
if (replyKeySet.has(key)) return false
|
||||||
if (mutePubkeySet.has(evt.pubkey)) return
|
if (mutePubkeySet.has(evt.pubkey)) return false
|
||||||
if (hideContentMentioningMutedUsers && isMentioningMutedUsers(evt, mutePubkeySet)) return
|
if (hideContentMentioningMutedUsers && isMentioningMutedUsers(evt, mutePubkeySet)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (hideUntrustedInteractions && !isUserTrusted(evt.pubkey)) {
|
||||||
|
const replyKey = getEventKey(evt)
|
||||||
|
const repliesForThisReply = repliesMap.get(replyKey)
|
||||||
|
// If the reply is not trusted and there are no trusted replies for this reply, skip rendering
|
||||||
|
if (
|
||||||
|
!repliesForThisReply ||
|
||||||
|
repliesForThisReply.events.every((evt) => !isUserTrusted(evt.pubkey))
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
replyKeySet.add(key)
|
replyKeySet.add(key)
|
||||||
replyEvents.push(evt)
|
return true
|
||||||
})
|
})
|
||||||
parentKeys = events.map((evt) => getEventKey(evt))
|
|
||||||
}
|
|
||||||
return replyEvents.sort((a, b) => a.created_at - b.created_at)
|
return replyEvents.sort((a, b) => a.created_at - b.created_at)
|
||||||
}, [stuffKey, repliesMap])
|
}, [
|
||||||
|
stuffKey,
|
||||||
|
repliesMap,
|
||||||
|
mutePubkeySet,
|
||||||
|
hideContentMentioningMutedUsers,
|
||||||
|
hideUntrustedInteractions
|
||||||
|
])
|
||||||
const [timelineKey, setTimelineKey] = useState<string | undefined>(undefined)
|
const [timelineKey, setTimelineKey] = useState<string | undefined>(undefined)
|
||||||
const [until, setUntil] = useState<number | undefined>(undefined)
|
const [until, setUntil] = useState<number | undefined>(undefined)
|
||||||
const [loading, setLoading] = useState<boolean>(false)
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
const [showCount, setShowCount] = useState(SHOW_COUNT)
|
const [showCount, setShowCount] = useState(SHOW_COUNT)
|
||||||
const [highlightReplyKey, setHighlightReplyKey] = useState<string | undefined>(undefined)
|
|
||||||
const replyRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
|
||||||
const bottomRef = useRef<HTMLDivElement | null>(null)
|
const bottomRef = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -252,26 +260,6 @@ export default function ReplyNoteList({
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
}, [loading, until, timelineKey])
|
}, [loading, until, timelineKey])
|
||||||
|
|
||||||
const highlightReply = useCallback((key: string, eventId?: string, scrollTo = true) => {
|
|
||||||
let found = false
|
|
||||||
if (scrollTo) {
|
|
||||||
const ref = replyRefs.current[key]
|
|
||||||
if (ref) {
|
|
||||||
found = true
|
|
||||||
ref.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
if (eventId) push(toNote(eventId))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setHighlightReplyKey(key)
|
|
||||||
setTimeout(() => {
|
|
||||||
setHighlightReplyKey((pre) => (pre === key ? undefined : pre))
|
|
||||||
}, 1500)
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-[80vh]">
|
<div className="min-h-[80vh]">
|
||||||
{loading && <LoadingBar />}
|
{loading && <LoadingBar />}
|
||||||
@@ -285,44 +273,11 @@ export default function ReplyNoteList({
|
|||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
{replies.slice(0, showCount).map((reply) => {
|
{replies.slice(0, showCount).map((reply) => {
|
||||||
if (hideUntrustedInteractions && !isUserTrusted(reply.pubkey)) {
|
const key = getEventKey(reply)
|
||||||
const replyKey = getEventKey(reply)
|
|
||||||
const repliesForThisReply = repliesMap.get(replyKey)
|
|
||||||
// If the reply is not trusted and there are no trusted replies for this reply, skip rendering
|
|
||||||
if (
|
|
||||||
!repliesForThisReply ||
|
|
||||||
repliesForThisReply.events.every((evt) => !isUserTrusted(evt.pubkey))
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootKey = event ? getEventKey(event) : externalContent!
|
|
||||||
const currentReplyKey = getEventKey(reply)
|
|
||||||
const parentTag = getParentTag(reply)
|
|
||||||
const parentKey = parentTag ? getKeyFromTag(parentTag.tag) : undefined
|
|
||||||
const parentEventId = parentTag
|
|
||||||
? parentTag.type === 'e'
|
|
||||||
? generateBech32IdFromETag(parentTag.tag)
|
|
||||||
: parentTag.type === 'a'
|
|
||||||
? generateBech32IdFromATag(parentTag.tag)
|
|
||||||
: undefined
|
|
||||||
: undefined
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={key}>
|
||||||
ref={(el) => (replyRefs.current[currentReplyKey] = el)}
|
<ReplyNote event={reply} />
|
||||||
key={currentReplyKey}
|
<SubReplies parentKey={key} />
|
||||||
className="scroll-mt-12"
|
|
||||||
>
|
|
||||||
<ReplyNote
|
|
||||||
event={reply}
|
|
||||||
parentEventId={rootKey !== parentKey ? parentEventId : undefined}
|
|
||||||
onClickParent={() => {
|
|
||||||
if (!parentKey) return
|
|
||||||
highlightReply(parentKey, parentEventId)
|
|
||||||
}}
|
|
||||||
highlight={highlightReplyKey === currentReplyKey}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -587,6 +587,8 @@ export default {
|
|||||||
'Relay Feeds': 'تدفقات الترحيل',
|
'Relay Feeds': 'تدفقات الترحيل',
|
||||||
'Create Highlight': 'إنشاء تمييز',
|
'Create Highlight': 'إنشاء تمييز',
|
||||||
'Write your thoughts about this highlight...': 'اكتب أفكارك حول هذا التمييز...',
|
'Write your thoughts about this highlight...': 'اكتب أفكارك حول هذا التمييز...',
|
||||||
'Publish Highlight': 'نشر التمييز'
|
'Publish Highlight': 'نشر التمييز',
|
||||||
|
'Show replies': 'إظهار الردود',
|
||||||
|
'Hide replies': 'إخفاء الردود'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -604,6 +604,8 @@ export default {
|
|||||||
'Create Highlight': 'Markierung Erstellen',
|
'Create Highlight': 'Markierung Erstellen',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'Schreiben Sie Ihre Gedanken zu dieser Markierung...',
|
'Schreiben Sie Ihre Gedanken zu dieser Markierung...',
|
||||||
'Publish Highlight': 'Markierung Veröffentlichen'
|
'Publish Highlight': 'Markierung Veröffentlichen',
|
||||||
|
'Show replies': 'Antworten anzeigen',
|
||||||
|
'Hide replies': 'Antworten ausblenden'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -590,6 +590,8 @@ export default {
|
|||||||
'Relay Feeds': 'Relay Feeds',
|
'Relay Feeds': 'Relay Feeds',
|
||||||
'Create Highlight': 'Create Highlight',
|
'Create Highlight': 'Create Highlight',
|
||||||
'Write your thoughts about this highlight...': 'Write your thoughts about this highlight...',
|
'Write your thoughts about this highlight...': 'Write your thoughts about this highlight...',
|
||||||
'Publish Highlight': 'Publish Highlight'
|
'Publish Highlight': 'Publish Highlight',
|
||||||
|
'Show replies': 'Show replies',
|
||||||
|
'Hide replies': 'Hide replies'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,6 +600,8 @@ export default {
|
|||||||
'Create Highlight': 'Crear Resaltado',
|
'Create Highlight': 'Crear Resaltado',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'Escribe tus pensamientos sobre este resaltado...',
|
'Escribe tus pensamientos sobre este resaltado...',
|
||||||
'Publish Highlight': 'Publicar Resaltado'
|
'Publish Highlight': 'Publicar Resaltado',
|
||||||
|
'Show replies': 'Mostrar respuestas',
|
||||||
|
'Hide replies': 'Ocultar respuestas'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -593,6 +593,8 @@ export default {
|
|||||||
'Relay Feeds': 'فیدهای رله',
|
'Relay Feeds': 'فیدهای رله',
|
||||||
'Create Highlight': 'ایجاد برجستهسازی',
|
'Create Highlight': 'ایجاد برجستهسازی',
|
||||||
'Write your thoughts about this highlight...': 'نظرات خود را درباره این برجستهسازی بنویسید...',
|
'Write your thoughts about this highlight...': 'نظرات خود را درباره این برجستهسازی بنویسید...',
|
||||||
'Publish Highlight': 'انتشار برجستهسازی'
|
'Publish Highlight': 'انتشار برجستهسازی',
|
||||||
|
'Show replies': 'نمایش پاسخها',
|
||||||
|
'Hide replies': 'پنهان کردن پاسخها'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -602,6 +602,8 @@ export default {
|
|||||||
'Relay Feeds': 'Flux de Relais',
|
'Relay Feeds': 'Flux de Relais',
|
||||||
'Create Highlight': 'Créer un Surlignage',
|
'Create Highlight': 'Créer un Surlignage',
|
||||||
'Write your thoughts about this highlight...': 'Écrivez vos pensées sur ce surlignage...',
|
'Write your thoughts about this highlight...': 'Écrivez vos pensées sur ce surlignage...',
|
||||||
'Publish Highlight': 'Publier le Surlignage'
|
'Publish Highlight': 'Publier le Surlignage',
|
||||||
|
'Show replies': 'Afficher les réponses',
|
||||||
|
'Hide replies': 'Masquer les réponses'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -594,6 +594,8 @@ export default {
|
|||||||
'Relay Feeds': 'रिले फ़ीड',
|
'Relay Feeds': 'रिले फ़ीड',
|
||||||
'Create Highlight': 'हाइलाइट बनाएं',
|
'Create Highlight': 'हाइलाइट बनाएं',
|
||||||
'Write your thoughts about this highlight...': 'इस हाइलाइट के बारे में अपने विचार लिखें...',
|
'Write your thoughts about this highlight...': 'इस हाइलाइट के बारे में अपने विचार लिखें...',
|
||||||
'Publish Highlight': 'हाइलाइट प्रकाशित करें'
|
'Publish Highlight': 'हाइलाइट प्रकाशित करें',
|
||||||
|
'Show replies': 'जवाब दिखाएं',
|
||||||
|
'Hide replies': 'जवाब छुपाएं'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -588,6 +588,8 @@ export default {
|
|||||||
'Relay Feeds': 'Relay Feedek',
|
'Relay Feeds': 'Relay Feedek',
|
||||||
'Create Highlight': 'Kiemelés Létrehozása',
|
'Create Highlight': 'Kiemelés Létrehozása',
|
||||||
'Write your thoughts about this highlight...': 'Írd le a gondolataidat erről a kiemelésről...',
|
'Write your thoughts about this highlight...': 'Írd le a gondolataidat erről a kiemelésről...',
|
||||||
'Publish Highlight': 'Kiemelés Közzététele'
|
'Publish Highlight': 'Kiemelés Közzététele',
|
||||||
|
'Show replies': 'Válaszok megjelenítése',
|
||||||
|
'Hide replies': 'Válaszok elrejtése'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -599,6 +599,8 @@ export default {
|
|||||||
'Create Highlight': 'Crea Evidenziazione',
|
'Create Highlight': 'Crea Evidenziazione',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'Scrivi i tuoi pensieri su questa evidenziazione...',
|
'Scrivi i tuoi pensieri su questa evidenziazione...',
|
||||||
'Publish Highlight': 'Pubblica Evidenziazione'
|
'Publish Highlight': 'Pubblica Evidenziazione',
|
||||||
|
'Show replies': 'Mostra risposte',
|
||||||
|
'Hide replies': 'Nascondi risposte'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -594,6 +594,8 @@ export default {
|
|||||||
'Create Highlight': 'ハイライトを作成',
|
'Create Highlight': 'ハイライトを作成',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'このハイライトについての考えを書いてください...',
|
'このハイライトについての考えを書いてください...',
|
||||||
'Publish Highlight': 'ハイライトを公開'
|
'Publish Highlight': 'ハイライトを公開',
|
||||||
|
'Show replies': '返信を表示',
|
||||||
|
'Hide replies': '返信を非表示'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -592,6 +592,8 @@ export default {
|
|||||||
'Relay Feeds': '릴레이 피드',
|
'Relay Feeds': '릴레이 피드',
|
||||||
'Create Highlight': '하이라이트 만들기',
|
'Create Highlight': '하이라이트 만들기',
|
||||||
'Write your thoughts about this highlight...': '이 하이라이트에 대한 생각을 작성하세요...',
|
'Write your thoughts about this highlight...': '이 하이라이트에 대한 생각을 작성하세요...',
|
||||||
'Publish Highlight': '하이라이트 게시'
|
'Publish Highlight': '하이라이트 게시',
|
||||||
|
'Show replies': '답글 표시',
|
||||||
|
'Hide replies': '답글 숨기기'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -600,6 +600,8 @@ export default {
|
|||||||
'Create Highlight': 'Utwórz Podświetlenie',
|
'Create Highlight': 'Utwórz Podświetlenie',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'Napisz swoje przemyślenia na temat tego podświetlenia...',
|
'Napisz swoje przemyślenia na temat tego podświetlenia...',
|
||||||
'Publish Highlight': 'Opublikuj Podświetlenie'
|
'Publish Highlight': 'Opublikuj Podświetlenie',
|
||||||
|
'Show replies': 'Pokaż odpowiedzi',
|
||||||
|
'Hide replies': 'Ukryj odpowiedzi'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -595,6 +595,8 @@ export default {
|
|||||||
'Create Highlight': 'Criar Destaque',
|
'Create Highlight': 'Criar Destaque',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'Escreva seus pensamentos sobre este destaque...',
|
'Escreva seus pensamentos sobre este destaque...',
|
||||||
'Publish Highlight': 'Publicar Destaque'
|
'Publish Highlight': 'Publicar Destaque',
|
||||||
|
'Show replies': 'Mostrar respostas',
|
||||||
|
'Hide replies': 'Ocultar respostas'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -598,6 +598,8 @@ export default {
|
|||||||
'Create Highlight': 'Criar Destaque',
|
'Create Highlight': 'Criar Destaque',
|
||||||
'Write your thoughts about this highlight...':
|
'Write your thoughts about this highlight...':
|
||||||
'Escreva os seus pensamentos sobre este destaque...',
|
'Escreva os seus pensamentos sobre este destaque...',
|
||||||
'Publish Highlight': 'Publicar Destaque'
|
'Publish Highlight': 'Publicar Destaque',
|
||||||
|
'Show replies': 'Mostrar respostas',
|
||||||
|
'Hide replies': 'Ocultar respostas'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -599,6 +599,8 @@ export default {
|
|||||||
'Relay Feeds': 'Ленты Релеев',
|
'Relay Feeds': 'Ленты Релеев',
|
||||||
'Create Highlight': 'Создать Выделение',
|
'Create Highlight': 'Создать Выделение',
|
||||||
'Write your thoughts about this highlight...': 'Напишите свои мысли об этом выделении...',
|
'Write your thoughts about this highlight...': 'Напишите свои мысли об этом выделении...',
|
||||||
'Publish Highlight': 'Опубликовать Выделение'
|
'Publish Highlight': 'Опубликовать Выделение',
|
||||||
|
'Show replies': 'Показать ответы',
|
||||||
|
'Hide replies': 'Скрыть ответы'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -586,6 +586,8 @@ export default {
|
|||||||
'Relay Feeds': 'ฟีดรีเลย์',
|
'Relay Feeds': 'ฟีดรีเลย์',
|
||||||
'Create Highlight': 'สร้างไฮไลท์',
|
'Create Highlight': 'สร้างไฮไลท์',
|
||||||
'Write your thoughts about this highlight...': 'เขียนความคิดของคุณเกี่ยวกับไฮไลท์นี้...',
|
'Write your thoughts about this highlight...': 'เขียนความคิดของคุณเกี่ยวกับไฮไลท์นี้...',
|
||||||
'Publish Highlight': 'เผยแพร่ไฮไลท์'
|
'Publish Highlight': 'เผยแพร่ไฮไลท์',
|
||||||
|
'Show replies': 'แสดงการตอบกลับ',
|
||||||
|
'Hide replies': 'ซ่อนการตอบกลับ'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,6 +579,8 @@ export default {
|
|||||||
'Relay Feeds': '中继订阅',
|
'Relay Feeds': '中继订阅',
|
||||||
'Create Highlight': '创建高亮',
|
'Create Highlight': '创建高亮',
|
||||||
'Write your thoughts about this highlight...': '写下你对这段高亮的想法...',
|
'Write your thoughts about this highlight...': '写下你对这段高亮的想法...',
|
||||||
'Publish Highlight': '发布高亮'
|
'Publish Highlight': '发布高亮',
|
||||||
|
'Show replies': '显示回复',
|
||||||
|
'Hide replies': '隐藏回复'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user