migrate runtime call sites to lex clients and sdk actions
Phase 3 tasks 3-7 (parallel wave): composer/post pipeline on pdsClient/appviewClient with structural+instance blob guards and a golden CID fixture test; chat Convo/EventBus/queries on the dedicated chat client; preferences sugar to SDK actions on the PDS client; remaining state/queries producers (usePostThread unspecced flip, video scoped-token clients, notifications, starter packs, lists) to client.call; UI runtime sweep (AtUri from @atproto/syntax, moderation fns from @bsky.app/sdk/moderation, SDK RichText, ozone reason tokens, guard rewrites via #/types/bsky). Intermediate checkpoint (hooks skipped): ~114 typecheck errors remain in cross-boundary consumer files, resolved by the type-only codemod next. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {type LayoutChangeEvent, View} from 'react-native'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {ChatBskyConvoDefs, moderateProfile} from '@atproto/api'
|
||||
import {ChatBskyConvoDefs} from '@atproto/api'
|
||||
import {
|
||||
ScrollEdgeEffect,
|
||||
ScrollEdgeEffectProvider,
|
||||
} from '@bsky.app/expo-scroll-edge-effect'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {
|
||||
type RouteProp,
|
||||
@@ -45,6 +46,7 @@ import {type ConvoWithDetails, parseConvoView} from '#/components/dms/util'
|
||||
import {Error} from '#/components/Error'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {IS_LIQUID_GLASS} from '#/env'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {ChatDisabled} from './components/ChatDisabled'
|
||||
import {ChatEnded} from './components/ChatEnded'
|
||||
import {ChatLocked} from './components/ChatLocked'
|
||||
@@ -225,7 +227,8 @@ function InnerReady({
|
||||
const moderationOpts = useModerationOpts()
|
||||
const primaryMemberModeration = useMemo(() => {
|
||||
if (!primaryMember || !moderationOpts) return null
|
||||
return moderateProfile(primaryMember, moderationOpts)
|
||||
// TODO(phase4): drop toLex once useMaybeProfileShadow emits #/lexicons views
|
||||
return moderateProfile(toLex(primaryMember), moderationOpts)
|
||||
}, [primaryMember, moderationOpts])
|
||||
|
||||
const header = <MessagesListHeader convo={convo} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
import {moderateProfile} from '@atproto/api'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
|
||||
@@ -21,6 +21,7 @@ import * as ProfileCard from '#/components/ProfileCard'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {MemberMenu} from './MemberMenu'
|
||||
import {RemoveMemberPrompt} from './prompts'
|
||||
import {StatusBadge} from './StatusBadge'
|
||||
@@ -80,7 +81,8 @@ export function Member({
|
||||
return <MemberPlaceholder />
|
||||
}
|
||||
|
||||
const moderation = moderateProfile(profile, moderationOpts)
|
||||
// TODO(phase4): drop toLex once useProfileShadow emits #/lexicons views
|
||||
const moderation = moderateProfile(toLex(profile), moderationOpts)
|
||||
|
||||
const isDeletedAccount = profile.handle === 'missing.invalid'
|
||||
const displayName = isDeletedAccount
|
||||
@@ -106,10 +108,13 @@ export function Member({
|
||||
}
|
||||
|
||||
const joinedReason = profile.kind?.addedBy
|
||||
? l`Added by ${createSanitizedDisplayName(
|
||||
? // TODO(phase4): drop toLex once addedBy emits #/lexicons views
|
||||
l`Added by ${createSanitizedDisplayName(
|
||||
profile.kind.addedBy,
|
||||
true,
|
||||
moderateProfile(profile.kind.addedBy, moderationOpts).ui('displayName'),
|
||||
moderateProfile(toLex(profile.kind.addedBy), moderationOpts).ui(
|
||||
'displayName',
|
||||
),
|
||||
)}`
|
||||
: l`Added by invite link`
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import {
|
||||
ChatBskyActorDefs,
|
||||
ChatBskyConvoDefs,
|
||||
ChatBskyConvoUnlockConvo,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -19,6 +14,7 @@ import {
|
||||
type NativeStackScreenProps,
|
||||
type NavigationProp,
|
||||
} from '#/lib/routes/types'
|
||||
import {getErrorName} from '#/lib/xrpc-error'
|
||||
import {logger} from '#/logger'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useConvoQuery} from '#/state/queries/messages/conversation'
|
||||
@@ -59,6 +55,7 @@ import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {InviteLinkDialog} from '../components/InviteLinkDialog'
|
||||
import {AddMembersLink} from './AddMembersLink'
|
||||
@@ -172,16 +169,20 @@ function keyExtractor(item: Item) {
|
||||
return item.key
|
||||
}
|
||||
|
||||
/*
|
||||
* The member list now comes from the migrated lexicon-typed query, but the
|
||||
* narrowed `GroupConvoMember` target is still the old-typed shape from
|
||||
* `#/components/dms/util` (migrates in a later task) - the guard doubles as
|
||||
* the mixed-world bridge. TODO(phase4): retype to the lexicon member types
|
||||
* once dms/util migrates.
|
||||
*/
|
||||
function isGroupMember(
|
||||
member: ChatBskyActorDefs.ProfileViewBasic,
|
||||
): member is GroupConvoMember {
|
||||
member: chat.bsky.actor.defs.ProfileViewBasic,
|
||||
): member is chat.bsky.actor.defs.ProfileViewBasic & GroupConvoMember {
|
||||
// Kind is missing when the account has been deleted.
|
||||
return (
|
||||
member.kind === undefined ||
|
||||
bsky.dangerousIsType<ChatBskyActorDefs.GroupConvoMember>(
|
||||
member.kind,
|
||||
ChatBskyActorDefs.isGroupConvoMember,
|
||||
)
|
||||
bsky.isType(chat.bsky.actor.defs.groupConvoMember, member.kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -204,7 +205,15 @@ function GroupSettings({
|
||||
|
||||
const {data: memberListData = [], refetch} = useListConvoMembersQuery({
|
||||
convoId: convo.view.id,
|
||||
placeholderData: convo.members,
|
||||
/*
|
||||
* `convo.members` comes from the still-old-typed `#/components/dms/util`
|
||||
* (migrates in a later task) while the member-list query is now typed on
|
||||
* the lexicon ProfileViewBasic. TODO(phase4): drop toLex once dms/util
|
||||
* migrates.
|
||||
*/
|
||||
placeholderData: bsky.toLex<chat.bsky.actor.defs.ProfileViewBasic[]>(
|
||||
convo.members,
|
||||
),
|
||||
})
|
||||
|
||||
const {data: joinRequestsData, hasNextPage: hasMoreRequests} =
|
||||
@@ -417,7 +426,7 @@ function SettingsHeader({
|
||||
isPending: isLocking,
|
||||
} = useLockConvo(convoId, {
|
||||
onSuccess: (data, {silent}) => {
|
||||
if (!ChatBskyConvoDefs.isGroupConvo(data.convo.kind)) return
|
||||
if (!bsky.isType(chat.bsky.convo.defs.groupConvo, data.convo.kind)) return
|
||||
if (silent) return
|
||||
if (data.convo.kind.lockStatus === 'locked') {
|
||||
ax.metric('groupchat:owner:lock', {convoId})
|
||||
@@ -431,9 +440,7 @@ function SettingsHeader({
|
||||
if (lock) {
|
||||
logger.error('Failed to lock group chat', {message: e})
|
||||
Toast.show(l`Failed to lock group chat`, {type: 'error'})
|
||||
} else if (
|
||||
e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError
|
||||
) {
|
||||
} else if (getErrorName(e) === 'ConvoLockedByModeration') {
|
||||
Toast.show(l`This chat is locked by a moderation action`, {
|
||||
type: 'error',
|
||||
})
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {useEffect} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {ImageBackground} from 'expo-image'
|
||||
import {ChatBskyGroupDefs, moderateProfile} from '@atproto/api'
|
||||
import {ChatBskyGroupDefs} from '@atproto/api'
|
||||
import {type ThemeName} from '@bsky.app/alf'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
|
||||
@@ -20,6 +21,7 @@ import {PersonGroup_Stroke2_Corner2_Rounded as PersonGroupIcon} from '#/componen
|
||||
import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
const desktopDarkBg = require('../../../assets/images/chat-desktop-bg-dark.webp')
|
||||
const desktopDimBg = require('../../../assets/images/chat-desktop-bg-dim.webp')
|
||||
@@ -179,8 +181,9 @@ export function JoinRequest({setScreenState}: Props) {
|
||||
{createSanitizedDisplayName(
|
||||
joinLinkPreview.owner,
|
||||
true,
|
||||
// TODO(phase4): drop toLex once join link preview emits #/lexicons views
|
||||
moderateProfile(
|
||||
joinLinkPreview.owner,
|
||||
toLex(joinLinkPreview.owner),
|
||||
moderationOpts,
|
||||
).ui('displayName'),
|
||||
)}
|
||||
@@ -215,12 +218,14 @@ export function JoinRequest({setScreenState}: Props) {
|
||||
? l`Sign in to request access to this group chat.`
|
||||
: l`Sign in to accept invite.`}{' '}
|
||||
{joinLinkPreview.joinRule === 'followedByOwner' &&
|
||||
// TODO(phase4): drop toLex once join link preview emits #/lexicons views
|
||||
l`Only people ${createSanitizedDisplayName(
|
||||
joinLinkPreview.owner,
|
||||
true,
|
||||
moderateProfile(joinLinkPreview.owner, moderationOpts).ui(
|
||||
'displayName',
|
||||
),
|
||||
moderateProfile(
|
||||
toLex(joinLinkPreview.owner),
|
||||
moderationOpts,
|
||||
).ui('displayName'),
|
||||
)} follows can join.`}
|
||||
</Text>
|
||||
<ActionButtons setScreenState={setScreenState} />
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
ChatBskyGroupApproveJoinRequest,
|
||||
type ChatBskyGroupListJoinRequests,
|
||||
ChatBskyGroupRejectJoinRequest,
|
||||
} from '@atproto/api'
|
||||
import {type ChatBskyGroupListJoinRequests} from '@atproto/api'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {type InfiniteData, useQueryClient} from '@tanstack/react-query'
|
||||
@@ -16,6 +12,7 @@ import {
|
||||
type NativeStackScreenProps,
|
||||
type NavigationProp,
|
||||
} from '#/lib/routes/types'
|
||||
import {getErrorName} from '#/lib/xrpc-error'
|
||||
import {logger} from '#/logger'
|
||||
import {ConvoProvider, useConvo} from '#/state/messages/convo'
|
||||
import {ConvoStatus} from '#/state/messages/convo/types'
|
||||
@@ -190,18 +187,11 @@ function JoinRequestsList({
|
||||
let errorMessage = l`Failed to accept join request`
|
||||
if (isNetworkError(error)) {
|
||||
errorMessage = l`A network error occurred. Please check your internet connection.`
|
||||
} else if (
|
||||
error instanceof ChatBskyGroupApproveJoinRequest.InvalidConvoError
|
||||
) {
|
||||
} else if (getErrorName(error) === 'InvalidConvo') {
|
||||
errorMessage = l`Conversation not found.`
|
||||
} else if (
|
||||
error instanceof ChatBskyGroupApproveJoinRequest.InsufficientRoleError
|
||||
) {
|
||||
} else if (getErrorName(error) === 'InsufficientRole') {
|
||||
errorMessage = l`Only admins can accept join requests.`
|
||||
} else if (
|
||||
error instanceof
|
||||
ChatBskyGroupApproveJoinRequest.MemberLimitReachedError
|
||||
) {
|
||||
} else if (getErrorName(error) === 'MemberLimitReached') {
|
||||
errorMessage = l`The member limit has been reached.`
|
||||
}
|
||||
Toast.show(errorMessage, {type: 'error'})
|
||||
@@ -223,13 +213,9 @@ function JoinRequestsList({
|
||||
let errorMessage = l`Failed to reject join request`
|
||||
if (isNetworkError(error)) {
|
||||
errorMessage = l`A network error occurred. Please check your internet connection.`
|
||||
} else if (
|
||||
error instanceof ChatBskyGroupRejectJoinRequest.InvalidConvoError
|
||||
) {
|
||||
} else if (getErrorName(error) === 'InvalidConvo') {
|
||||
errorMessage = l`Conversation not found.`
|
||||
} else if (
|
||||
error instanceof ChatBskyGroupRejectJoinRequest.InsufficientRoleError
|
||||
) {
|
||||
} else if (getErrorName(error) === 'InsufficientRole') {
|
||||
errorMessage = l`Only admins can reject join requests.`
|
||||
}
|
||||
Toast.show(errorMessage, {type: 'error'})
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import {useCallback, useState} from 'react'
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {ToolsOzoneReportDefs} from '@atproto/api'
|
||||
import {api} from '@bsky.app/sdk'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useMutation} from '@tanstack/react-query'
|
||||
|
||||
import {BLUESKY_MOD_SERVICE_HEADERS} from '#/lib/constants'
|
||||
import {logger} from '#/logger'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {usePdsClient, useSession} from '#/state/session'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
@@ -14,6 +13,8 @@ import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {com, tools} from '#/lexicons'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
export function ChatDisabled({
|
||||
shape = 'pill',
|
||||
@@ -92,25 +93,25 @@ function DialogInner() {
|
||||
const control = Dialog.useDialogContext()
|
||||
const [details, setDetails] = useState('')
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const agent = useAgent()
|
||||
const pdsClient = usePdsClient()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const {mutate, isPending} = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!currentAccount)
|
||||
throw new Error('No current account, should be unreachable')
|
||||
await agent.createModerationReport(
|
||||
{
|
||||
reasonType: ToolsOzoneReportDefs.REASONAPPEAL,
|
||||
await pdsClient.call(
|
||||
com.atproto.moderation.createReport,
|
||||
toLex<com.atproto.moderation.createReport.$InputBody>({
|
||||
reasonType: tools.ozone.report.defs.reasonAppeal.value,
|
||||
subject: {
|
||||
$type: 'com.atproto.admin.defs#repoRef',
|
||||
did: currentAccount.did,
|
||||
},
|
||||
reason: details,
|
||||
},
|
||||
}),
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: BLUESKY_MOD_SERVICE_HEADERS,
|
||||
service: api.moderation.service,
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {type GestureResponderEvent, View} from 'react-native'
|
||||
import {type ChatBskyConvoDefs} from '@atproto/api'
|
||||
import {
|
||||
ChatBskyConvoDefs,
|
||||
moderateProfile,
|
||||
type ModerationDecision,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
} from '@bsky.app/sdk/moderation'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -53,7 +53,8 @@ import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {useIsWithinSplitView} from './splitView/context'
|
||||
|
||||
export const ChatListItemPortal = createPortalGroup()
|
||||
@@ -125,7 +126,8 @@ function DirectChatItem({
|
||||
const {isWithinLeftPanel} = useIsWithinSplitView()
|
||||
|
||||
const moderation = useMemo(
|
||||
() => moderateProfile(profile, moderationOpts),
|
||||
// TODO(phase4): drop toLex once useProfileShadow emits #/lexicons views
|
||||
() => moderateProfile(bsky.toLex(profile), moderationOpts),
|
||||
[profile, moderationOpts],
|
||||
)
|
||||
|
||||
@@ -192,7 +194,10 @@ function GroupChatItem({
|
||||
|
||||
const moderation = useMemo(
|
||||
() =>
|
||||
groupOwner ? moderateProfile(groupOwner, moderationOpts) : undefined,
|
||||
groupOwner
|
||||
? // TODO(phase4): drop toLex once useMaybeProfileShadow emits #/lexicons views
|
||||
moderateProfile(bsky.toLex(groupOwner), moderationOpts)
|
||||
: undefined,
|
||||
[groupOwner, moderationOpts],
|
||||
)
|
||||
|
||||
@@ -314,7 +319,12 @@ function BaseChatItem({
|
||||
let lastMessageSentAt: string | null = null
|
||||
|
||||
// Deleted message
|
||||
if (ChatBskyConvoDefs.isDeletedMessageView(convo.view.lastMessage)) {
|
||||
if (
|
||||
bsky.isType(
|
||||
chat.bsky.convo.defs.deletedMessageView,
|
||||
convo.view.lastMessage,
|
||||
)
|
||||
) {
|
||||
lastMessageSentAt = convo.view.lastMessage.sentAt
|
||||
|
||||
lastMessage = isDeletedAccount
|
||||
@@ -323,7 +333,7 @@ function BaseChatItem({
|
||||
}
|
||||
|
||||
// Message
|
||||
if (ChatBskyConvoDefs.isMessageView(convo.view.lastMessage)) {
|
||||
if (bsky.isType(chat.bsky.convo.defs.messageView, convo.view.lastMessage)) {
|
||||
const info = getMessageInfo({
|
||||
convo: convo.view,
|
||||
currentAccountDid: currentAccount?.did,
|
||||
@@ -339,7 +349,12 @@ function BaseChatItem({
|
||||
}
|
||||
|
||||
// Reaction
|
||||
if (ChatBskyConvoDefs.isMessageAndReactionView(convo.view.lastReaction)) {
|
||||
if (
|
||||
bsky.isType(
|
||||
chat.bsky.convo.defs.messageAndReactionView,
|
||||
convo.view.lastReaction,
|
||||
)
|
||||
) {
|
||||
const info = getReactionInfo({
|
||||
convo: convo.view,
|
||||
currentAccountDid: currentAccount?.did,
|
||||
@@ -358,7 +373,12 @@ function BaseChatItem({
|
||||
}
|
||||
|
||||
// System message
|
||||
if (ChatBskyConvoDefs.isSystemMessageView(convo.view.lastMessage)) {
|
||||
if (
|
||||
bsky.isType(
|
||||
chat.bsky.convo.defs.systemMessageView,
|
||||
convo.view.lastMessage,
|
||||
)
|
||||
) {
|
||||
const info = getSystemMessageInfo(
|
||||
convo.view.lastMessage.data,
|
||||
new Map(convo.view.members.map(m => [m.did, m])),
|
||||
@@ -404,7 +424,15 @@ function BaseChatItem({
|
||||
for (const member of convo.view.members) {
|
||||
unstableCacheProfileView(queryClient, member)
|
||||
}
|
||||
precacheConvoQuery(queryClient, convo.view)
|
||||
/*
|
||||
* `convo.view` comes from the still-old-typed `#/components/dms/util`
|
||||
* (migrates in a later task) while the convo cache is now keyed on the
|
||||
* lexicon ConvoView. TODO(phase4): drop toLex once dms/util migrates.
|
||||
*/
|
||||
precacheConvoQuery(
|
||||
queryClient,
|
||||
bsky.toLex<chat.bsky.convo.defs.ConvoView>(convo.view),
|
||||
)
|
||||
void decrementBadgeCount(convo.view.unreadCount)
|
||||
if (isDeletedAccount) {
|
||||
e.preventDefault()
|
||||
@@ -496,7 +524,13 @@ function BaseChatItem({
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
onPressIn={() => precacheConvoQuery(queryClient, convo.view)}
|
||||
onPressIn={() =>
|
||||
// see onPress: old-typed dms/util view into the new-typed cache
|
||||
precacheConvoQuery(
|
||||
queryClient,
|
||||
bsky.toLex<chat.bsky.convo.defs.ConvoView>(convo.view),
|
||||
)
|
||||
}
|
||||
onPress={onPress}
|
||||
onLongPress={showMenu && IS_NATIVE ? onLongPress : undefined}
|
||||
onAccessibilityAction={showMenu ? onLongPress : undefined}>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Pressable} from 'react-native'
|
||||
import {ChatBskyConvoUnlockConvo} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {HITSLOP_10} from '#/lib/constants'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {getErrorName} from '#/lib/xrpc-error'
|
||||
import {logger} from '#/logger'
|
||||
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
|
||||
import {useLockConvo} from '#/state/queries/messages/lock-conversation'
|
||||
@@ -41,7 +41,7 @@ export function ChatLocked({
|
||||
Toast.show(l({message: 'Group chat unlocked', context: 'toast'}))
|
||||
},
|
||||
onError: e => {
|
||||
if (e instanceof ChatBskyConvoUnlockConvo.ConvoLockedByModerationError) {
|
||||
if (getErrorName(e) === 'ConvoLockedByModeration') {
|
||||
Toast.show(l`This chat is locked by a moderation action`, {
|
||||
type: 'error',
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {moderateProfile} from '@atproto/api'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
|
||||
@@ -17,6 +17,7 @@ import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {usePromptControl} from '#/components/Prompt'
|
||||
import {Text} from '#/components/Typography'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {AcceptChatButton, DeleteChatButton, RejectMenu} from './RequestButtons'
|
||||
|
||||
export function ChatStatusInfo({convoState}: {convoState: ActiveConvoStates}) {
|
||||
@@ -114,7 +115,8 @@ function InviterHeader({
|
||||
const t = useTheme()
|
||||
const profile = useProfileShadow(profileUnshadowed)
|
||||
const moderation = useMemo(
|
||||
() => moderateProfile(profile, moderationOpts),
|
||||
// TODO(phase4): drop toLex once useProfileShadow emits #/lexicons views
|
||||
() => moderateProfile(toLex(profile), moderationOpts),
|
||||
[profile, moderationOpts],
|
||||
)
|
||||
const displayName = createSanitizedDisplayName(
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {
|
||||
type ChatBskyGroupDefs,
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {type ChatBskyGroupDefs} from '@atproto/api'
|
||||
import {moderateProfile, type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
@@ -37,6 +34,7 @@ import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {CopyTextButton} from './CopyTextButton'
|
||||
import {EditTextButton} from './EditTextButton'
|
||||
|
||||
@@ -71,7 +69,8 @@ export function InviteLinkDialog({
|
||||
const ownerName = createSanitizedDisplayName(
|
||||
owner,
|
||||
false,
|
||||
moderateProfile(owner, moderationOpts).ui('displayName'),
|
||||
// TODO(phase4): drop toLex once GroupConvoMember emits #/lexicons views
|
||||
moderateProfile(toLex(owner), moderationOpts).ui('displayName'),
|
||||
)
|
||||
|
||||
const {joinLink} = convo.details
|
||||
|
||||
@@ -13,7 +13,7 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {scheduleOnRN} from 'react-native-worklets'
|
||||
import {GlassContainer} from 'expo-glass-effect'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {type $Typed, type ChatBskyConvoDefs} from '@atproto/api'
|
||||
import {type $Typed} from '@atproto/lex'
|
||||
import {ScrollEdgeEffect} from '@bsky.app/expo-scroll-edge-effect'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {countGraphemes} from 'unicode-segmenter/grapheme'
|
||||
@@ -37,6 +37,8 @@ import {PaperPlaneVertical_Filled_Stroke2_Corner1_Rounded as PaperPlaneIcon} fro
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {type chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {type MessageEmbedState} from './MessageInputEmbed'
|
||||
|
||||
const MIN_HEIGHT = 40
|
||||
@@ -53,7 +55,7 @@ export function MessageComposer({
|
||||
onSendMessage: (
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
replyTo?: $Typed<chat.bsky.convo.defs.MessageView>,
|
||||
) => void
|
||||
messageEmbed: MessageEmbedState | undefined
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
@@ -67,7 +69,16 @@ export function MessageComposer({
|
||||
const editable = !needsEmailVerification && !loading
|
||||
const {getDraft, clearDraft} = useMessageDraft()
|
||||
const composerInternalApiRef = useComposerInternalApiRef()
|
||||
const {replyTo, clearReply} = useMessageReplies()
|
||||
const {replyTo: replyToOld, clearReply} = useMessageReplies()
|
||||
/*
|
||||
* `useMessageReplies` (`#/components/dms`, migrates in a later task) still
|
||||
* emits the old `@atproto/api` MessageView; the send path is typed on the
|
||||
* lexicon view. Structurally identical modulo branded strings.
|
||||
* TODO(phase4): drop toLex once dms/MessageReplies migrates.
|
||||
*/
|
||||
const replyTo = bsky.toLex<chat.bsky.convo.defs.MessageView | null>(
|
||||
replyToOld,
|
||||
)
|
||||
|
||||
const [text, setText] = useState(getDraft)
|
||||
useSaveMessageDraft(text)
|
||||
@@ -106,7 +117,7 @@ export function MessageComposer({
|
||||
const onSubmit = (
|
||||
message: string,
|
||||
embed: MessageEmbedState | undefined,
|
||||
replyTo: ChatBskyConvoDefs.MessageView | null,
|
||||
replyTo: chat.bsky.convo.defs.MessageView | null,
|
||||
) => {
|
||||
if (!editable) return
|
||||
if (!embed && message.trim() === '') return
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {LayoutAnimation, View} from 'react-native'
|
||||
import {
|
||||
AppBskyFeedPost,
|
||||
AppBskyRichtextFacet,
|
||||
AtUri,
|
||||
moderatePost,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {moderatePost} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {type RouteProp, useNavigation, useRoute} from '@react-navigation/native'
|
||||
|
||||
@@ -36,6 +32,7 @@ import {ContentHider} from '#/components/moderation/ContentHider'
|
||||
import {PostAlerts} from '#/components/moderation/PostAlerts'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
/**
|
||||
@@ -166,13 +163,7 @@ function MessageInputPostEmbed({
|
||||
)
|
||||
|
||||
const {rt, record} = useMemo(() => {
|
||||
if (
|
||||
post &&
|
||||
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
)
|
||||
) {
|
||||
if (post && bsky.isType(app.bsky.feed.post, post.record)) {
|
||||
return {
|
||||
rt: new RichTextAPI({
|
||||
text: post.record.text,
|
||||
|
||||
@@ -22,16 +22,9 @@ import Animated, {
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {scheduleOnRN} from 'react-native-worklets'
|
||||
import {
|
||||
type $Typed,
|
||||
type AppBskyEmbedRecord,
|
||||
AppBskyRichtextFacet,
|
||||
ChatBskyConvoDefs,
|
||||
type ChatBskyEmbedJoinLink,
|
||||
ChatBskyGroupDefs,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {type $Typed} from '@atproto/lex'
|
||||
import {useScrollEdgeEffectRef} from '@bsky.app/expo-scroll-edge-effect'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {mergeRefs} from '#/lib/merge-refs'
|
||||
@@ -52,7 +45,7 @@ import {type ConvoState, ConvoStatus} from '#/state/messages/convo/types'
|
||||
import {useGetJoinLinkPreview} from '#/state/queries/join-links'
|
||||
import {useGetPost} from '#/state/queries/post'
|
||||
import {createEmbedViewRecordFromPost} from '#/state/queries/postgate/util'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {usePdsClient, useSession} from '#/state/session'
|
||||
import {List, type ListMethods} from '#/view/com/util/List'
|
||||
import {MessageComposer} from '#/screens/Messages/components/MessageComposer'
|
||||
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
||||
@@ -71,6 +64,8 @@ import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_ANDROID, IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {app, chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {ChatStatusInfo} from './ChatStatusInfo'
|
||||
import {groupSystemMessages, type RenderItem} from './groupSystemMessages'
|
||||
import {InviteLinkDialogProvider} from './InviteLinkDialogProvider'
|
||||
@@ -116,8 +111,8 @@ function getNeighborMessage(
|
||||
neighbor.type === 'deleted-message'
|
||||
) {
|
||||
if (
|
||||
ChatBskyConvoDefs.isMessageView(neighbor.message) ||
|
||||
ChatBskyConvoDefs.isDeletedMessageView(neighbor.message)
|
||||
bsky.isType(chat.bsky.convo.defs.messageView, neighbor.message) ||
|
||||
bsky.isType(chat.bsky.convo.defs.deletedMessageView, neighbor.message)
|
||||
) {
|
||||
return neighbor.message
|
||||
}
|
||||
@@ -144,7 +139,12 @@ export function MessagesList({
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
const convoState = useConvoActive()
|
||||
const agent = useAgent()
|
||||
/*
|
||||
* Facet detection resolves handles via `com.atproto.identity.resolveHandle`,
|
||||
* which the account (PDS) client serves - chat requires a session, so the
|
||||
* client is always live here.
|
||||
*/
|
||||
const pdsClient = usePdsClient()
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
const getPost = useGetPost()
|
||||
const getJoinLinkPreview = useGetJoinLinkPreview()
|
||||
@@ -520,7 +520,7 @@ export function MessagesList({
|
||||
async (
|
||||
text: string,
|
||||
embedState?: MessageEmbedState,
|
||||
reply?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
reply?: $Typed<chat.bsky.convo.defs.MessageView>,
|
||||
) => {
|
||||
let rt = new RichText({text: text.trimEnd()}, {cleanNewlines: true})
|
||||
|
||||
@@ -533,14 +533,14 @@ export function MessagesList({
|
||||
rt.detectFacetsWithoutResolution()
|
||||
|
||||
let embed:
|
||||
| $Typed<AppBskyEmbedRecord.Main>
|
||||
| $Typed<ChatBskyEmbedJoinLink.Main>
|
||||
| $Typed<app.bsky.embed.record.Main>
|
||||
| $Typed<chat.bsky.embed.joinLink.Main>
|
||||
| undefined
|
||||
let embedView:
|
||||
| $Typed<AppBskyEmbedRecord.View>
|
||||
| $Typed<ChatBskyEmbedJoinLink.View>
|
||||
| $Typed<app.bsky.embed.record.View>
|
||||
| $Typed<chat.bsky.embed.joinLink.View>
|
||||
| undefined
|
||||
let replyTo: ChatBskyConvoDefs.ReplyRef | undefined
|
||||
let replyTo: chat.bsky.convo.defs.ReplyRef | undefined
|
||||
|
||||
/**
|
||||
* Find the embedded link facet and, if it's at the start or end of the
|
||||
@@ -550,7 +550,8 @@ export function MessagesList({
|
||||
const linkFacet = rt.facets?.find(facet =>
|
||||
facet.features.find(
|
||||
feature =>
|
||||
AppBskyRichtextFacet.isLink(feature) && predicate(feature.uri),
|
||||
bsky.isType(app.bsky.richtext.facet.link, feature) &&
|
||||
predicate(feature.uri),
|
||||
),
|
||||
)
|
||||
if (linkFacet) {
|
||||
@@ -568,18 +569,24 @@ export function MessagesList({
|
||||
try {
|
||||
const post = await getPost({uri: embedState.uri})
|
||||
if (post) {
|
||||
embed = {
|
||||
/*
|
||||
* `post` comes from the still-old-typed `useGetPost` producer
|
||||
* (migrates in a later task), so its uri/view shapes carry plain
|
||||
* strings where the lexicon types are branded. TODO(phase4): drop
|
||||
* toLex once that producer migrates.
|
||||
*/
|
||||
embed = bsky.toLex<$Typed<app.bsky.embed.record.Main>>({
|
||||
$type: 'app.bsky.embed.record',
|
||||
record: {
|
||||
uri: post.uri,
|
||||
cid: post.cid,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
embedView = {
|
||||
embedView = bsky.toLex<$Typed<app.bsky.embed.record.View>>({
|
||||
$type: 'app.bsky.embed.record#view',
|
||||
record: createEmbedViewRecordFromPost(post),
|
||||
}
|
||||
})
|
||||
|
||||
stripLinkFacet(uri => {
|
||||
if (!isBskyPostUrl(uri)) return false
|
||||
@@ -604,10 +611,14 @@ export function MessagesList({
|
||||
|
||||
const joinLinkPreview = await getJoinLinkPreview({code, hasSession})
|
||||
if (joinLinkPreview) {
|
||||
embedView = {
|
||||
/*
|
||||
* The preview comes from the still-old-typed `join-links.ts` query
|
||||
* (migrates in a later task). TODO(phase4): drop toLex once it does.
|
||||
*/
|
||||
embedView = bsky.toLex<$Typed<chat.bsky.embed.joinLink.View>>({
|
||||
$type: 'chat.bsky.embed.joinLink#view',
|
||||
joinLinkPreview,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
stripLinkFacet(uri => getChatInviteCodeFromUrl(uri) === code)
|
||||
@@ -617,7 +628,7 @@ export function MessagesList({
|
||||
replyTo = {messageId: reply.id}
|
||||
}
|
||||
|
||||
await rt.detectFacets(agent)
|
||||
await rt.detectFacets(pdsClient)
|
||||
|
||||
rt = shortenLinks(rt)
|
||||
rt = stripInvalidMentions(rt)
|
||||
@@ -658,7 +669,10 @@ export function MessagesList({
|
||||
}
|
||||
if (
|
||||
embedView?.$type === 'chat.bsky.embed.joinLink#view' &&
|
||||
ChatBskyGroupDefs.isJoinLinkPreviewView(embedView.joinLinkPreview)
|
||||
bsky.isType(
|
||||
chat.bsky.group.defs.joinLinkPreviewView,
|
||||
embedView.joinLinkPreview,
|
||||
)
|
||||
) {
|
||||
ax.metric('groupchat:inviteLink:shared', {
|
||||
convoId: embedView.joinLinkPreview.convoId,
|
||||
@@ -667,7 +681,7 @@ export function MessagesList({
|
||||
}
|
||||
},
|
||||
[
|
||||
agent,
|
||||
pdsClient,
|
||||
convoState,
|
||||
getPost,
|
||||
getJoinLinkPreview,
|
||||
@@ -896,7 +910,7 @@ function Composer({
|
||||
onSendMessage: (
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
replyTo?: $Typed<chat.bsky.convo.defs.MessageView>,
|
||||
) => Promise<void>
|
||||
messageEmbed: MessageEmbedState | undefined
|
||||
setEmbed: (embedUrl: string | undefined) => void
|
||||
@@ -906,7 +920,7 @@ function Composer({
|
||||
(
|
||||
message: string,
|
||||
embed?: MessageEmbedState,
|
||||
replyTo?: $Typed<ChatBskyConvoDefs.MessageView>,
|
||||
replyTo?: $Typed<chat.bsky.convo.defs.MessageView>,
|
||||
) => {
|
||||
void onSendMessage(message, embed, replyTo)
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
import {moderateProfile} from '@atproto/api'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -16,6 +16,7 @@ import {Person_Stroke2_Corner2_Rounded as PersonIcon} from '#/components/icons/P
|
||||
import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
export function MessagesListInfoPanel({
|
||||
convo,
|
||||
@@ -34,10 +35,11 @@ export function MessagesListInfoPanel({
|
||||
)[0]
|
||||
const handle = sanitizeHandle(profile.handle, '@')
|
||||
const displayName = moderationOpts
|
||||
? createSanitizedDisplayName(
|
||||
? // TODO(phase4): drop toLex once convo members emit #/lexicons views
|
||||
createSanitizedDisplayName(
|
||||
profile,
|
||||
true,
|
||||
moderateProfile(profile, moderationOpts).ui('displayName'),
|
||||
moderateProfile(toLex(profile), moderationOpts).ui('displayName'),
|
||||
)
|
||||
: handle
|
||||
const profileLink =
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type ChatBskyGroupDefs,
|
||||
ChatBskyGroupWithdrawJoinRequest,
|
||||
} from '@atproto/api'
|
||||
import {type ChatBskyGroupDefs} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {isNetworkError} from '#/lib/strings/errors'
|
||||
import {getErrorName} from '#/lib/xrpc-error'
|
||||
import {useWithdrawJoinGroupChatRequest} from '#/state/queries/messages/withdraw-join-group-chat'
|
||||
import {TimeElapsed} from '#/view/com/util/TimeElapsed'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
@@ -34,10 +32,7 @@ export function OutgoingRequestListItem({
|
||||
let errorMessage = l`Failed to rescind your request. Please try again.`
|
||||
if (isNetworkError(error)) {
|
||||
errorMessage = l`There was a problem with your internet connection, please try again`
|
||||
} else if (
|
||||
error instanceof
|
||||
ChatBskyGroupWithdrawJoinRequest.InvalidJoinRequestError
|
||||
) {
|
||||
} else if (getErrorName(error) === 'InvalidJoinRequest') {
|
||||
errorMessage = l`Invalid rescind request.`
|
||||
}
|
||||
Toast.show(errorMessage)
|
||||
|
||||
@@ -41,6 +41,8 @@ import {Loader} from '#/components/Loader'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {ReportDialog} from '#/components/moderation/ReportDialog'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {type chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export function RejectMenu({
|
||||
convo,
|
||||
@@ -228,7 +230,18 @@ export function AcceptChatButton({
|
||||
onMutate: () => {
|
||||
onAcceptConvo?.()
|
||||
if (currentScreen === 'list') {
|
||||
precacheConvoQuery(queryClient, {...convo, status: 'accepted'})
|
||||
/*
|
||||
* `convo` is the old-typed view threaded from `#/components/dms/util`
|
||||
* consumers while the convo cache is now keyed on the lexicon
|
||||
* ConvoView. TODO(phase4): drop toLex once this file's props flip.
|
||||
*/
|
||||
precacheConvoQuery(
|
||||
queryClient,
|
||||
bsky.toLex<chat.bsky.convo.defs.ConvoView>({
|
||||
...convo,
|
||||
status: 'accepted',
|
||||
}),
|
||||
)
|
||||
navigation.navigate('MessagesConversation', {
|
||||
conversation: convo.id,
|
||||
accept: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {Fragment, useCallback} from 'react'
|
||||
import {Linking, View} from 'react-native'
|
||||
import {LABELS} from '@atproto/api'
|
||||
import {LABELS} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyActorProfile,
|
||||
type AppBskyGraphDefs,
|
||||
AppBskyGraphStarterpack,
|
||||
type Un$Typed,
|
||||
} from '@atproto/api'
|
||||
import {TID} from '@atproto/common-web'
|
||||
@@ -48,6 +47,7 @@ import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRight} from '#/components/ico
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {ValuePropositionPager} from './ValuePropositionPager'
|
||||
|
||||
@@ -209,10 +209,7 @@ export function StepFinished() {
|
||||
usedStarterPack: Boolean(starterPack),
|
||||
starterPackName:
|
||||
starterPack &&
|
||||
bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
starterPack.record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
)
|
||||
bsky.isType(app.bsky.graph.starterpack, starterPack.record)
|
||||
? starterPack.record.name
|
||||
: undefined,
|
||||
starterPackCreator: starterPack?.creator.did,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type ModerationOpts} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -21,6 +21,7 @@ import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
const IGNORED_ACCOUNT = 'did:plc:pifkcjimdcfwaxkanzhwxufp'
|
||||
@@ -102,12 +103,7 @@ export function StarterPackCard({
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
)
|
||||
) {
|
||||
if (!bsky.isType(app.bsky.graph.starterpack, record)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyFeedDefs, AtUri, moderateProfile} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
@@ -14,6 +16,7 @@ import {useFormatPostStatCount} from '#/components/PostControls/util'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
const AVI_SIZE = 20
|
||||
|
||||
@@ -91,7 +94,8 @@ export function KnownLikers({post}: {post: AppBskyFeedDefs.PostView}) {
|
||||
.map(like => like.actor)
|
||||
.map(actor => ({
|
||||
actor,
|
||||
moderation: moderateProfile(actor, moderationOpts),
|
||||
// TODO(phase4): drop toLex once useLikedBySampleQuery emits #/lexicons views
|
||||
moderation: moderateProfile(toLex(actor), moderationOpts),
|
||||
}))
|
||||
.filter(({actor, moderation}) => {
|
||||
const modui = moderation.ui('profileList')
|
||||
|
||||
@@ -4,9 +4,9 @@ import {
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
@@ -58,6 +58,7 @@ import {Text} from '#/components/Typography'
|
||||
import {WhoCanReply} from '#/components/WhoCanReply'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export function ThreadItemAnchor({
|
||||
@@ -563,10 +564,7 @@ function BackdatedPostIndicator({post}: {post: AppBskyFeedDefs.PostView}) {
|
||||
const control = Prompt.usePromptControl()
|
||||
|
||||
const indexedAt = new Date(post.indexedAt)
|
||||
const createdAt = bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
)
|
||||
const createdAt = bsky.isType(app.bsky.feed.post, post.record)
|
||||
? new Date(post.record.createdAt)
|
||||
: new Date(post.indexedAt)
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import {memo, type ReactNode, useCallback, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs, type AppBskyFeedThreadgate} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import {memo, useCallback, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs, type AppBskyFeedThreadgate} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import {memo, useCallback, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyLabelerDefs,
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyActorDefs, type AppBskyLabelerDefs} from '@atproto/api'
|
||||
import {moderateProfile, type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg, plural} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
@@ -37,6 +33,7 @@ import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_IOS} from '#/env'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {ProfileHeaderDisplayName} from './DisplayName'
|
||||
import {EditProfileDialog} from './EditProfileDialog'
|
||||
import {ProfileHeaderHandle} from './Handle'
|
||||
@@ -70,7 +67,7 @@ let ProfileHeaderLabeler = ({
|
||||
const isSelf = currentAccount?.did === profile.did
|
||||
|
||||
const moderation = useMemo(
|
||||
() => moderateProfile(profile, moderationOpts),
|
||||
() => moderateProfile(toLex(profile), moderationOpts),
|
||||
[profile, moderationOpts],
|
||||
)
|
||||
const {mutateAsync: likeMod, isPending: isLikePending} = useLikeMutation()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {memo, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
moderateProfile,
|
||||
type ModerationDecision,
|
||||
type ModerationOpts,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
} from '@bsky.app/sdk/moderation'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -40,6 +40,7 @@ import {useAnalytics} from '#/analytics'
|
||||
import {IS_IOS, IS_NATIVE} from '#/env'
|
||||
import {InviteFriendsDialog} from '#/features/inviteFriends'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {GermButton} from '../components/GermButton'
|
||||
import {ProfileHeaderDisplayName} from './DisplayName'
|
||||
import {EditProfileDialog} from './EditProfileDialog'
|
||||
@@ -68,7 +69,7 @@ let ProfileHeaderStandard = ({
|
||||
const {currentAccount} = useSession()
|
||||
const {_} = useLingui()
|
||||
const moderation = useMemo(
|
||||
() => moderateProfile(profile, moderationOpts),
|
||||
() => moderateProfile(toLex(profile), moderationOpts),
|
||||
[profile, moderationOpts],
|
||||
)
|
||||
const [, queueUnblock] = useProfileBlockMutationQueue(profile)
|
||||
|
||||
@@ -5,8 +5,9 @@ import Animated, {
|
||||
useAnimatedRef,
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {type AppBskyActorDefs, type ModerationDecision} from '@atproto/api'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {utils} from '@bsky.app/alf'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
|
||||
@@ -7,13 +7,9 @@ import Animated, {
|
||||
} from 'react-native-reanimated'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {scheduleOnRN} from 'react-native-worklets'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyLabelerDefs,
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyActorDefs, type AppBskyLabelerDefs} from '@atproto/api'
|
||||
import {moderateProfile, type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useIsFocused} from '@react-navigation/native'
|
||||
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
@@ -26,6 +22,7 @@ import {atoms as a, useTheme} from '#/alf'
|
||||
import {Header} from '#/components/Layout'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {
|
||||
HeaderLabelerButtons,
|
||||
ProfileHeaderLabeler,
|
||||
@@ -115,7 +112,8 @@ const MinimalHeader = memo(function MinimalHeader({
|
||||
const profile = useProfileShadow(profileUnshadowed)
|
||||
const moderationOpts = useModerationOpts()
|
||||
const moderation = useMemo(
|
||||
() => (moderationOpts ? moderateProfile(profile, moderationOpts) : null),
|
||||
() =>
|
||||
moderationOpts ? moderateProfile(toLex(profile), moderationOpts) : null,
|
||||
[moderationOpts, profile],
|
||||
)
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {useCallback, useEffect, useImperativeHandle, useMemo} from 'react'
|
||||
import {type ListRenderItemInfo, View} from 'react-native'
|
||||
import {type AppBskyLabelerDefs} from '@atproto/api'
|
||||
import {
|
||||
type AppBskyLabelerDefs,
|
||||
type InterpretedLabelValueDefinition,
|
||||
interpretLabelValueDefinitions,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Platform, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {type AppBskyActorDefs, type AppBskyActorGetProfile} from '@atproto/api'
|
||||
import {type Client} from '@atproto/lex-client'
|
||||
import {type DidString} from '@atproto/syntax'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -9,7 +10,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'
|
||||
import {until} from '#/lib/async/until'
|
||||
import {isNetworkError} from '#/lib/strings/errors'
|
||||
import {RQKEY} from '#/state/queries/profile'
|
||||
import {type SessionAgent, useAgent, useSession} from '#/state/session'
|
||||
import {useAppviewClient, usePdsClient, useSession} from '#/state/session'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
@@ -20,13 +21,14 @@ import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {app, com} from '#/lexicons'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
|
||||
export function GermButton({
|
||||
germ,
|
||||
profile,
|
||||
}: {
|
||||
germ: AppBskyActorDefs.ProfileAssociatedGerm
|
||||
germ: app.bsky.actor.defs.ProfileAssociatedGerm
|
||||
profile: bsky.profile.AnyProfileView
|
||||
}) {
|
||||
const t = useTheme()
|
||||
@@ -115,25 +117,26 @@ function GermSelfButton({did}: {did: string}) {
|
||||
const ax = useAnalytics()
|
||||
const {_} = useLingui()
|
||||
const selfExplanationDialogControl = Dialog.useDialogControl()
|
||||
const agent = useAgent()
|
||||
const pdsClient = usePdsClient()
|
||||
const appviewClient = useAppviewClient()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const {mutate: deleteDeclaration, isPending} = useMutation({
|
||||
mutationFn: async () => {
|
||||
const previousRecord = await agent.com.germnetwork.declaration
|
||||
.get({
|
||||
repo: did,
|
||||
const previousRecord = await pdsClient
|
||||
.get(com.germnetwork.declaration, {
|
||||
repo: did as DidString,
|
||||
rkey: 'self',
|
||||
})
|
||||
.then(res => res.value)
|
||||
.catch(() => null)
|
||||
|
||||
await agent.com.germnetwork.declaration.delete({
|
||||
repo: did,
|
||||
await pdsClient.delete(com.germnetwork.declaration, {
|
||||
repo: did as DidString,
|
||||
rkey: 'self',
|
||||
})
|
||||
|
||||
await whenAppViewReady(agent, did, res => !res.data.associated?.germ)
|
||||
await whenAppViewReady(appviewClient, did, res => !res.associated?.germ)
|
||||
|
||||
return previousRecord
|
||||
},
|
||||
@@ -143,14 +146,15 @@ function GermSelfButton({did}: {did: string}) {
|
||||
async function undo() {
|
||||
if (!previousRecord) return
|
||||
try {
|
||||
await agent.com.germnetwork.declaration.put(
|
||||
{
|
||||
repo: did,
|
||||
rkey: 'self',
|
||||
},
|
||||
previousRecord,
|
||||
await pdsClient.put(com.germnetwork.declaration, previousRecord, {
|
||||
repo: did as DidString,
|
||||
rkey: 'self',
|
||||
})
|
||||
await whenAppViewReady(
|
||||
appviewClient,
|
||||
did,
|
||||
res => !!res.associated?.germ,
|
||||
)
|
||||
await whenAppViewReady(agent, did, res => !!res.data.associated?.germ)
|
||||
await queryClient.refetchQueries({queryKey: RQKEY(did)})
|
||||
|
||||
Toast.show(_(msg`Germ DM reconnected`))
|
||||
@@ -273,7 +277,7 @@ function GermSelfButton({did}: {did: string}) {
|
||||
}
|
||||
|
||||
function constructGermUrl(
|
||||
declaration: AppBskyActorDefs.ProfileAssociatedGerm,
|
||||
declaration: app.bsky.actor.defs.ProfileAssociatedGerm,
|
||||
profile: bsky.profile.AnyProfileView,
|
||||
viewerDid?: string,
|
||||
) {
|
||||
@@ -319,14 +323,17 @@ function platform() {
|
||||
}
|
||||
|
||||
async function whenAppViewReady(
|
||||
agent: SessionAgent,
|
||||
appviewClient: Client,
|
||||
actor: string,
|
||||
fn: (res: AppBskyActorGetProfile.Response) => boolean,
|
||||
fn: (res: app.bsky.actor.getProfile.$OutputBody) => boolean,
|
||||
) {
|
||||
await until(
|
||||
5, // 5 tries
|
||||
1e3, // 1s delay between tries
|
||||
fn,
|
||||
() => agent.app.bsky.actor.getProfile({actor}),
|
||||
() =>
|
||||
appviewClient.call(app.bsky.actor.getProfile, {
|
||||
actor: actor as DidString,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {TRENDING_HANDLE} from '#/lib/constants'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
|
||||
import {AppBskyGraphDefs} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -23,6 +24,7 @@ import {Loader} from '#/components/Loader'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {MoreOptionsMenu} from './MoreOptionsMenu'
|
||||
import {SubscribeMenu} from './SubscribeMenu'
|
||||
|
||||
@@ -128,7 +130,8 @@ export function Header({
|
||||
list.description
|
||||
? new RichTextAPI({
|
||||
text: list.description,
|
||||
facets: list.descriptionFacets,
|
||||
// TODO(phase4): drop toLex once the list view producer emits #/lexicons facets
|
||||
facets: toLex(list.descriptionFacets),
|
||||
})
|
||||
: undefined,
|
||||
[list],
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {type AppBskyActorDefs, AppBskyGraphDefs, AtUri} from '@atproto/api'
|
||||
import {type AppBskyActorDefs, AppBskyGraphDefs} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import {useCallback, useMemo, useRef, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {useAnimatedRef} from 'react-native-reanimated'
|
||||
import {
|
||||
AppBskyGraphDefs,
|
||||
AtUri,
|
||||
moderateUserList,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyGraphDefs} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {moderateUserList, type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -42,6 +39,7 @@ import * as Layout from '#/components/Layout'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Hider from '#/components/moderation/Hider'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {toLex} from '#/types/bsky'
|
||||
import {AboutSection} from './AboutSection'
|
||||
import {ErrorScreen} from './components/ErrorScreen'
|
||||
import {Header} from './components/Header'
|
||||
@@ -169,7 +167,8 @@ function ProfileListScreenLoaded({
|
||||
const [headerHeight, setHeaderHeight] = useState<number | null>(null)
|
||||
|
||||
const moderation = useMemo(() => {
|
||||
return moderateUserList(list, moderationOpts)
|
||||
// TODO(phase4): drop toLex once ListView prop emits #/lexicons views
|
||||
return moderateUserList(toLex(list), moderationOpts)
|
||||
}, [list, moderationOpts])
|
||||
|
||||
useSetTitle(isHidden ? _(msg`List Hidden`) : list.name)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
|
||||
import {PressableScale} from '#/lib/custom-animations/PressableScale'
|
||||
import {makeCustomFeedLink} from '#/lib/routes/links'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ScrollView, View} from 'react-native'
|
||||
import {moderateProfile, type ModerationOpts} from '@atproto/api'
|
||||
import {moderateProfile, type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {createHitslop} from '#/lib/constants'
|
||||
@@ -22,6 +22,7 @@ import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
export function SearchHistory({
|
||||
searchHistory,
|
||||
@@ -206,7 +207,8 @@ function RecentProfileItem({
|
||||
const {t: l} = useLingui()
|
||||
const width = 80
|
||||
|
||||
const moderation = moderateProfile(profile, moderationOpts)
|
||||
// TODO(phase4): drop toLex once profile prop emits #/lexicons views
|
||||
const moderation = moderateProfile(toLex(profile), moderationOpts)
|
||||
const name = sanitizeDisplayName(
|
||||
profile.displayName || sanitizeHandle(profile.handle),
|
||||
moderation.ui('displayName'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type ModerationOpts} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyGraphDefs,
|
||||
AppBskyGraphStarterpack,
|
||||
moderateProfile,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -22,6 +19,7 @@ import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||
import {useStarterPackLink} from '#/components/StarterPack/StarterPackCard'
|
||||
import {SubtleHover} from '#/components/SubtleHover'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export function StarterPackCard({
|
||||
@@ -38,12 +36,7 @@ export function StarterPackCard({
|
||||
const link = useStarterPackLink({view})
|
||||
const record = view.record
|
||||
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
)
|
||||
) {
|
||||
if (!bsky.isType(app.bsky.graph.starterpack, record)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {memo, useEffect} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorSearchActors, type ModerationOpts} from '@atproto/api'
|
||||
import {type AppBskyActorSearchActors} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {type InfiniteData} from '@tanstack/react-query'
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import {useMemo} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {
|
||||
type AppBskyUnspeccedDefs,
|
||||
moderateProfile,
|
||||
RichText as RichTextApi,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyUnspeccedDefs} from '@atproto/api'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {RichText as RichTextApi} from '@bsky.app/sdk/richtext'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
@@ -31,6 +29,7 @@ import {useTrendingTopicSeen} from '#/components/TrendingTopics'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import * as ModuleHeader from '../components/ModuleHeader'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
const IMAGE_SIZE = 56
|
||||
|
||||
@@ -308,7 +307,8 @@ function useModerateTrendingActors(
|
||||
|
||||
return actors
|
||||
.filter(actor => {
|
||||
const decision = moderateProfile(actor, moderationOpts)
|
||||
// TODO(phase4): drop toLex once TrendView actors emit #/lexicons views
|
||||
const decision = moderateProfile(toLex(actor), moderationOpts)
|
||||
return !decision.ui('avatar').filter && !decision.ui('avatar').blur
|
||||
})
|
||||
.slice(0, 3)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {useMemo} from 'react'
|
||||
import {ScrollView, View} from 'react-native'
|
||||
import {AppBskyEmbedVideo, AtUri} from '@atproto/api'
|
||||
import {AppBskyEmbedVideo} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api'
|
||||
import {type $Typed, type ComAtprotoLabelDefs} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -22,6 +22,7 @@ import {Text} from '#/components/Typography'
|
||||
import {useSimpleVerificationState} from '#/components/verification'
|
||||
import {VerificationCheck} from '#/components/verification/VerificationCheck'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {com} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
@@ -53,9 +54,9 @@ export function AutomationLabelSettingsScreen({}: Props) {
|
||||
{
|
||||
profile,
|
||||
updates: existing => {
|
||||
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.validate(
|
||||
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.matches(
|
||||
com.atproto.label.defs.selfLabels,
|
||||
existing.labels,
|
||||
ComAtprotoLabelDefs.validateSelfLabels,
|
||||
)
|
||||
? existing.labels
|
||||
: {
|
||||
|
||||
@@ -4,8 +4,8 @@ import * as Contacts from 'expo-contacts'
|
||||
import {
|
||||
type AppBskyContactDefs,
|
||||
type AppBskyContactGetSyncStatus,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {useState} from 'react'
|
||||
import {Alert, LayoutAnimation, Linking, Pressable, View} from 'react-native'
|
||||
import {useReducedMotion} from 'react-native-reanimated'
|
||||
import {type AppBskyActorDefs, moderateProfile} from '@atproto/api'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {moderateProfile} from '@bsky.app/sdk/moderation'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
@@ -69,6 +70,7 @@ import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {device, useStorage} from '#/storage'
|
||||
import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Settings'>
|
||||
export function SettingsScreen({}: Props) {
|
||||
@@ -329,7 +331,8 @@ function ProfilePreview({
|
||||
|
||||
if (!moderationOpts) return null
|
||||
|
||||
const moderation = moderateProfile(profile, moderationOpts)
|
||||
// TODO(phase4): drop toLex once ProfileViewDetailed prop emits #/lexicons views
|
||||
const moderation = moderateProfile(toLex(profile), moderationOpts)
|
||||
const displayName = sanitizeDisplayName(
|
||||
profile.displayName || sanitizeHandle(profile.handle),
|
||||
moderation.ui('displayName'),
|
||||
@@ -636,7 +639,10 @@ function AccountRow({
|
||||
<UserAvatar
|
||||
size={28}
|
||||
avatar={profile.avatar}
|
||||
moderation={moderateProfile(profile, moderationOpts).ui('avatar')}
|
||||
// TODO(phase4): drop toLex once ProfileViewDetailed prop emits #/lexicons views
|
||||
moderation={moderateProfile(toLex(profile), moderationOpts).ui(
|
||||
'avatar',
|
||||
)}
|
||||
type={profile.associated?.labeler ? 'labeler' : 'user'}
|
||||
live={live}
|
||||
hideLiveBadge
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type $Typed, ComAtprotoLabelDefs} from '@atproto/api'
|
||||
import {type $Typed, type ComAtprotoLabelDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -13,6 +13,7 @@ import {useSession} from '#/state/session'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {com} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export function PwiOptOut() {
|
||||
@@ -35,9 +36,9 @@ export function PwiOptOut() {
|
||||
profile,
|
||||
updates: existing => {
|
||||
// create labels attr if needed
|
||||
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.validate(
|
||||
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.matches(
|
||||
com.atproto.label.defs.selfLabels,
|
||||
existing.labels,
|
||||
ComAtprotoLabelDefs.validateSelfLabels,
|
||||
)
|
||||
? existing.labels
|
||||
: {
|
||||
|
||||
@@ -3,7 +3,6 @@ import {AppState, type AppStateStatus, View} from 'react-native'
|
||||
import ReactNativeDeviceAttest from 'react-native-device-attest'
|
||||
import {KeyboardAvoidingView} from 'react-native-keyboard-controller'
|
||||
import Animated, {FadeIn, LayoutAnimationConfig} from 'react-native-reanimated'
|
||||
import {AppBskyGraphStarterpack} from '@atproto/api'
|
||||
import {tokens} from '@bsky.app/alf'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
@@ -32,6 +31,7 @@ import {ScreenTransition} from '#/components/ScreenTransition'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {GCP_PROJECT_ID, IS_ANDROID} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
@@ -141,10 +141,7 @@ export function Signup({onPressBack}: {onPressBack: () => void}) {
|
||||
scrollable>
|
||||
<View testID="createAccount" style={a.flex_1}>
|
||||
{showStarterPackCard &&
|
||||
bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
starterPack.record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
) ? (
|
||||
bsky.isType(app.bsky.graph.starterpack, starterPack.record) ? (
|
||||
<Animated.View
|
||||
entering={!isFetchedAtMount ? FadeIn : undefined}>
|
||||
<LinearGradientBackground
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||
import {
|
||||
AppBskyGraphDefs,
|
||||
AppBskyGraphStarterpack,
|
||||
AtUri,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -38,6 +35,7 @@ import {RichText} from '#/components/RichText'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB, IS_WEB_MOBILE_ANDROID} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
const AnimatedPressable = Animated.createAnimatedComponent(Pressable)
|
||||
@@ -86,12 +84,7 @@ export function LandingScreen({
|
||||
}
|
||||
|
||||
// Just for types, this cannot be hit
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
starterPack.record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
)
|
||||
) {
|
||||
if (!bsky.isType(app.bsky.graph.starterpack, starterPack.record)) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {
|
||||
AppBskyGraphDefs,
|
||||
AppBskyGraphStarterpack,
|
||||
AtUri,
|
||||
type ModerationOpts,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
@@ -77,6 +74,7 @@ import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
type StarterPackScreeProps = NativeStackScreenProps<
|
||||
@@ -406,19 +404,15 @@ function Header({
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
)
|
||||
) {
|
||||
if (!bsky.isType(app.bsky.graph.starterpack, record)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const richText = record.description
|
||||
? new RichTextAPI({
|
||||
text: record.description,
|
||||
facets: record.descriptionFacets,
|
||||
// TODO(phase4): drop toLex once the starterpack record producer emits #/lexicons facets
|
||||
facets: bsky.toLex(record.descriptionFacets),
|
||||
})
|
||||
: undefined
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {createContext, useContext, useReducer} from 'react'
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyGraphDefs,
|
||||
AppBskyGraphStarterpack,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs, type AppBskyGraphDefs} from '@atproto/api'
|
||||
import {msg, plural} from '@lingui/core/macro'
|
||||
|
||||
import {STARTER_PACK_MAX_SIZE} from '#/lib/constants'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
const steps = ['Details', 'Profiles', 'Feeds'] as const
|
||||
@@ -135,7 +132,7 @@ export function Provider({
|
||||
|
||||
if (
|
||||
starterPack &&
|
||||
bsky.validate(starterPack.record, AppBskyGraphStarterpack.validateRecord)
|
||||
bsky.matches(app.bsky.graph.starterpack, starterPack.record)
|
||||
) {
|
||||
return {
|
||||
canNext: true,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {useState} from 'react'
|
||||
import {type ListRenderItemInfo, View} from 'react-native'
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
|
||||
import {type AppBskyFeedDefs, type ModerationOpts} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {DISCOVER_FEED_URI} from '#/lib/constants'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useState} from 'react'
|
||||
import {type ListRenderItemInfo, View} from 'react-native'
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
|
||||
import {type ModerationOpts} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {useA11y} from '#/state/a11y'
|
||||
|
||||
@@ -7,9 +7,9 @@ import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyGraphDefs,
|
||||
AtUri,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
|
||||
+13
-14
@@ -2,19 +2,16 @@ import {useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {type ComAtprotoAdminDefs, ToolsOzoneReportDefs} from '@atproto/api'
|
||||
import {api} from '@bsky.app/sdk'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {useMutation} from '@tanstack/react-query'
|
||||
import {countGraphemes} from 'unicode-segmenter/grapheme'
|
||||
|
||||
import {
|
||||
BLUESKY_MOD_SERVICE_HEADERS,
|
||||
MAX_REPORT_REASON_GRAPHEME_LENGTH,
|
||||
} from '#/lib/constants'
|
||||
import {MAX_REPORT_REASON_GRAPHEME_LENGTH} from '#/lib/constants'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useAgent, useSession, useSessionApi} from '#/state/session'
|
||||
import {usePdsClient, useSession, useSessionApi} from '#/state/session'
|
||||
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
|
||||
import {Logo} from '#/view/icons/Logo'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
@@ -24,6 +21,8 @@ import {SimpleInlineLinkText} from '#/components/Link'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {P, Text} from '#/components/Typography'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {com, tools} from '#/lexicons'
|
||||
import {toLex} from '#/types/bsky'
|
||||
|
||||
const COL_WIDTH = 400
|
||||
|
||||
@@ -34,7 +33,7 @@ export function Takendown() {
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {currentAccount} = useSession()
|
||||
const {logoutCurrentAccount} = useSessionApi()
|
||||
const agent = useAgent()
|
||||
const pdsClient = usePdsClient()
|
||||
const [isAppealling, setIsAppealling] = useState(false)
|
||||
const [reason, setReason] = useState('')
|
||||
|
||||
@@ -50,18 +49,18 @@ export function Takendown() {
|
||||
} = useMutation({
|
||||
mutationFn: async (appealText: string) => {
|
||||
if (!currentAccount) throw new Error('No session')
|
||||
await agent.com.atproto.moderation.createReport(
|
||||
{
|
||||
reasonType: ToolsOzoneReportDefs.REASONAPPEAL,
|
||||
await pdsClient.call(
|
||||
com.atproto.moderation.createReport,
|
||||
toLex<com.atproto.moderation.createReport.$InputBody>({
|
||||
reasonType: tools.ozone.report.defs.reasonAppeal.value,
|
||||
subject: {
|
||||
$type: 'com.atproto.admin.defs#repoRef',
|
||||
did: currentAccount.did,
|
||||
} satisfies ComAtprotoAdminDefs.RepoRef,
|
||||
},
|
||||
reason: appealText,
|
||||
},
|
||||
}),
|
||||
{
|
||||
encoding: 'application/json',
|
||||
headers: BLUESKY_MOD_SERVICE_HEADERS,
|
||||
service: api.moderation.service,
|
||||
},
|
||||
)
|
||||
},
|
||||
|
||||
@@ -25,14 +25,10 @@ import {useEvent, useEventListener} from 'expo'
|
||||
import {Image, type ImageStyle} from 'expo-image'
|
||||
import {LinearGradient} from 'expo-linear-gradient'
|
||||
import {createVideoPlayer, type VideoPlayer, VideoView} from 'expo-video'
|
||||
import {
|
||||
AppBskyEmbedVideo,
|
||||
type AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
AtUri,
|
||||
type ModerationDecision,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyEmbedVideo, type AppBskyFeedDefs} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {
|
||||
type RouteProp,
|
||||
@@ -102,6 +98,7 @@ import {RichText} from '#/components/RichText'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_ANDROID} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {Scrubber, VIDEO_PLAYER_BOTTOM_INSET} from './components/Scrubber'
|
||||
|
||||
@@ -804,15 +801,13 @@ function Overlay({
|
||||
)
|
||||
|
||||
const rkey = new AtUri(post.uri).rkey
|
||||
const record = bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
)
|
||||
const record = bsky.isType(app.bsky.feed.post, post.record)
|
||||
? post.record
|
||||
: undefined
|
||||
const richText = new RichTextAPI({
|
||||
text: record?.text || '',
|
||||
facets: record?.facets,
|
||||
// TODO(phase4): drop toLex once the post record producer emits #/lexicons facets
|
||||
facets: bsky.toLex(record?.facets),
|
||||
})
|
||||
const handle = sanitizeHandle(post.author.handle, '@')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user