Implement thread locking (#4545)
* Add the ability to edit threadgates * Fix bottom border on mobile * Refresh thread after threadgate editzio/stable
parent
4165a02b2d
commit
d6ce16d15a
|
@ -32,6 +32,8 @@ export type TrackPropertiesMap = {
|
||||||
'Post:ThreadMute': {} // CAN BE SERVER
|
'Post:ThreadMute': {} // CAN BE SERVER
|
||||||
'Post:ThreadUnmute': {} // CAN BE SERVER
|
'Post:ThreadUnmute': {} // CAN BE SERVER
|
||||||
'Post:Reply': {} // CAN BE SERVER
|
'Post:Reply': {} // CAN BE SERVER
|
||||||
|
'Post:EditThreadgateOpened': {}
|
||||||
|
'Post:ThreadgateEdited': {}
|
||||||
// PROFILE events
|
// PROFILE events
|
||||||
'Profile:Follow': {
|
'Profile:Follow': {
|
||||||
username: string
|
username: string
|
||||||
|
|
|
@ -270,7 +270,7 @@ export async function post(agent: BskyAgent, opts: PostOpts) {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createThreadgate(
|
export async function createThreadgate(
|
||||||
agent: BskyAgent,
|
agent: BskyAgent,
|
||||||
postUri: string,
|
postUri: string,
|
||||||
threadgate: ThreadgateSetting[],
|
threadgate: ThreadgateSetting[],
|
||||||
|
@ -296,10 +296,17 @@ async function createThreadgate(
|
||||||
}
|
}
|
||||||
|
|
||||||
const postUrip = new AtUri(postUri)
|
const postUrip = new AtUri(postUri)
|
||||||
await agent.api.app.bsky.feed.threadgate.create(
|
await agent.api.com.atproto.repo.putRecord({
|
||||||
{repo: agent.session!.did, rkey: postUrip.rkey},
|
repo: agent.session!.did,
|
||||||
{post: postUri, createdAt: new Date().toISOString(), allow},
|
collection: 'app.bsky.feed.threadgate',
|
||||||
)
|
rkey: postUrip.rkey,
|
||||||
|
record: {
|
||||||
|
$type: 'app.bsky.feed.threadgate',
|
||||||
|
post: postUri,
|
||||||
|
allow,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|
|
@ -70,7 +70,8 @@ export interface SelfLabelModal {
|
||||||
export interface ThreadgateModal {
|
export interface ThreadgateModal {
|
||||||
name: 'threadgate'
|
name: 'threadgate'
|
||||||
settings: ThreadgateSetting[]
|
settings: ThreadgateSetting[]
|
||||||
onChange: (settings: ThreadgateSetting[]) => void
|
onChange?: (settings: ThreadgateSetting[]) => void
|
||||||
|
onConfirm?: (settings: ThreadgateSetting[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChangeHandleModal {
|
export interface ChangeHandleModal {
|
||||||
|
|
|
@ -32,7 +32,7 @@ import {
|
||||||
} from './util'
|
} from './util'
|
||||||
|
|
||||||
const REPLY_TREE_DEPTH = 10
|
const REPLY_TREE_DEPTH = 10
|
||||||
const RQKEY_ROOT = 'post-thread'
|
export const RQKEY_ROOT = 'post-thread'
|
||||||
export const RQKEY = (uri: string) => [RQKEY_ROOT, uri]
|
export const RQKEY = (uri: string) => [RQKEY_ROOT, uri]
|
||||||
type ThreadViewNode = AppBskyFeedGetPostThread.OutputSchema['thread']
|
type ThreadViewNode = AppBskyFeedGetPostThread.OutputSchema['thread']
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,38 @@
|
||||||
|
import {AppBskyFeedDefs, AppBskyFeedThreadgate} from '@atproto/api'
|
||||||
|
|
||||||
export type ThreadgateSetting =
|
export type ThreadgateSetting =
|
||||||
| {type: 'nobody'}
|
| {type: 'nobody'}
|
||||||
| {type: 'mention'}
|
| {type: 'mention'}
|
||||||
| {type: 'following'}
|
| {type: 'following'}
|
||||||
| {type: 'list'; list: string}
|
| {type: 'list'; list: string}
|
||||||
|
|
||||||
|
export function threadgateViewToSettings(
|
||||||
|
threadgate: AppBskyFeedDefs.ThreadgateView | undefined,
|
||||||
|
): ThreadgateSetting[] {
|
||||||
|
const record =
|
||||||
|
threadgate &&
|
||||||
|
AppBskyFeedThreadgate.isRecord(threadgate.record) &&
|
||||||
|
AppBskyFeedThreadgate.validateRecord(threadgate.record).success
|
||||||
|
? threadgate.record
|
||||||
|
: null
|
||||||
|
if (!record) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
if (!record.allow?.length) {
|
||||||
|
return [{type: 'nobody'}]
|
||||||
|
}
|
||||||
|
return record.allow
|
||||||
|
.map(allow => {
|
||||||
|
if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') {
|
||||||
|
return {type: 'mention'}
|
||||||
|
}
|
||||||
|
if (allow.$type === 'app.bsky.feed.threadgate#followingRule') {
|
||||||
|
return {type: 'following'}
|
||||||
|
}
|
||||||
|
if (allow.$type === 'app.bsky.feed.threadgate#listRule') {
|
||||||
|
return {type: 'list', list: allow.list}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
.filter(Boolean) as ThreadgateSetting[]
|
||||||
|
}
|
||||||
|
|
|
@ -26,9 +26,11 @@ export const snapPoints = ['60%']
|
||||||
export function Component({
|
export function Component({
|
||||||
settings,
|
settings,
|
||||||
onChange,
|
onChange,
|
||||||
|
onConfirm,
|
||||||
}: {
|
}: {
|
||||||
settings: ThreadgateSetting[]
|
settings: ThreadgateSetting[]
|
||||||
onChange: (settings: ThreadgateSetting[]) => void
|
onChange?: (settings: ThreadgateSetting[]) => void
|
||||||
|
onConfirm?: (settings: ThreadgateSetting[]) => void
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {closeModal} = useModalControls()
|
const {closeModal} = useModalControls()
|
||||||
|
@ -38,12 +40,12 @@ export function Component({
|
||||||
|
|
||||||
const onPressEverybody = () => {
|
const onPressEverybody = () => {
|
||||||
setSelected([])
|
setSelected([])
|
||||||
onChange([])
|
onChange?.([])
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPressNobody = () => {
|
const onPressNobody = () => {
|
||||||
setSelected([{type: 'nobody'}])
|
setSelected([{type: 'nobody'}])
|
||||||
onChange([{type: 'nobody'}])
|
onChange?.([{type: 'nobody'}])
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPressAudience = (setting: ThreadgateSetting) => {
|
const onPressAudience = (setting: ThreadgateSetting) => {
|
||||||
|
@ -57,7 +59,7 @@ export function Component({
|
||||||
newSelected.splice(i, 1)
|
newSelected.splice(i, 1)
|
||||||
}
|
}
|
||||||
setSelected(newSelected)
|
setSelected(newSelected)
|
||||||
onChange(newSelected)
|
onChange?.(newSelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -124,6 +126,7 @@ export function Component({
|
||||||
testID="confirmBtn"
|
testID="confirmBtn"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
closeModal()
|
closeModal()
|
||||||
|
onConfirm?.(selected)
|
||||||
}}
|
}}
|
||||||
style={styles.btn}
|
style={styles.btn}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
|
|
|
@ -25,7 +25,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
import {countLines} from 'lib/strings/helpers'
|
import {countLines} from 'lib/strings/helpers'
|
||||||
import {niceDate} from 'lib/strings/time'
|
import {niceDate} from 'lib/strings/time'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
import {isWeb} from 'platform/detection'
|
import {isNative, isWeb} from 'platform/detection'
|
||||||
import {useSession} from 'state/session'
|
import {useSession} from 'state/session'
|
||||||
import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn'
|
import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
|
@ -189,6 +189,7 @@ let PostThreadItemLoaded = ({
|
||||||
const itemTitle = _(msg`Post by ${post.author.handle}`)
|
const itemTitle = _(msg`Post by ${post.author.handle}`)
|
||||||
const authorHref = makeProfileLink(post.author)
|
const authorHref = makeProfileLink(post.author)
|
||||||
const authorTitle = post.author.handle
|
const authorTitle = post.author.handle
|
||||||
|
const isThreadAuthor = getThreadAuthor(post, record) === currentAccount?.did
|
||||||
const likesHref = React.useMemo(() => {
|
const likesHref = React.useMemo(() => {
|
||||||
const urip = new AtUri(post.uri)
|
const urip = new AtUri(post.uri)
|
||||||
return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by')
|
return makeProfileLink(post.author, 'post', urip.rkey, 'liked-by')
|
||||||
|
@ -395,7 +396,11 @@ let PostThreadItemLoaded = ({
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<WhoCanReply post={post} />
|
<WhoCanReply
|
||||||
|
post={post}
|
||||||
|
isThreadAuthor={isThreadAuthor}
|
||||||
|
style={{borderBottomWidth: isNative ? 1 : 0}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -578,7 +583,9 @@ let PostThreadItemLoaded = ({
|
||||||
post={post}
|
post={post}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
|
borderBottomWidth: 1,
|
||||||
}}
|
}}
|
||||||
|
isThreadAuthor={isThreadAuthor}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -681,6 +688,20 @@ function ExpandedPostDetails({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getThreadAuthor(
|
||||||
|
post: AppBskyFeedDefs.PostView,
|
||||||
|
record: AppBskyFeedPost.Record,
|
||||||
|
): string {
|
||||||
|
if (!record.reply) {
|
||||||
|
return post.author.did
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new AtUri(record.reply.root.uri).host
|
||||||
|
} catch {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
outer: {
|
outer: {
|
||||||
borderTopWidth: hairlineWidth,
|
borderTopWidth: hairlineWidth,
|
||||||
|
|
|
@ -1,128 +1,172 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleProp, View, ViewStyle} from 'react-native'
|
import {Keyboard, StyleProp, View, ViewStyle} from 'react-native'
|
||||||
import {
|
import {AppBskyFeedDefs, AppBskyGraphDefs, AtUri} from '@atproto/api'
|
||||||
AppBskyFeedDefs,
|
import {msg, Trans} from '@lingui/macro'
|
||||||
AppBskyFeedThreadgate,
|
import {useLingui} from '@lingui/react'
|
||||||
AppBskyGraphDefs,
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
AtUri,
|
|
||||||
} from '@atproto/api'
|
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {Trans} from '@lingui/macro'
|
|
||||||
|
|
||||||
|
import {useAnalytics} from '#/lib/analytics/analytics'
|
||||||
|
import {createThreadgate} from '#/lib/api'
|
||||||
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
|
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
|
||||||
import {makeListLink, makeProfileLink} from '#/lib/routes/links'
|
import {makeListLink, makeProfileLink} from '#/lib/routes/links'
|
||||||
import {colors} from '#/lib/styles'
|
import {colors} from '#/lib/styles'
|
||||||
|
import {logger} from '#/logger'
|
||||||
|
import {isNative} from '#/platform/detection'
|
||||||
|
import {useModalControls} from '#/state/modals'
|
||||||
|
import {RQKEY_ROOT as POST_THREAD_RQKEY_ROOT} from '#/state/queries/post-thread'
|
||||||
|
import {
|
||||||
|
ThreadgateSetting,
|
||||||
|
threadgateViewToSettings,
|
||||||
|
} from '#/state/queries/threadgate'
|
||||||
|
import {useAgent} from '#/state/session'
|
||||||
|
import * as Toast from 'view/com/util/Toast'
|
||||||
|
import {Button} from '#/components/Button'
|
||||||
import {TextLink} from '../util/Link'
|
import {TextLink} from '../util/Link'
|
||||||
import {Text} from '../util/text/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
export function WhoCanReply({
|
export function WhoCanReply({
|
||||||
post,
|
post,
|
||||||
|
isThreadAuthor,
|
||||||
style,
|
style,
|
||||||
}: {
|
}: {
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
|
isThreadAuthor: boolean
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
const {track} = useAnalytics()
|
||||||
|
const {_} = useLingui()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {isMobile} = useWebMediaQueries()
|
const agent = useAgent()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const {openModal} = useModalControls()
|
||||||
const containerStyles = useColorSchemeStyle(
|
const containerStyles = useColorSchemeStyle(
|
||||||
{
|
{
|
||||||
borderColor: pal.colors.unreadNotifBorder,
|
|
||||||
backgroundColor: pal.colors.unreadNotifBg,
|
backgroundColor: pal.colors.unreadNotifBg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
borderColor: pal.colors.unreadNotifBorder,
|
|
||||||
backgroundColor: pal.colors.unreadNotifBg,
|
backgroundColor: pal.colors.unreadNotifBg,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
const iconStyles = useColorSchemeStyle(
|
|
||||||
{
|
|
||||||
backgroundColor: colors.blue3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
backgroundColor: colors.blue3,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
const textStyles = useColorSchemeStyle(
|
const textStyles = useColorSchemeStyle(
|
||||||
{color: colors.gray7},
|
{color: colors.blue5},
|
||||||
{color: colors.blue1},
|
{color: colors.blue1},
|
||||||
)
|
)
|
||||||
const record = React.useMemo(
|
const hoverStyles = useColorSchemeStyle(
|
||||||
() =>
|
{
|
||||||
post.threadgate &&
|
backgroundColor: colors.white,
|
||||||
AppBskyFeedThreadgate.isRecord(post.threadgate.record) &&
|
},
|
||||||
AppBskyFeedThreadgate.validateRecord(post.threadgate.record).success
|
{
|
||||||
? post.threadgate.record
|
backgroundColor: pal.colors.background,
|
||||||
: null,
|
},
|
||||||
|
)
|
||||||
|
const settings = React.useMemo(
|
||||||
|
() => threadgateViewToSettings(post.threadgate),
|
||||||
[post],
|
[post],
|
||||||
)
|
)
|
||||||
if (record) {
|
const isRootPost = !('reply' in post.record)
|
||||||
return (
|
|
||||||
<View
|
const onPressEdit = () => {
|
||||||
style={[
|
track('Post:EditThreadgateOpened')
|
||||||
{
|
if (isNative && Keyboard.isVisible()) {
|
||||||
flexDirection: 'row',
|
Keyboard.dismiss()
|
||||||
alignItems: 'center',
|
}
|
||||||
gap: isMobile ? 8 : 10,
|
openModal({
|
||||||
paddingHorizontal: isMobile ? 16 : 18,
|
name: 'threadgate',
|
||||||
paddingVertical: 12,
|
settings,
|
||||||
borderWidth: 1,
|
async onConfirm(newSettings: ThreadgateSetting[]) {
|
||||||
borderLeftWidth: isMobile ? 0 : 1,
|
try {
|
||||||
borderRightWidth: isMobile ? 0 : 1,
|
if (newSettings.length) {
|
||||||
},
|
await createThreadgate(agent, post.uri, newSettings)
|
||||||
containerStyles,
|
} else {
|
||||||
style,
|
await agent.api.com.atproto.repo.deleteRecord({
|
||||||
]}>
|
repo: agent.session!.did,
|
||||||
<View
|
collection: 'app.bsky.feed.threadgate',
|
||||||
style={[
|
rkey: new AtUri(post.uri).rkey,
|
||||||
{
|
})
|
||||||
flexDirection: 'row',
|
}
|
||||||
alignItems: 'center',
|
Toast.show('Thread settings updated')
|
||||||
justifyContent: 'center',
|
queryClient.invalidateQueries({
|
||||||
width: 32,
|
queryKey: [POST_THREAD_RQKEY_ROOT],
|
||||||
height: 32,
|
})
|
||||||
borderRadius: 19,
|
track('Post:ThreadgateEdited')
|
||||||
},
|
} catch (err) {
|
||||||
iconStyles,
|
Toast.show(
|
||||||
]}>
|
'There was an issue. Please check your internet connection and try again.',
|
||||||
<FontAwesomeIcon
|
)
|
||||||
icon={['far', 'comments']}
|
logger.error('Failed to edit threadgate', {message: err})
|
||||||
size={16}
|
}
|
||||||
color={'#fff'}
|
},
|
||||||
/>
|
})
|
||||||
</View>
|
|
||||||
<View style={{flex: 1}}>
|
|
||||||
<Text type="sm" style={[{flexWrap: 'wrap'}, textStyles]}>
|
|
||||||
{!record.allow?.length ? (
|
|
||||||
<Trans>Replies to this thread are disabled</Trans>
|
|
||||||
) : (
|
|
||||||
<Trans>
|
|
||||||
Only{' '}
|
|
||||||
{record.allow.map((rule, i) => (
|
|
||||||
<>
|
|
||||||
<Rule
|
|
||||||
key={`rule-${i}`}
|
|
||||||
rule={rule}
|
|
||||||
post={post}
|
|
||||||
lists={post.threadgate!.lists}
|
|
||||||
/>
|
|
||||||
<Separator
|
|
||||||
key={`sep-${i}`}
|
|
||||||
i={i}
|
|
||||||
length={record.allow!.length}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
))}{' '}
|
|
||||||
can reply.
|
|
||||||
</Trans>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
|
if (!isRootPost) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (!settings.length && !isThreadAuthor) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 10,
|
||||||
|
paddingLeft: 18,
|
||||||
|
paddingRight: 14,
|
||||||
|
paddingVertical: 10,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
},
|
||||||
|
pal.border,
|
||||||
|
containerStyles,
|
||||||
|
style,
|
||||||
|
]}>
|
||||||
|
<View style={{flex: 1, paddingVertical: 6}}>
|
||||||
|
<Text type="sm" style={[{flexWrap: 'wrap'}, textStyles]}>
|
||||||
|
{!settings.length ? (
|
||||||
|
<Trans>Everybody can reply.</Trans>
|
||||||
|
) : settings[0].type === 'nobody' ? (
|
||||||
|
<Trans>Replies to this thread are disabled.</Trans>
|
||||||
|
) : (
|
||||||
|
<Trans>
|
||||||
|
Only{' '}
|
||||||
|
{settings.map((rule, i) => (
|
||||||
|
<>
|
||||||
|
<Rule
|
||||||
|
key={`rule-${i}`}
|
||||||
|
rule={rule}
|
||||||
|
post={post}
|
||||||
|
lists={post.threadgate!.lists}
|
||||||
|
/>
|
||||||
|
<Separator key={`sep-${i}`} i={i} length={settings.length} />
|
||||||
|
</>
|
||||||
|
))}{' '}
|
||||||
|
can reply.
|
||||||
|
</Trans>
|
||||||
|
)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{isThreadAuthor && (
|
||||||
|
<View>
|
||||||
|
<Button label={_(msg`Edit`)} onPress={onPressEdit}>
|
||||||
|
{({hovered}) => (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
hovered && hoverStyles,
|
||||||
|
{paddingVertical: 6, paddingHorizontal: 8, borderRadius: 8},
|
||||||
|
]}>
|
||||||
|
<Text type="sm" style={pal.link}>
|
||||||
|
<Trans>Edit</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Rule({
|
function Rule({
|
||||||
|
@ -130,15 +174,15 @@ function Rule({
|
||||||
post,
|
post,
|
||||||
lists,
|
lists,
|
||||||
}: {
|
}: {
|
||||||
rule: any
|
rule: ThreadgateSetting
|
||||||
post: AppBskyFeedDefs.PostView
|
post: AppBskyFeedDefs.PostView
|
||||||
lists: AppBskyGraphDefs.ListViewBasic[] | undefined
|
lists: AppBskyGraphDefs.ListViewBasic[] | undefined
|
||||||
}) {
|
}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
if (AppBskyFeedThreadgate.isMentionRule(rule)) {
|
if (rule.type === 'mention') {
|
||||||
return <Trans>mentioned users</Trans>
|
return <Trans>mentioned users</Trans>
|
||||||
}
|
}
|
||||||
if (AppBskyFeedThreadgate.isFollowingRule(rule)) {
|
if (rule.type === 'following') {
|
||||||
return (
|
return (
|
||||||
<Trans>
|
<Trans>
|
||||||
users followed by{' '}
|
users followed by{' '}
|
||||||
|
@ -151,7 +195,7 @@ function Rule({
|
||||||
</Trans>
|
</Trans>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (AppBskyFeedThreadgate.isListRule(rule)) {
|
if (rule.type === 'list') {
|
||||||
const list = lists?.find(l => l.uri === rule.list)
|
const list = lists?.find(l => l.uri === rule.list)
|
||||||
if (list) {
|
if (list) {
|
||||||
const listUrip = new AtUri(list.uri)
|
const listUrip = new AtUri(list.uri)
|
||||||
|
|
Loading…
Reference in New Issue