flip the component type imports to the generated lexicons

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-04 04:20:42 +03:00
parent dd957dd123
commit e056c43116
109 changed files with 716 additions and 741 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
import {Fragment, useCallback} from 'react' import {Fragment, useCallback} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {isJwtExpired} from '#/lib/jwt' import {isJwtExpired} from '#/lib/jwt'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
@@ -107,7 +107,7 @@ function AccountItem({
isCurrentAccount, isCurrentAccount,
isPendingAccount, isPendingAccount,
}: { }: {
profile?: AppBskyActorDefs.ProfileViewDetailed profile?: app.bsky.actor.defs.ProfileViewDetailed
account: SessionAccount account: SessionAccount
onSelect: (account: SessionAccount) => void onSelect: (account: SessionAccount) => void
isCurrentAccount: boolean isCurrentAccount: boolean
+2 -2
View File
@@ -1,7 +1,7 @@
import {type Insets, View} from 'react-native' import {type Insets, View} from 'react-native'
import {type ComAtprotoLabelDefs} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {com} from '#/lexicons'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {BotAccountAlert} from '#/components/BotAccountAlert' import {BotAccountAlert} from '#/components/BotAccountAlert'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
@@ -12,7 +12,7 @@ import type * as bsky from '#/types/bsky'
export function isBotAccount(profile: { export function isBotAccount(profile: {
did: string did: string
labels?: ComAtprotoLabelDefs.Label[] labels?: com.atproto.label.defs.Label[]
}): boolean { }): boolean {
return ( return (
profile.labels?.some(l => l.val === 'bot' && l.src === profile.did) ?? false profile.labels?.some(l => l.val === 'bot' && l.src === profile.did) ?? false
+6 -5
View File
@@ -1,10 +1,11 @@
import {useCallback, useEffect, useMemo} from 'react' import {useCallback, useEffect, useMemo} from 'react'
import {type GestureResponderEvent, View} from 'react-native' import {type GestureResponderEvent, View} from 'react-native'
import {type AppBskyFeedDefs, type AppBskyGraphDefs, AtUri} from '@atproto/api'
import {RichText as RichTextApi} from '@bsky.app/sdk/richtext' import {RichText as RichTextApi} from '@bsky.app/sdk/richtext'
import {Plural, Trans, useLingui} from '@lingui/react/macro' import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {precacheFeedFromGeneratorView} from '#/state/queries/feed' import {precacheFeedFromGeneratorView} from '#/state/queries/feed'
@@ -35,7 +36,7 @@ import type * as bsky from '#/types/bsky'
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from './icons/Trash' import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from './icons/Trash'
type Props = { type Props = {
view: AppBskyFeedDefs.GeneratorView view: app.bsky.feed.defs.GeneratorView
onPress?: () => void onPress?: () => void
} }
@@ -254,7 +255,7 @@ export function SaveButton({
pin, pin,
...props ...props
}: { }: {
view: AppBskyFeedDefs.GeneratorView | AppBskyGraphDefs.ListView view: app.bsky.feed.defs.GeneratorView | app.bsky.graph.defs.ListView
pin?: boolean pin?: boolean
text?: boolean text?: boolean
} & Partial<ButtonProps>) { } & Partial<ButtonProps>) {
@@ -269,7 +270,7 @@ function SaveButtonInner({
text = true, text = true,
...buttonProps ...buttonProps
}: { }: {
view: AppBskyFeedDefs.GeneratorView | AppBskyGraphDefs.ListView view: app.bsky.feed.defs.GeneratorView | app.bsky.graph.defs.ListView
pin?: boolean pin?: boolean
text?: boolean text?: boolean
} & Partial<ButtonProps>) { } & Partial<ButtonProps>) {
@@ -379,7 +380,7 @@ function SaveButtonInner({
export function createProfileFeedHref({ export function createProfileFeedHref({
feed, feed,
}: { }: {
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
}) { }) {
const urip = new AtUri(feed.uri) const urip = new AtUri(feed.uri)
const handleOrDid = feed.creator.handle || feed.creator.did const handleOrDid = feed.creator.handle || feed.creator.did
+2 -2
View File
@@ -7,10 +7,10 @@ import Animated, {
LayoutAnimationConfig, LayoutAnimationConfig,
LinearTransition, LinearTransition,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {type AppBskyFeedDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useGetPopularFeedsQuery} from '#/state/queries/feed' import {useGetPopularFeedsQuery} from '#/state/queries/feed'
@@ -569,7 +569,7 @@ export function SuggestedFeeds() {
const {gtMobile} = useBreakpoints() const {gtMobile} = useBreakpoints()
const feeds = useMemo(() => { const feeds = useMemo(() => {
const items: AppBskyFeedDefs.GeneratorView[] = [] const items: app.bsky.feed.defs.GeneratorView[] = []
if (!data) return items if (!data) return items
+6 -4
View File
@@ -1,9 +1,9 @@
import {useRef} from 'react' import {useRef} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {Plural, Trans, useLingui} from '@lingui/react/macro' import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
@@ -24,7 +24,7 @@ const AVI_BORDER = 1
* `count` includes blocked users and `followers` does not. * `count` includes blocked users and `followers` does not.
*/ */
export function shouldShowKnownFollowers( export function shouldShowKnownFollowers(
knownFollowers?: AppBskyActorDefs.KnownFollowers, knownFollowers?: app.bsky.actor.defs.KnownFollowers,
) { ) {
return knownFollowers && knownFollowers.followers.length > 0 return knownFollowers && knownFollowers.followers.length > 0
} }
@@ -42,7 +42,9 @@ export function KnownFollowers({
minimal?: boolean minimal?: boolean
showIfEmpty?: boolean showIfEmpty?: boolean
}) { }) {
const cache = useRef<Map<string, AppBskyActorDefs.KnownFollowers>>(new Map()) const cache = useRef<Map<string, app.bsky.actor.defs.KnownFollowers>>(
new Map(),
)
/* /*
* Results for `knownFollowers` are not sorted consistently, so when * Results for `knownFollowers` are not sorted consistently, so when
@@ -83,7 +85,7 @@ function KnownFollowersInner({
}: { }: {
profile: bsky.profile.AnyProfileView profile: bsky.profile.AnyProfileView
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
cachedKnownFollowers: AppBskyActorDefs.KnownFollowers cachedKnownFollowers: app.bsky.actor.defs.KnownFollowers
onLinkPress?: LinkProps['onPress'] onLinkPress?: LinkProps['onPress']
minimal?: boolean minimal?: boolean
showIfEmpty?: boolean showIfEmpty?: boolean
+9 -3
View File
@@ -1,8 +1,8 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {type AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {app} from '#/lexicons'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -12,7 +12,13 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
import {List} from '#/view/com/util/List' import {List} from '#/view/com/util/List'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists' import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
function renderItem({item, index}: {item: GetLikes.Like; index: number}) { function renderItem({
item,
index,
}: {
item: app.bsky.feed.getLikes.Like
index: number
}) {
return ( return (
<ProfileCardWithFollowBtn <ProfileCardWithFollowBtn
key={item.actor.did} key={item.actor.did}
@@ -22,7 +28,7 @@ function renderItem({item, index}: {item: GetLikes.Like; index: number}) {
) )
} }
function keyExtractor(item: GetLikes.Like) { function keyExtractor(item: app.bsky.feed.getLikes.Like) {
return item.actor.did return item.actor.did
} }
+5 -4
View File
@@ -1,12 +1,13 @@
import {useEffect, useMemo} from 'react' import {useEffect, useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyGraphDefs, AtUri} from '@atproto/api'
import {type ModerationUI} from '@bsky.app/sdk/moderation' import {type ModerationUI} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {moderateUserList} from '#/lib/moderation/subjects' import {moderateUserList} from '#/lib/moderation/subjects'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
@@ -44,7 +45,7 @@ const CURATELIST = 'app.bsky.graph.defs#curatelist'
const MODLIST = 'app.bsky.graph.defs#modlist' const MODLIST = 'app.bsky.graph.defs#modlist'
type Props = { type Props = {
view: AppBskyGraphDefs.ListView view: app.bsky.graph.defs.ListView
showPinButton?: boolean showPinButton?: boolean
} }
@@ -108,7 +109,7 @@ export function TitleAndByline({
}: { }: {
title: string title: string
creator?: bsky.profile.AnyProfileView creator?: bsky.profile.AnyProfileView
purpose?: AppBskyGraphDefs.ListView['purpose'] purpose?: app.bsky.graph.defs.ListView['purpose']
modUi?: ModerationUI modUi?: ModerationUI
}) { }) {
const t = useTheme() const t = useTheme()
@@ -157,7 +158,7 @@ export function TitleAndByline({
export function createProfileListHref({ export function createProfileListHref({
list, list,
}: { }: {
list: AppBskyGraphDefs.ListView list: app.bsky.graph.defs.ListView
}) { }) {
const urip = new AtUri(list.uri) const urip = new AtUri(list.uri)
const handleOrDid = list.creator.handle || list.creator.did const handleOrDid = list.creator.handle || list.creator.did
+5 -9
View File
@@ -1,12 +1,8 @@
import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {
AppBskyEmbedGallery,
type AppBskyEmbedImages,
type AppBskyFeedDefs,
} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {shareImageModal} from '#/lib/media/manip' import {shareImageModal} from '#/lib/media/manip'
import {useSaveImageToMediaLibrary} from '#/lib/media/save-image' import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
import {isGifEmbed} from '#/lib/strings/embed-player' import {isGifEmbed} from '#/lib/strings/embed-player'
@@ -27,7 +23,7 @@ export function Embed({
style, style,
peekable = false, peekable = false,
}: { }: {
embed: AppBskyFeedDefs.PostView['embed'] embed: app.bsky.feed.defs.PostView['embed']
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
peekable?: boolean peekable?: boolean
}) { }) {
@@ -59,9 +55,9 @@ export function Embed({
const tiles: React.ReactNode[] = [] const tiles: React.ReactNode[] = []
for (const item of e.view.items) { for (const item of e.view.items) {
if (tiles.length >= 4) break if (tiles.length >= 4) break
if (!AppBskyEmbedGallery.isViewImage(item)) continue if (!bsky.isType(app.bsky.embed.gallery.viewImage, item)) continue
if (peekable) { if (peekable) {
const image: AppBskyEmbedImages.ViewImage = { const image: app.bsky.embed.images.ViewImage = {
thumb: item.thumbnail, thumb: item.thumbnail,
fullsize: item.fullsize, fullsize: item.fullsize,
alt: item.alt, alt: item.alt,
@@ -202,7 +198,7 @@ export function VideoItem({
) )
} }
function PeekableImageItem({image}: {image: AppBskyEmbedImages.ViewImage}) { function PeekableImageItem({image}: {image: app.bsky.embed.images.ViewImage}) {
const {t: l} = useLingui() const {t: l} = useLingui()
const saveImage = useSaveImageToMediaLibrary() const saveImage = useSaveImageToMediaLibrary()
+3 -3
View File
@@ -1,11 +1,11 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {differenceInSeconds} from 'date-fns' import {differenceInSeconds} from 'date-fns'
import {app} from '#/lexicons'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
@@ -25,7 +25,7 @@ export function NewskieDialog({
profile, profile,
disabled, disabled,
}: { }: {
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
disabled?: boolean disabled?: boolean
}) { }) {
const t = useTheme() const t = useTheme()
@@ -75,7 +75,7 @@ function DialogInner({
createdAt, createdAt,
now, now,
}: { }: {
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
createdAt: string createdAt: string
now: number now: number
}) { }) {
@@ -1,6 +1,6 @@
import {type StyleProp, type ViewStyle} from 'react-native' import {type StyleProp, type ViewStyle} from 'react-native'
import {type AppBskyEmbedExternal} from '@atproto/api'
import {app} from '#/lexicons'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import * as ChatInvite from '#/components/dms/ChatInvite' import * as ChatInvite from '#/components/dms/ChatInvite'
import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed' import {ExternalEmbed} from '#/components/Post/Embed/ExternalEmbed'
@@ -18,7 +18,7 @@ export function ChatInviteEmbed({
style, style,
}: { }: {
code: string code: string
link: AppBskyEmbedExternal.ViewExternal link: app.bsky.embed.external.ViewExternal
onOpen?: () => void onOpen?: () => void
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
}) { }) {
@@ -34,7 +34,7 @@ function ChatInviteEmbedBody({
onOpen, onOpen,
style, style,
}: { }: {
link: AppBskyEmbedExternal.ViewExternal link: app.bsky.embed.external.ViewExternal
onOpen?: () => void onOpen?: () => void
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
}) { }) {
@@ -5,10 +5,10 @@ import {
Pressable, Pressable,
} from 'react-native' } from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {type AppBskyEmbedExternal} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {app} from '#/lexicons'
import {type EmbedPlayerParams} from '#/lib/strings/embed-player' import {type EmbedPlayerParams} from '#/lib/strings/embed-player'
import {useExternalEmbedsPrefs} from '#/state/preferences' import {useExternalEmbedsPrefs} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -22,7 +22,7 @@ export function ExternalGif({
link, link,
params, params,
}: { }: {
link: AppBskyEmbedExternal.ViewExternal link: app.bsky.embed.external.ViewExternal
params: EmbedPlayerParams params: EmbedPlayerParams
}) { }) {
const t = useTheme() const t = useTheme()
@@ -15,11 +15,11 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {WebView} from 'react-native-webview' import {WebView} from 'react-native-webview'
import {scheduleOnRN} from 'react-native-worklets' import {scheduleOnRN} from 'react-native-worklets'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {type AppBskyEmbedExternal} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import { import {
type EmbedPlayerParams, type EmbedPlayerParams,
@@ -122,7 +122,7 @@ export function ExternalPlayer({
link, link,
params, params,
}: { }: {
link: AppBskyEmbedExternal.ViewExternal link: app.bsky.embed.external.ViewExternal
params: EmbedPlayerParams params: EmbedPlayerParams
}) { }) {
const t = useTheme() const t = useTheme()
@@ -1,10 +1,10 @@
import {useMemo} from 'react' import {useMemo} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {type AppBskyEmbedExternal} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {app} from '#/lexicons'
import {parseAltFromGIFDescription} from '#/lib/gif-alt-text' import {parseAltFromGIFDescription} from '#/lib/gif-alt-text'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
@@ -30,7 +30,7 @@ export const ExternalEmbed = ({
style, style,
hideAlt, hideAlt,
}: { }: {
link: AppBskyEmbedExternal.ViewExternal link: app.bsky.embed.external.ViewExternal
onOpen?: () => void onOpen?: () => void
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
hideAlt?: boolean hideAlt?: boolean
+11 -8
View File
@@ -2,8 +2,9 @@ import {useRef} from 'react'
import {InteractionManager, View} from 'react-native' import {InteractionManager, View} from 'react-native'
import {type AnimatedRef} from 'react-native-reanimated' import {type AnimatedRef} from 'react-native-reanimated'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {AppBskyEmbedGallery, type AppBskyEmbedImages} from '@atproto/api'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {atoms as a, tokens} from '#/alf' import {atoms as a, tokens} from '#/alf'
import {AutoSizedImage} from '#/components/images/AutoSizedImage' import {AutoSizedImage} from '#/components/images/AutoSizedImage'
import {Gallery} from '#/components/images/Gallery' import {Gallery} from '#/components/images/Gallery'
@@ -29,14 +30,16 @@ export function ImageEmbed({
}) { }) {
const ax = useAnalytics() const ax = useAnalytics()
const {openLightbox} = useLightboxControls() const {openLightbox} = useLightboxControls()
const images: AppBskyEmbedImages.ViewImage[] = const images: app.bsky.embed.images.ViewImage[] =
embed.type === 'gallery' embed.type === 'gallery'
? embed.view.items.filter(AppBskyEmbedGallery.isViewImage).map(item => ({ ? embed.view.items
thumb: item.thumbnail, .filter(item => bsky.isType(app.bsky.embed.gallery.viewImage, item))
fullsize: item.fullsize, .map(item => ({
alt: item.alt, thumb: item.thumbnail,
aspectRatio: item.aspectRatio, fullsize: item.fullsize,
})) alt: item.alt,
aspectRatio: item.aspectRatio,
}))
: embed.view.images : embed.view.images
const useExpandedLayout = const useExpandedLayout =
embed.type === 'gallery' embed.type === 'gallery'
@@ -1,8 +1,8 @@
import {Fragment, type ReactNode} from 'react' import {Fragment, type ReactNode} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {AtUri} from '@atproto/api'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {AtUri} from '@atproto/syntax'
import {toNiceDomain} from '#/lib/strings/url-helpers' import {toNiceDomain} from '#/lib/strings/url-helpers'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import { import {
@@ -1,5 +1,4 @@
import {type AppBskyEmbedExternal} from '@atproto/api' import {app} from '#/lexicons'
import {Context, useAlf, utils} from '#/alf' import {Context, useAlf, utils} from '#/alf'
/** /**
@@ -12,7 +11,7 @@ export function StandardSiteThemeProvider({
view, view,
children, children,
}: { }: {
view: AppBskyEmbedExternal.ViewExternal view: app.bsky.embed.external.ViewExternal
children: React.ReactNode children: React.ReactNode
}) { }) {
const alf = useAlf() const alf = useAlf()
@@ -1,9 +1,9 @@
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {AtUri} from '@atproto/api'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {AtUri} from '@atproto/syntax'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {niceDate} from '#/lib/strings/time' import {niceDate} from '#/lib/strings/time'
@@ -1,5 +1,4 @@
import {type AppBskyEmbedExternal} from '@atproto/api' import {app} from '#/lexicons'
import {Leaflet} from '#/components/icons/community/Leaflet' import {Leaflet} from '#/components/icons/community/Leaflet'
import {Offprint} from '#/components/icons/community/Offprint' import {Offprint} from '#/components/icons/community/Offprint'
import {Pckt} from '#/components/icons/community/Pckt' import {Pckt} from '#/components/icons/community/Pckt'
@@ -25,7 +24,7 @@ function hostFromUri(uri: string | undefined): string | null {
} }
export function getStandardSitePublisherHost( export function getStandardSitePublisherHost(
view: AppBskyEmbedExternal.ViewExternal, view: app.bsky.embed.external.ViewExternal,
): string | null { ): string | null {
return hostFromUri(view.source?.uri) return hostFromUri(view.source?.uri)
} }
@@ -40,7 +39,7 @@ function matchByHost(host: string | null): StandardSitePublisher | null {
} }
export function matchStandardSitePublisher( export function matchStandardSitePublisher(
view: AppBskyEmbedExternal.ViewExternal, view: app.bsky.embed.external.ViewExternal,
): StandardSitePublisher | null { ): StandardSitePublisher | null {
return matchByHost(getStandardSitePublisherHost(view)) return matchByHost(getStandardSitePublisherHost(view))
} }
@@ -1,7 +1,7 @@
import {type AppBskyEmbedExternal} from '@atproto/api' import {app} from '#/lexicons'
export type CommonProps = { export type CommonProps = {
view: AppBskyEmbedExternal.ViewExternal view: app.bsky.embed.external.ViewExternal
} }
export type PreviewProps = { export type PreviewProps = {
@@ -1,10 +1,9 @@
import {type AppBskyEmbedExternal} from '@atproto/api'
import {isStandardSiteEmbed, isStandardSitePublicationEmbed} from './utils' import {isStandardSiteEmbed, isStandardSitePublicationEmbed} from './utils'
import {app} from '#/lexicons'
function makeView( function makeView(
partial: Record<string, unknown>, partial: Record<string, unknown>,
): AppBskyEmbedExternal.ViewExternal { ): app.bsky.embed.external.ViewExternal {
return { return {
uri: 'https://example.com/post', uri: 'https://example.com/post',
title: 'title', title: 'title',
@@ -1,29 +1,30 @@
import { import {AtUri} from '@atproto/syntax'
type AppBskyEmbedExternal, import {app, com} from '#/lexicons'
AtUri,
type ComAtprotoRepoStrongRef,
} from '@atproto/api'
export function isStandardSiteDocumentUri(ref: ComAtprotoRepoStrongRef.Main) { export function isStandardSiteDocumentUri(
ref: com.atproto.repo.strongRef.Main,
) {
return new AtUri(ref.uri).collection.startsWith('site.standard.document') return new AtUri(ref.uri).collection.startsWith('site.standard.document')
} }
export function isStandardSitePublicationUri( export function isStandardSitePublicationUri(
ref: ComAtprotoRepoStrongRef.Main, ref: com.atproto.repo.strongRef.Main,
) { ) {
return new AtUri(ref.uri).collection.startsWith('site.standard.publication') return new AtUri(ref.uri).collection.startsWith('site.standard.publication')
} }
export function isStandardSiteUri(ref: ComAtprotoRepoStrongRef.Main) { export function isStandardSiteUri(ref: com.atproto.repo.strongRef.Main) {
return new AtUri(ref.uri).collection.startsWith('site.standard.') return new AtUri(ref.uri).collection.startsWith('site.standard.')
} }
export function isStandardSiteEmbed(view: AppBskyEmbedExternal.ViewExternal) { export function isStandardSiteEmbed(
view: app.bsky.embed.external.ViewExternal,
) {
return view.associatedRefs?.some(ref => isStandardSiteUri(ref)) return view.associatedRefs?.some(ref => isStandardSiteUri(ref))
} }
export function isStandardSitePublicationEmbed( export function isStandardSitePublicationEmbed(
view: AppBskyEmbedExternal.ViewExternal, view: app.bsky.embed.external.ViewExternal,
) { ) {
return ( return (
view.associatedRefs?.some( view.associatedRefs?.some(
@@ -1,9 +1,9 @@
import {useImperativeHandle, useRef, useState} from 'react' import {useImperativeHandle, useRef, useState} from 'react'
import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native' import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
import {type AppBskyEmbedVideo} from '@atproto/api'
import {BlueskyVideoView} from '@bsky.app/video' import {BlueskyVideoView} from '@bsky.app/video'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {HITSLOP_30} from '#/lib/constants' import {HITSLOP_30} from '#/lib/constants'
import {useAutoplayDisabled} from '#/state/preferences' import {useAutoplayDisabled} from '#/state/preferences'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -29,7 +29,7 @@ export function VideoEmbedInnerNative({
onError, onError,
}: { }: {
ref: React.Ref<{togglePlayback: () => void}> ref: React.Ref<{togglePlayback: () => void}>
embed: AppBskyEmbedVideo.View embed: app.bsky.embed.video.View
setStatus: (status: 'playing' | 'paused') => void setStatus: (status: 'playing' | 'paused') => void
setIsLoading: (isLoading: boolean) => void setIsLoading: (isLoading: boolean) => void
setIsActive: (isActive: boolean) => void setIsActive: (isActive: boolean) => void
@@ -1,7 +1,7 @@
import {type AppBskyEmbedVideo} from '@atproto/api' import {app} from '#/lexicons'
export type VideoEmbedInnerWebProps = { export type VideoEmbedInnerWebProps = {
embed: AppBskyEmbedVideo.View embed: app.bsky.embed.video.View
active: boolean active: boolean
setActive: () => void setActive: () => void
onScreen: boolean onScreen: boolean
@@ -1,7 +1,6 @@
import {useCallback, useEffect, useRef, useState} from 'react' import {useCallback, useEffect, useRef, useState} from 'react'
import {ActivityIndicator, View} from 'react-native' import {ActivityIndicator, View} from 'react-native'
import {ImageBackground} from 'expo-image' import {ImageBackground} from 'expo-image'
import {type AppBskyEmbedVideo} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
@@ -10,6 +9,7 @@ import {
createPlaybackTelemetry, createPlaybackTelemetry,
type PlaybackTelemetry, type PlaybackTelemetry,
} from '#/lib/media/video/playbackTelemetry' } from '#/lib/media/video/playbackTelemetry'
import {app} from '#/lexicons'
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
import {atoms as a, platform} from '#/alf' import {atoms as a, platform} from '#/alf'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
@@ -22,7 +22,7 @@ import {VideoEmbedInnerNative} from './VideoEmbedInner/VideoEmbedInnerNative'
import * as VideoFallback from './VideoEmbedInner/VideoFallback' import * as VideoFallback from './VideoEmbedInner/VideoFallback'
interface Props { interface Props {
embed: AppBskyEmbedVideo.View embed: app.bsky.embed.video.View
} }
export function VideoEmbed({embed}: Props) { export function VideoEmbed({embed}: Props) {
@@ -7,10 +7,10 @@ import {
useState, useState,
} from 'react' } from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyEmbedVideo} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {app} from '#/lexicons'
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {useIsWithinMessage} from '#/components/dms/MessageContext' import {useIsWithinMessage} from '#/components/dms/MessageContext'
@@ -37,7 +37,7 @@ const noop = () => {}
*/ */
const MIN_CARD_WIDTH = 280 const MIN_CARD_WIDTH = 280
export function VideoEmbed({embed}: {embed: AppBskyEmbedVideo.View}) { export function VideoEmbed({embed}: {embed: app.bsky.embed.video.View}) {
const t = useTheme() const t = useTheme()
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const { const {
@@ -297,7 +297,7 @@ function VideoError({
error, error,
retry, retry,
}: { }: {
embed: AppBskyEmbedVideo.View embed: app.bsky.embed.video.View
error: unknown error: unknown
retry: () => void retry: () => void
}) { }) {
+5 -14
View File
@@ -1,15 +1,12 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {
type $Typed,
type AppBskyFeedDefs,
AppBskyFeedPost,
AtUri,
} from '@atproto/api'
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {type $Typed} from '@atproto/lex'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {moderatePost} from '#/lib/moderation/subjects' import {moderatePost} from '#/lib/moderation/subjects'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {asSdkFacets} from '#/lib/strings/rich-text-helpers' import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
@@ -267,7 +264,7 @@ export function QuoteEmbed({
linkDisabled?: boolean linkDisabled?: boolean
}) { }) {
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const quote = useMemo<$Typed<AppBskyFeedDefs.PostView>>( const quote = useMemo<$Typed<app.bsky.feed.defs.PostView>>(
() => ({ () => ({
...embed.view, ...embed.view,
$type: 'app.bsky.feed.defs#postView', $type: 'app.bsky.feed.defs#postView',
@@ -287,13 +284,7 @@ export function QuoteEmbed({
const itemTitle = `Post by ${quote.author.handle}` const itemTitle = `Post by ${quote.author.handle}`
const richText = useMemo(() => { const richText = useMemo(() => {
if ( if (!bsky.isType(app.bsky.feed.post, quote.record)) return undefined
!bsky.dangerousIsType<AppBskyFeedPost.Record>(
quote.record,
AppBskyFeedPost.isRecord,
)
)
return undefined
const {text, facets} = quote.record const {text, facets} = quote.record
return text.trim() return text.trim()
? new RichTextAPI({text: text, facets: asSdkFacets(facets)}) ? new RichTextAPI({text: text, facets: asSdkFacets(facets)})
+3 -3
View File
@@ -1,6 +1,6 @@
import {type StyleProp, type ViewStyle} from 'react-native' import {type StyleProp, type ViewStyle} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} from '@bsky.app/sdk/moderation'
import {app} from '#/lexicons'
export enum PostEmbedViewContext { export enum PostEmbedViewContext {
ThreadHighlighted = 'ThreadHighlighted', ThreadHighlighted = 'ThreadHighlighted',
@@ -20,10 +20,10 @@ export type CommonProps = {
* events (post:photoEmbed:*). When the embed has no owning post (e.g. * events (post:photoEmbed:*). When the embed has no owning post (e.g.
* composer previews), leave this undefined and no events will be emitted. * composer previews), leave this undefined and no events will be emitted.
*/ */
post?: AppBskyFeedDefs.PostView post?: app.bsky.feed.defs.PostView
feedDescriptor?: string feedDescriptor?: string
} }
export type EmbedProps = CommonProps & { export type EmbedProps = CommonProps & {
embed?: AppBskyFeedDefs.PostView['embed'] embed?: app.bsky.feed.defs.PostView['embed']
} }
+4 -7
View File
@@ -1,8 +1,8 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import {Platform, type StyleProp, type TextStyle, View} from 'react-native' import {Platform, type StyleProp, type TextStyle, View} from 'react-native'
import {type AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {HITSLOP_30} from '#/lib/constants' import {HITSLOP_30} from '#/lib/constants'
import {useTranslate} from '#/lib/translation' import {useTranslate} from '#/lib/translation'
import { import {
@@ -38,7 +38,7 @@ export function TranslatedPost({
postTextStyle = a.text_md, postTextStyle = a.text_md,
}: { }: {
hideTranslateLink?: boolean hideTranslateLink?: boolean
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
postTextStyle?: StyleProp<TextStyle> postTextStyle?: StyleProp<TextStyle>
}) { }) {
const langPrefs = useLanguagePrefs() const langPrefs = useLanguagePrefs()
@@ -46,11 +46,8 @@ export function TranslatedPost({
key: post.uri, key: post.uri,
}) })
const record = useMemo<AppBskyFeedPost.Record | undefined>(() => { const record = useMemo<app.bsky.feed.post.Main | undefined>(() => {
return bsky.dangerousIsType<AppBskyFeedPost.Record>( return bsky.isType(app.bsky.feed.post, post.record)
post.record,
AppBskyFeedPost.isRecord,
)
? post.record ? post.record
: undefined : undefined
}, [post]) }, [post])
@@ -1,10 +1,10 @@
import {memo} from 'react' import {memo} from 'react'
import {type Insets} from 'react-native' import {type Insets} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {useCleanError} from '#/lib/hooks/useCleanError' import {useCleanError} from '#/lib/hooks/useCleanError'
import {type Shadow} from '#/state/cache/post-shadow' import {type Shadow} from '#/state/cache/post-shadow'
import {useFeedFeedbackContext} from '#/state/feed-feedback' import {useFeedFeedbackContext} from '#/state/feed-feedback'
@@ -23,7 +23,7 @@ export const BookmarkButton = memo(function BookmarkButton({
logContext, logContext,
hitSlop, hitSlop,
}: { }: {
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
big?: boolean big?: boolean
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
hitSlop?: Insets hitSlop?: Insets
@@ -6,17 +6,13 @@ import {
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import * as Clipboard from 'expo-clipboard' import * as Clipboard from 'expo-clipboard'
import {
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyFeedThreadgate,
AtUri,
} from '@atproto/api'
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {DISCOVER_DEBUG_DIDS} from '#/lib/constants' import {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
import {useOpenLink} from '#/lib/hooks/useOpenLink' import {useOpenLink} from '#/lib/hooks/useOpenLink'
import {getCurrentRoute} from '#/lib/routes/helpers' import {getCurrentRoute} from '#/lib/routes/helpers'
@@ -110,17 +106,17 @@ let PostMenuItems = ({
forceGoogleTranslate, forceGoogleTranslate,
}: { }: {
testID: string testID: string
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
postFeedContext: string | undefined postFeedContext: string | undefined
postReqId: string | undefined postReqId: string | undefined
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
richText: RichTextAPI richText: RichTextAPI
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
hitSlop?: PressableProps['hitSlop'] hitSlop?: PressableProps['hitSlop']
size?: 'lg' | 'md' | 'sm' size?: 'lg' | 'md' | 'sm'
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: app.bsky.feed.threadgate.Main
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
forceGoogleTranslate: boolean forceGoogleTranslate: boolean
}): React.ReactNode => { }): React.ReactNode => {
@@ -1,13 +1,9 @@
import {memo, useMemo, useState} from 'react' import {memo, useMemo, useState} from 'react'
import {type Insets} from 'react-native' import {type Insets} from 'react-native'
import {
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyFeedThreadgate,
} from '@atproto/api'
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {type Shadow} from '#/state/cache/post-shadow' import {type Shadow} from '#/state/cache/post-shadow'
import {EventStopper} from '#/view/com/util/EventStopper' import {EventStopper} from '#/view/com/util/EventStopper'
import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid' import {DotGrid3x1_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
@@ -32,15 +28,15 @@ let PostMenuButton = ({
forceGoogleTranslate, forceGoogleTranslate,
}: { }: {
testID: string testID: string
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
postFeedContext: string | undefined postFeedContext: string | undefined
postReqId: string | undefined postReqId: string | undefined
big?: boolean big?: boolean
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
richText: RichTextAPI richText: RichTextAPI
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: app.bsky.feed.threadgate.Main
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
hitSlop?: Insets hitSlop?: Insets
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
forceGoogleTranslate: boolean forceGoogleTranslate: boolean
@@ -1,11 +1,11 @@
import {ScrollView, View} from 'react-native' import {ScrollView, View} from 'react-native'
import {type ChatBskyActorDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {chat} from '#/lexicons'
import {isBlockedOrBlocking, isMuted} from '#/lib/moderation/blocked-and-muted' import {isBlockedOrBlocking, isMuted} from '#/lib/moderation/blocked-and-muted'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
@@ -114,7 +114,7 @@ function RecentChatItem({
onPress: () => void onPress: () => void
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
convo: ConvoWithDetails convo: ConvoWithDetails
primaryMember: ChatBskyActorDefs.ProfileViewBasic primaryMember: chat.bsky.actor.defs.ProfileViewBasic
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
@@ -1,10 +1,10 @@
import {memo, useMemo} from 'react' import {memo, useMemo} from 'react'
import * as ExpoClipboard from 'expo-clipboard' import * as ExpoClipboard from 'expo-clipboard'
import {AtUri} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing' import {shareText, shareUrl} from '#/lib/sharing'
@@ -1,22 +1,18 @@
import {type PressableProps, type StyleProp, type ViewStyle} from 'react-native' import {type PressableProps, type StyleProp, type ViewStyle} from 'react-native'
import {
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyFeedThreadgate,
} from '@atproto/api'
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {app} from '#/lexicons'
import {type Shadow} from '#/state/cache/post-shadow' import {type Shadow} from '#/state/cache/post-shadow'
export interface ShareMenuItemsProps { export interface ShareMenuItemsProps {
testID: string testID: string
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
richText: RichTextAPI richText: RichTextAPI
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
hitSlop?: PressableProps['hitSlop'] hitSlop?: PressableProps['hitSlop']
size?: 'lg' | 'md' | 'sm' size?: 'lg' | 'md' | 'sm'
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: app.bsky.feed.threadgate.Main
onShare: () => void onShare: () => void
} }
@@ -1,8 +1,8 @@
import {memo, useMemo} from 'react' import {memo, useMemo} from 'react'
import {AtUri} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {AtUri} from '@atproto/syntax'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {shareText, shareUrl} from '#/lib/sharing' import {shareText, shareUrl} from '#/lib/sharing'
@@ -1,15 +1,11 @@
import {memo, useMemo, useState} from 'react' import {memo, useMemo, useState} from 'react'
import {type Insets} from 'react-native' import {type Insets} from 'react-native'
import {
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyFeedThreadgate,
AtUri,
} from '@atproto/api'
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {toShareUrl} from '#/lib/strings/url-helpers' import {toShareUrl} from '#/lib/strings/url-helpers'
@@ -37,12 +33,12 @@ let ShareMenuButton = ({
logContext, logContext,
}: { }: {
testID: string testID: string
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
big?: boolean big?: boolean
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
richText: RichTextAPI richText: RichTextAPI
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: app.bsky.feed.threadgate.Main
onShare: () => void onShare: () => void
hitSlop?: Insets hitSlop?: Insets
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
+5 -9
View File
@@ -1,14 +1,10 @@
import {memo, useMemo, useState} from 'react' import {memo, useMemo, useState} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyFeedThreadgate,
} from '@atproto/api'
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {CountWheel} from '#/lib/custom-animations/CountWheel' import {CountWheel} from '#/lib/custom-animations/CountWheel'
import {AnimatedLikeIcon} from '#/lib/custom-animations/LikeIcon' import {AnimatedLikeIcon} from '#/lib/custom-animations/LikeIcon'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
@@ -57,8 +53,8 @@ let PostControls = ({
forceGoogleTranslate = false, forceGoogleTranslate = false,
}: { }: {
big?: boolean big?: boolean
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
richText: RichTextAPI richText: RichTextAPI
feedContext?: string | undefined feedContext?: string | undefined
reqId?: string | undefined reqId?: string | undefined
@@ -66,8 +62,8 @@ let PostControls = ({
onPressReply: () => void onPressReply: () => void
onPostReply?: (postUri: string | undefined) => void onPostReply?: (postUri: string | undefined) => void
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: app.bsky.feed.threadgate.Main
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
viaRepost?: {uri: string; cid: string} viaRepost?: {uri: string; cid: string}
variant?: 'compact' | 'normal' | 'large' variant?: 'compact' | 'normal' | 'large'
forceGoogleTranslate?: boolean forceGoogleTranslate?: boolean
@@ -1,12 +1,12 @@
import {memo, useCallback, useEffect, useMemo, useReducer, useRef} from 'react' import {memo, useCallback, useEffect, useMemo, useReducer, useRef} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom' import {flip, offset, shift, size, useFloating} from '@floating-ui/react-dom'
import {msg, plural} from '@lingui/core/macro' import {msg, plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {getModerationCauseKey} from '#/lib/moderation' import {getModerationCauseKey} from '#/lib/moderation'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
@@ -416,7 +416,7 @@ function Inner({
moderationOpts, moderationOpts,
hide, hide,
}: { }: {
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
hide: () => void hide: () => void
}) { }) {
@@ -1,7 +1,7 @@
import {forwardRef, useCallback, useImperativeHandle, useState} from 'react' import {forwardRef, useCallback, useImperativeHandle, useState} from 'react'
import {type ListRenderItemInfo, View} from 'react-native' import {type ListRenderItemInfo, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {app} from '#/lexicons'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {List, type ListRef} from '#/view/com/util/List' import {List, type ListRef} from '#/view/com/util/List'
import {type SectionRef} from '#/screens/Profile/Sections/types' import {type SectionRef} from '#/screens/Profile/Sections/types'
@@ -9,12 +9,12 @@ import {atoms as a, useTheme} from '#/alf'
import * as FeedCard from '#/components/FeedCard' import * as FeedCard from '#/components/FeedCard'
import {IS_NATIVE, IS_WEB} from '#/env' import {IS_NATIVE, IS_WEB} from '#/env'
function keyExtractor(item: AppBskyFeedDefs.GeneratorView) { function keyExtractor(item: app.bsky.feed.defs.GeneratorView) {
return item.uri return item.uri
} }
interface ProfilesListProps { interface ProfilesListProps {
feeds: AppBskyFeedDefs.GeneratorView[] feeds: app.bsky.feed.defs.GeneratorView[]
headerHeight: number headerHeight: number
scrollElRef: ListRef scrollElRef: ListRef
} }
@@ -39,7 +39,7 @@ export const FeedsList = forwardRef<SectionRef, ProfilesListProps>(
const renderItem = ({ const renderItem = ({
item, item,
index, index,
}: ListRenderItemInfo<AppBskyFeedDefs.GeneratorView>) => { }: ListRenderItemInfo<app.bsky.feed.defs.GeneratorView>) => {
return ( return (
<View <View
style={[ style={[
@@ -1,16 +1,13 @@
import {forwardRef, useCallback, useImperativeHandle, useState} from 'react' import {forwardRef, useCallback, useImperativeHandle, useState} from 'react'
import {type ListRenderItemInfo, View} from 'react-native' import {type ListRenderItemInfo, View} from 'react-native'
import {
type AppBskyActorDefs,
type AppBskyGraphGetList,
AtUri,
} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import { import {
type InfiniteData, type InfiniteData,
type UseInfiniteQueryResult, type UseInfiniteQueryResult,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted' import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
@@ -23,14 +20,14 @@ import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {Default as ProfileCard} from '#/components/ProfileCard' import {Default as ProfileCard} from '#/components/ProfileCard'
import {IS_NATIVE, IS_WEB} from '#/env' import {IS_NATIVE, IS_WEB} from '#/env'
function keyExtractor(item: AppBskyActorDefs.ProfileView, index: number) { function keyExtractor(item: app.bsky.actor.defs.ProfileView, index: number) {
return `${item.did}-${index}` return `${item.did}-${index}`
} }
interface ProfilesListProps { interface ProfilesListProps {
listUri: string listUri: string
listMembersQuery: UseInfiniteQueryResult< listMembersQuery: UseInfiniteQueryResult<
InfiniteData<AppBskyGraphGetList.OutputSchema> InfiniteData<app.bsky.graph.getList.$OutputBody>
> >
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
headerHeight: number headerHeight: number
@@ -87,7 +84,7 @@ export const ProfilesList = forwardRef<SectionRef, ProfilesListProps>(
const renderItem = ({ const renderItem = ({
item, item,
index, index,
}: ListRenderItemInfo<AppBskyActorDefs.ProfileView>) => { }: ListRenderItemInfo<app.bsky.actor.defs.ProfileView>) => {
return ( return (
<View <View
style={[ style={[
@@ -6,12 +6,12 @@ import {
View, View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type AppBskyGraphDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
@@ -57,7 +57,7 @@ interface ProfileFeedgensProps {
emptyStateIcon?: React.ComponentType<any> | React.ReactElement emptyStateIcon?: React.ComponentType<any> | React.ReactElement
} }
function keyExtractor(item: AppBskyGraphDefs.StarterPackViewBasic) { function keyExtractor(item: app.bsky.graph.defs.StarterPackViewBasic) {
return item.uri return item.uri
} }
@@ -147,7 +147,7 @@ export function ProfileStarterPacks({
({ ({
item, item,
index, index,
}: ListRenderItemInfo<AppBskyGraphDefs.StarterPackViewBasic>) => { }: ListRenderItemInfo<app.bsky.graph.defs.StarterPackViewBasic>) => {
return ( return (
<View <View
style={[ style={[
+3 -8
View File
@@ -3,9 +3,9 @@ import {View} from 'react-native'
// @ts-expect-error missing types // @ts-expect-error missing types
import QRCode from 'react-native-qrcode-styled' import QRCode from 'react-native-qrcode-styled'
import type ViewShot from 'react-native-view-shot' import type ViewShot from 'react-native-view-shot'
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {Logo} from '#/view/icons/Logo' import {Logo} from '#/view/icons/Logo'
import {Logotype} from '#/view/icons/Logotype' import {Logotype} from '#/view/icons/Logotype'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -24,18 +24,13 @@ export function QrCode({
link, link,
ref, ref,
}: { }: {
starterPack: AppBskyGraphDefs.StarterPackView starterPack: app.bsky.graph.defs.StarterPackView
link: string link: string
ref: React.Ref<ViewShot> ref: React.Ref<ViewShot>
}) { }) {
const {record} = starterPack const {record} = starterPack
if ( if (!bsky.isType(app.bsky.graph.starterpack, record)) {
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
record,
AppBskyGraphStarterpack.isRecord,
)
) {
return null return null
} }
+3 -8
View File
@@ -3,11 +3,11 @@ import {View} from 'react-native'
import type ViewShot from 'react-native-view-shot' import type ViewShot from 'react-native-view-shot'
import {requestPermissionsAsync, saveToLibraryAsync} from 'expo-media-library' import {requestPermissionsAsync, saveToLibraryAsync} from 'expo-media-library'
import * as Sharing from 'expo-sharing' import * as Sharing from 'expo-sharing'
import {type AppBskyGraphDefs, AppBskyGraphStarterpack} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {logger} from '#/logger' import {logger} from '#/logger'
import {atoms as a, useBreakpoints} from '#/alf' import {atoms as a, useBreakpoints} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {Button, ButtonIcon, ButtonText} from '#/components/Button'
@@ -28,7 +28,7 @@ export function QrCodeDialog({
link, link,
control, control,
}: { }: {
starterPack: AppBskyGraphDefs.StarterPackView starterPack: app.bsky.graph.defs.StarterPackView
link?: string link?: string
control: DialogControlProps control: DialogControlProps
}) { }) {
@@ -87,12 +87,7 @@ export function QrCodeDialog({
} else { } else {
setIsSaveProcessing(true) setIsSaveProcessing(true)
if ( if (!bsky.matches(app.bsky.graph.starterpack, starterPack.record)) {
!bsky.validate(
starterPack.record,
AppBskyGraphStarterpack.validateRecord,
)
) {
return return
} }
+2 -2
View File
@@ -1,10 +1,10 @@
import {View} from 'react-native' import {View} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {type AppBskyGraphDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {useSaveImageToMediaLibrary} from '#/lib/media/save-image' import {useSaveImageToMediaLibrary} from '#/lib/media/save-image'
import {shareUrl} from '#/lib/sharing' import {shareUrl} from '#/lib/sharing'
import {getStarterPackOgCard} from '#/lib/strings/starter-pack' import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
@@ -21,7 +21,7 @@ import {useAnalytics} from '#/analytics'
import {IS_NATIVE, IS_WEB} from '#/env' import {IS_NATIVE, IS_WEB} from '#/env'
interface Props { interface Props {
starterPack: AppBskyGraphDefs.StarterPackView starterPack: app.bsky.graph.defs.StarterPackView
link?: string link?: string
imageLoaded?: boolean imageLoaded?: boolean
qrDialogControl: DialogControlProps qrDialogControl: DialogControlProps
+5 -17
View File
@@ -1,12 +1,13 @@
import {useMemo} from 'react' import {useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {AppBskyGraphStarterpack, AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Plural, Trans} from '@lingui/react/macro' import {Plural, Trans} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {getStarterPackOgCard} from '#/lib/strings/starter-pack' import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
import {precacheResolvedUri} from '#/state/queries/resolve-uri' import {precacheResolvedUri} from '#/state/queries/resolve-uri'
@@ -62,12 +63,7 @@ export function Card({
const t = useTheme() const t = useTheme()
const {currentAccount} = useSession() const {currentAccount} = useSession()
if ( if (!bsky.isType(app.bsky.graph.starterpack, record)) {
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
record,
AppBskyGraphStarterpack.isRecord,
)
) {
return null return null
} }
@@ -127,10 +123,7 @@ export function useStarterPackLink({
return { return {
to: `/starter-pack/${handleOrDid}/${rkey}`, to: `/starter-pack/${handleOrDid}/${rkey}`,
label: bsky.dangerousIsType<AppBskyGraphStarterpack.Record>( label: bsky.isType(app.bsky.graph.starterpack, view.record)
view.record,
AppBskyGraphStarterpack.isRecord,
)
? _(msg`Navigate to ${view.record.name}`) ? _(msg`Navigate to ${view.record.name}`)
: _(msg`Navigate to starter pack`), : _(msg`Navigate to starter pack`),
precache, precache,
@@ -154,12 +147,7 @@ export function Link({
return {rkey, handleOrDid: creator.handle || creator.did} return {rkey, handleOrDid: creator.handle || creator.did}
}, [starterPack]) }, [starterPack])
if ( if (!bsky.isType(app.bsky.graph.starterpack, record)) {
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
record,
AppBskyGraphStarterpack.isRecord,
)
) {
return null return null
} }
@@ -1,12 +1,12 @@
import {useRef} from 'react' import {useRef} from 'react'
import {type ListRenderItemInfo} from 'react-native' import {type ListRenderItemInfo} from 'react-native'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs, type AppBskyFeedDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {type ListMethods} from '#/view/com/util/List' import {type ListMethods} from '#/view/com/util/List'
import { import {
@@ -24,7 +24,7 @@ import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env' import {IS_WEB} from '#/env'
function keyExtractor( function keyExtractor(
item: AppBskyActorDefs.ProfileViewBasic | AppBskyFeedDefs.GeneratorView, item: app.bsky.actor.defs.ProfileViewBasic | app.bsky.feed.defs.GeneratorView,
index: number, index: number,
) { ) {
return `${item.did}-${index}` return `${item.did}-${index}`
@@ -41,7 +41,7 @@ export function WizardEditListDialog({
state: WizardState state: WizardState
dispatch: (action: WizardAction) => void dispatch: (action: WizardAction) => void
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
@@ -1,10 +1,10 @@
import {Keyboard, View} from 'react-native' import {Keyboard, View} from 'react-native'
import {type AppBskyActorDefs, type AppBskyFeedDefs} from '@atproto/api'
import {type ModerationOpts, type ModerationUI} from '@bsky.app/sdk/moderation' import {type ModerationOpts, type ModerationUI} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {DISCOVER_FEED_URI, STARTER_PACK_MAX_SIZE} from '#/lib/constants' import {DISCOVER_FEED_URI, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
import {moderateFeedGenerator, moderateProfile} from '#/lib/moderation/subjects' import {moderateFeedGenerator, moderateProfile} from '#/lib/moderation/subjects'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
@@ -36,8 +36,8 @@ function WizardListCard({
}: { }: {
type: 'user' | 'algo' type: 'user' | 'algo'
btnType: 'checkbox' | 'remove' btnType: 'checkbox' | 'remove'
profile?: AppBskyActorDefs.ProfileViewBasic profile?: app.bsky.actor.defs.ProfileViewBasic
feed?: AppBskyFeedDefs.GeneratorView feed?: app.bsky.feed.defs.GeneratorView
displayName: string displayName: string
subtitle: string subtitle: string
onPress: () => void onPress: () => void
@@ -178,7 +178,7 @@ export function WizardFeedCard({
moderationOpts, moderationOpts,
}: { }: {
btnType: 'checkbox' | 'remove' btnType: 'checkbox' | 'remove'
generator: AppBskyFeedDefs.GeneratorView generator: app.bsky.feed.defs.GeneratorView
state: WizardState state: WizardState
dispatch: (action: WizardAction) => void dispatch: (action: WizardAction) => void
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
+4 -3
View File
@@ -1,7 +1,8 @@
import {useEffect, useMemo} from 'react' import {useEffect, useMemo} from 'react'
import {type AppBskyUnspeccedDefs, type AtUri} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {type AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {PressableScale} from '#/lib/custom-animations/PressableScale' import {PressableScale} from '#/lib/custom-animations/PressableScale'
import {useCallOnce} from '#/lib/once' import {useCallOnce} from '#/lib/once'
// import {makeProfileLink} from '#/lib/routes/links' // import {makeProfileLink} from '#/lib/routes/links'
@@ -18,7 +19,7 @@ export function TrendingTopicLink({
children, children,
...rest ...rest
}: { }: {
topic: AppBskyUnspeccedDefs.TrendView topic: app.bsky.unspecced.defs.TrendView
metricContext: Metrics['trendingTopic:seen']['context'] metricContext: Metrics['trendingTopic:seen']['context']
rank: number rank: number
recId?: string recId?: string
@@ -75,7 +76,7 @@ type ParsedTrendingTopic =
} }
export function useTopic( export function useTopic(
raw: AppBskyUnspeccedDefs.TrendView, raw: app.bsky.unspecced.defs.TrendView,
): ParsedTrendingTopic { ): ParsedTrendingTopic {
const {t: l} = useLingui() const {t: l} = useLingui()
return useMemo(() => { return useMemo(() => {
+7 -15
View File
@@ -2,15 +2,10 @@ import {useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {LinearGradient} from 'expo-linear-gradient' import {LinearGradient} from 'expo-linear-gradient'
import {
type AppBskyActorDefs,
AppBskyEmbedVideo,
type AppBskyFeedDefs,
AppBskyFeedPost,
} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} from '@bsky.app/sdk/moderation'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import {formatCount} from '#/view/com/util/numeric/format' import {formatCount} from '#/view/com/util/numeric/format'
import {UserAvatar} from '#/view/com/util/UserAvatar' import {UserAvatar} from '#/view/com/util/UserAvatar'
@@ -42,7 +37,7 @@ export function VideoPostCard({
moderation, moderation,
onInteract, onInteract,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
sourceContext: VideoFeedSourceContext sourceContext: VideoFeedSourceContext
moderation: ModerationDecision moderation: ModerationDecision
/** /**
@@ -75,13 +70,10 @@ export function VideoPostCard({
* Filtering should be done at a higher level, such as `PostFeed` or * Filtering should be done at a higher level, such as `PostFeed` or
* `PostFeedVideoGridRow`, but we need to protect here as well. * `PostFeedVideoGridRow`, but we need to protect here as well.
*/ */
if (!AppBskyEmbedVideo.isView(embed)) return null if (!bsky.isType(app.bsky.embed.video.view, embed)) return null
const author = post.author const author = post.author
const text = bsky.dangerousIsType<AppBskyFeedPost.Record>( const text = bsky.isType(app.bsky.feed.post, post.record)
post.record,
AppBskyFeedPost.isRecord,
)
? post.record?.text ? post.record?.text
: '' : ''
const likeCount = post?.likeCount ?? 0 const likeCount = post?.likeCount ?? 0
@@ -275,7 +267,7 @@ export function VideoPostCardPlaceholder() {
export function VideoPostCardTextPlaceholder({ export function VideoPostCardTextPlaceholder({
author, author,
}: { }: {
author?: AppBskyActorDefs.ProfileViewBasic author?: app.bsky.actor.defs.ProfileViewBasic
}) { }) {
const t = useTheme() const t = useTheme()
@@ -355,7 +347,7 @@ export function CompactVideoPostCard({
moderation, moderation,
onInteract, onInteract,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
sourceContext: VideoFeedSourceContext sourceContext: VideoFeedSourceContext
moderation: ModerationDecision moderation: ModerationDecision
/** /**
@@ -386,7 +378,7 @@ export function CompactVideoPostCard({
* Filtering should be done at a higher level, such as `PostFeed` or * Filtering should be done at a higher level, such as `PostFeed` or
* `PostFeedVideoGridRow`, but we need to protect here as well. * `PostFeedVideoGridRow`, but we need to protect here as well.
*/ */
if (!AppBskyEmbedVideo.isView(embed)) return null if (!bsky.isType(app.bsky.embed.video.view, embed)) return null
const likeCount = post?.likeCount ?? 0 const likeCount = post?.likeCount ?? 0
const showLikeCount = false const showLikeCount = false
+8 -15
View File
@@ -6,16 +6,12 @@ import {
View, View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {
type AppBskyFeedDefs,
AppBskyFeedPost,
type AppBskyGraphDefs,
AtUri,
} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {makeListLink, makeProfileLink} from '#/lib/routes/links' import {makeListLink, makeProfileLink} from '#/lib/routes/links'
import { import {
@@ -41,7 +37,7 @@ import {IS_NATIVE} from '#/env'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
interface WhoCanReplyProps { interface WhoCanReplyProps {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
isThreadAuthor: boolean isThreadAuthor: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
} }
@@ -58,10 +54,7 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
* unexpectedly, we should check to make sure it's for sure the root URI. * unexpectedly, we should check to make sure it's for sure the root URI.
*/ */
const rootUri = const rootUri =
bsky.dangerousIsType<AppBskyFeedPost.Record>( bsky.isType(app.bsky.feed.post, post.record) && post.record.reply?.root
post.record,
AppBskyFeedPost.isRecord,
) && post.record.reply?.root
? post.record.reply.root.uri ? post.record.reply.root.uri
: post.uri : post.uri
const settings = useMemo(() => { const settings = useMemo(() => {
@@ -209,7 +202,7 @@ function WhoCanReplyDialog({
embeddingDisabled, embeddingDisabled,
}: { }: {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
settings: ThreadgateAllowUISetting[] settings: ThreadgateAllowUISetting[]
embeddingDisabled: boolean embeddingDisabled: boolean
}) { }) {
@@ -255,7 +248,7 @@ function Rules({
settings, settings,
embeddingDisabled, embeddingDisabled,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
settings: ThreadgateAllowUISetting[] settings: ThreadgateAllowUISetting[]
embeddingDisabled: boolean embeddingDisabled: boolean
}) { }) {
@@ -313,8 +306,8 @@ function Rule({
lists, lists,
}: { }: {
rule: ThreadgateAllowUISetting rule: ThreadgateAllowUISetting
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
lists: AppBskyGraphDefs.ListViewBasic[] | undefined lists: app.bsky.graph.defs.ListViewBasic[] | undefined
}) { }) {
if (rule.type === 'mention') { if (rule.type === 'mention') {
return <Trans>mentioned users</Trans> return <Trans>mentioned users</Trans>
@@ -1,6 +1,5 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyNotificationDefs, type Un$Typed} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
@@ -12,6 +11,7 @@ import {
useQueryClient, useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {type Un$Typed} from '@atproto/lex'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
@@ -118,7 +118,7 @@ function DialogInner({
error, error,
} = useMutation({ } = useMutation({
mutationFn: async ( mutationFn: async (
activitySubscription: Un$Typed<AppBskyNotificationDefs.ActivitySubscription>, activitySubscription: Un$Typed<app.bsky.notification.defs.ActivitySubscription>,
) => { ) => {
await client.call(app.bsky.notification.putActivitySubscription, { await client.call(app.bsky.notification.putActivitySubscription, {
// the profile view carries an already-resolved did // the profile view carries an already-resolved did
@@ -307,8 +307,8 @@ function DialogInner({
} }
function parseActivitySubscription( function parseActivitySubscription(
sub?: AppBskyNotificationDefs.ActivitySubscription, sub?: app.bsky.notification.defs.ActivitySubscription,
): Un$Typed<AppBskyNotificationDefs.ActivitySubscription> { ): Un$Typed<app.bsky.notification.defs.ActivitySubscription> {
if (!sub) return {post: false, reply: false} if (!sub) return {post: false, reply: false}
const {post, reply} = sub const {post, reply} = sub
return {post, reply} return {post, reply}
@@ -1,6 +1,5 @@
import {useState} from 'react' import {useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {ToolsOzoneReportDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -18,7 +17,7 @@ import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {logger} from '#/ageAssurance' import {logger} from '#/ageAssurance'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {com} from '#/lexicons' import {com, tools} from '#/lexicons'
export function AgeAssuranceAppealDialog({ export function AgeAssuranceAppealDialog({
control, control,
@@ -60,7 +59,7 @@ function Inner({control}: {control: Dialog.DialogControlProps}) {
await client.call( await client.call(
com.atproto.moderation.createReport, com.atproto.moderation.createReport,
{ {
reasonType: ToolsOzoneReportDefs.REASONAPPEAL, reasonType: tools.ozone.report.defs.reasonAppeal,
subject: { subject: {
$type: 'com.atproto.admin.defs#repoRef', $type: 'com.atproto.admin.defs#repoRef',
// the persisted account did is already resolved // the persisted account did is already resolved
+3 -4
View File
@@ -1,5 +1,4 @@
import {type AppBskyContactDefs} from '@atproto/api' import {app} from '#/lexicons'
import {type CountryCode} from '#/lib/international-telephone-codes' import {type CountryCode} from '#/lib/international-telephone-codes'
import {normalizePhoneNumber} from './phone-number' import {normalizePhoneNumber} from './phone-number'
import {type Contact, type Match} from './state' import {type Contact, type Match} from './state'
@@ -70,7 +69,7 @@ export function normalizeContactBook(
export function filterMatchedNumbers( export function filterMatchedNumbers(
contacts: Contact[], contacts: Contact[],
results: AppBskyContactDefs.MatchAndContactIndex[], results: app.bsky.contact.defs.MatchAndContactIndex[],
mapping: Map<number, Contact['id']>, mapping: Map<number, Contact['id']>,
) { ) {
const filteredIds = new Set<Contact['id']>() const filteredIds = new Set<Contact['id']>()
@@ -87,7 +86,7 @@ export function filterMatchedNumbers(
export function getMatchedContacts( export function getMatchedContacts(
contacts: Contact[], contacts: Contact[],
results: AppBskyContactDefs.MatchAndContactIndex[], results: app.bsky.contact.defs.MatchAndContactIndex[],
mapping: Map<number, Contact['id']>, mapping: Map<number, Contact['id']>,
): Array<Match> { ): Array<Match> {
const contactsById = new Map(contacts.map(c => [c.id, c])) const contactsById = new Map(contacts.map(c => [c.id, c]))
+4 -3
View File
@@ -1,10 +1,11 @@
import {memo, useEffect, useMemo, useState} from 'react' import {memo, useEffect, useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs, type AppBskyFeedPost, AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {EMBED_SCRIPT} from '#/lib/constants' import {EMBED_SCRIPT} from '#/lib/constants'
import {niceDate} from '#/lib/strings/time' import {niceDate} from '#/lib/strings/time'
import {toShareUrl} from '#/lib/strings/url-helpers' import {toShareUrl} from '#/lib/strings/url-helpers'
@@ -25,10 +26,10 @@ export type ColorModeValues = 'system' | 'light' | 'dark'
type EmbedDialogProps = { type EmbedDialogProps = {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
postAuthor: AppBskyActorDefs.ProfileViewBasic postAuthor: app.bsky.actor.defs.ProfileViewBasic
postCid: string postCid: string
postUri: string postUri: string
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
timestamp: string timestamp: string
} }
@@ -1,6 +1,5 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {LayoutAnimation, Text as NestedText, View} from 'react-native' import {LayoutAnimation, Text as NestedText, View} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {AtUri} from '@atproto/syntax' import {AtUri} from '@atproto/syntax'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -50,7 +49,7 @@ import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
import {IS_IOS} from '#/env' import {IS_IOS} from '#/env'
import {type app} from '#/lexicons' import {app} from '#/lexicons'
export type PostInteractionSettingsFormProps = { export type PostInteractionSettingsFormProps = {
canSave?: boolean canSave?: boolean
@@ -133,10 +132,10 @@ export type PostInteractionSettingsDialogProps = {
*/ */
rootPostUri: string rootPostUri: string
/** /**
* Optional initial {@link AppBskyFeedDefs.ThreadgateView} to use if we * Optional initial {@link app.bsky.feed.defs.ThreadgateView} to use if we
* happen to have one before opening the settings dialog. * happen to have one before opening the settings dialog.
*/ */
initialThreadgateView?: AppBskyFeedDefs.ThreadgateView initialThreadgateView?: app.bsky.feed.defs.ThreadgateView
} }
/** /**
+3 -11
View File
@@ -1,14 +1,11 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {
type AppBskyGraphGetStarterPacksWithMembership,
AppBskyGraphStarterpack,
} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Plural, Trans} from '@lingui/react/macro' import {Plural, Trans} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
@@ -36,7 +33,7 @@ import {IS_WEB} from '#/env'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
type StarterPackWithMembership = type StarterPackWithMembership =
AppBskyGraphGetStarterPacksWithMembership.StarterPackWithMembership app.bsky.graph.getStarterPacksWithMembership.StarterPackWithMembership
export type StarterPackDialogProps = { export type StarterPackDialogProps = {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
@@ -324,12 +321,7 @@ function StarterPackItem({
const {record} = starterPack const {record} = starterPack
if ( if (!bsky.isType(app.bsky.graph.starterpack, record)) {
!bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
record,
AppBskyGraphStarterpack.isRecord,
)
) {
return null return null
} }
@@ -1,5 +1,4 @@
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyGraphDefs, type AppBskyGraphStarterpack} from '@atproto/api'
import {TID} from '@atproto/common-web' import {TID} from '@atproto/common-web'
import {type $Typed} from '@atproto/lex' import {type $Typed} from '@atproto/lex'
import { import {
@@ -37,7 +36,7 @@ export function CreateListFromStarterPackDialog({
starterPack, starterPack,
}: { }: {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
starterPack: AppBskyGraphDefs.StarterPackView starterPack: app.bsky.graph.defs.StarterPackView
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
@@ -50,7 +49,7 @@ export function CreateListFromStarterPackDialog({
const createDialogControl = Dialog.useDialogControl() const createDialogControl = Dialog.useDialogControl()
const loadingDialogControl = Dialog.useDialogControl() const loadingDialogControl = Dialog.useDialogControl()
const record = starterPack.record as AppBskyGraphStarterpack.Record const record = starterPack.record as app.bsky.graph.starterpack.Main
const onPressCreate = () => { const onPressCreate = () => {
control.close(() => createDialogControl.open()) control.close(() => createDialogControl.open())
@@ -1,11 +1,11 @@
import {useCallback, useEffect, useMemo, useState} from 'react' import {useCallback, useEffect, useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyGraphDefs} from '@atproto/api'
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Plural, Trans} from '@lingui/react/macro' import {Plural, Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {isOverMaxGraphemeCount} from '#/lib/strings/helpers' import {isOverMaxGraphemeCount} from '#/lib/strings/helpers'
import {asSdkFacets, richTextToString} from '#/lib/strings/rich-text-helpers' import {asSdkFacets, richTextToString} from '#/lib/strings/rich-text-helpers'
@@ -46,8 +46,8 @@ export function CreateOrEditListDialog({
initialValues, initialValues,
}: { }: {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
list?: AppBskyGraphDefs.ListView list?: app.bsky.graph.defs.ListView
purpose?: AppBskyGraphDefs.ListPurpose purpose?: app.bsky.graph.defs.ListPurpose
onSave?: (uri: string) => void onSave?: (uri: string) => void
initialValues?: InitialListValues initialValues?: InitialListValues
}) { }) {
@@ -114,8 +114,8 @@ function DialogInner({
onPressCancel, onPressCancel,
initialValues, initialValues,
}: { }: {
list?: AppBskyGraphDefs.ListView list?: app.bsky.graph.defs.ListView
purpose?: AppBskyGraphDefs.ListPurpose purpose?: app.bsky.graph.defs.ListPurpose
onSave?: (uri: string) => void onSave?: (uri: string) => void
setDirty: (dirty: boolean) => void setDirty: (dirty: boolean) => void
onPressCancel: () => void onPressCancel: () => void
@@ -1,11 +1,11 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyGraphDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useAllListMembersQuery} from '#/state/queries/list-members' import {useAllListMembersQuery} from '#/state/queries/list-members'
@@ -31,7 +31,7 @@ export function ListAddRemoveUsersDialog({
onChange, onChange,
}: { }: {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
list: AppBskyGraphDefs.ListView list: app.bsky.graph.defs.ListView
onChange?: ( onChange?: (
type: 'add' | 'remove', type: 'add' | 'remove',
profile: bsky.profile.AnyProfileView, profile: bsky.profile.AnyProfileView,
@@ -52,7 +52,7 @@ function DialogInner({
list, list,
onChange, onChange,
}: { }: {
list: AppBskyGraphDefs.ListView list: app.bsky.graph.defs.ListView
onChange?: ( onChange?: (
type: 'add' | 'remove', type: 'add' | 'remove',
profile: bsky.profile.AnyProfileView, profile: bsky.profile.AnyProfileView,
@@ -89,7 +89,7 @@ function DialogInner({
* Returns undefined for pending, false for not a member, and string for a member (the URI of the membership record) * Returns undefined for pending, false for not a member, and string for a member (the URI of the membership record)
*/ */
function getMembership( function getMembership(
listMembers: AppBskyGraphDefs.ListItemView[] | undefined, listMembers: app.bsky.graph.defs.ListItemView[] | undefined,
actorDid: string, actorDid: string,
): string | false | undefined { ): string | false | undefined {
if (!listMembers) { if (!listMembers) {
@@ -107,8 +107,8 @@ function UserResult({
moderationOpts, moderationOpts,
}: { }: {
profile: bsky.profile.AnyProfileView profile: bsky.profile.AnyProfileView
list: AppBskyGraphDefs.ListView list: app.bsky.graph.defs.ListView
listMembers: AppBskyGraphDefs.ListItemView[] | undefined listMembers: app.bsky.graph.defs.ListItemView[] | undefined
onChange?: ( onChange?: (
type: 'add' | 'remove', type: 'add' | 'remove',
profile: bsky.profile.AnyProfileView, profile: bsky.profile.AnyProfileView,
+2 -2
View File
@@ -6,8 +6,8 @@ import {
useMemo, useMemo,
useState, useState,
} from 'react' } from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {app} from '#/lexicons'
import {logger} from '#/logger' import {logger} from '#/logger'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs' import {Nux, useNuxs, useResetNuxs, useSaveNux} from '#/state/queries/nuxs'
@@ -93,7 +93,7 @@ function Inner({
preferences, preferences,
}: { }: {
currentAccount: SessionAccount currentAccount: SessionAccount
currentProfile: AppBskyActorDefs.ProfileViewDetailed currentProfile: app.bsky.actor.defs.ProfileViewDetailed
preferences: UsePreferencesQueryResponse preferences: UsePreferencesQueryResponse
}) { }) {
const ax = useAnalytics() const ax = useAnalytics()
+2 -3
View File
@@ -1,5 +1,4 @@
import {type AppBskyActorDefs} from '@atproto/api' import {app} from '#/lexicons'
import {type UsePreferencesQueryResponse} from '#/state/queries/preferences' import {type UsePreferencesQueryResponse} from '#/state/queries/preferences'
import {type SessionAccount} from '#/state/session' import {type SessionAccount} from '#/state/session'
import {type AnalyticsContextType} from '#/analytics' import {type AnalyticsContextType} from '#/analytics'
@@ -8,7 +7,7 @@ import {type Geolocation} from '#/geolocation'
export type EnabledCheckProps = { export type EnabledCheckProps = {
features: AnalyticsContextType['features'] features: AnalyticsContextType['features']
currentAccount: SessionAccount currentAccount: SessionAccount
currentProfile: AppBskyActorDefs.ProfileViewDetailed currentProfile: app.bsky.actor.defs.ProfileViewDetailed
preferences: UsePreferencesQueryResponse preferences: UsePreferencesQueryResponse
geolocation: Geolocation geolocation: Geolocation
} }
+2 -2
View File
@@ -1,8 +1,8 @@
import {View} from 'react-native' import {View} from 'react-native'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {chat} from '#/lexicons'
import {MessageContextMenu} from '#/components/dms/MessageContextMenu' import {MessageContextMenu} from '#/components/dms/MessageContextMenu'
import {useMessageReplies} from '#/components/dms/MessageReplies' import {useMessageReplies} from '#/components/dms/MessageReplies'
import {SwipeToReply} from '#/components/dms/SwipeToReply' import {SwipeToReply} from '#/components/dms/SwipeToReply'
@@ -15,7 +15,7 @@ export function ActionsWrapper({
moderationOpts, moderationOpts,
children, children,
}: { }: {
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
isFromSelf: boolean isFromSelf: boolean
senderProfile?: bsky.profile.AnyProfileView senderProfile?: bsky.profile.AnyProfileView
moderationOpts: ModerationOpts | undefined moderationOpts: ModerationOpts | undefined
+2 -2
View File
@@ -1,10 +1,10 @@
import {useCallback, useRef, useState} from 'react' import {useCallback, useRef, useState} from 'react'
import {Pressable, View} from 'react-native' import {Pressable, View} from 'react-native'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {chat} from '#/lexicons'
import {EMOJI_REACTION_LIMIT} from '#/lib/constants' import {EMOJI_REACTION_LIMIT} from '#/lib/constants'
import {useMaybeProfileShadow} from '#/state/cache/profile-shadow' import {useMaybeProfileShadow} from '#/state/cache/profile-shadow'
import {useConvoActive} from '#/state/messages/convo' import {useConvoActive} from '#/state/messages/convo'
@@ -29,7 +29,7 @@ export function ActionsWrapper({
moderationOpts, moderationOpts,
children, children,
}: { }: {
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
isFromSelf: boolean isFromSelf: boolean
senderProfile?: bsky.profile.AnyProfileView senderProfile?: bsky.profile.AnyProfileView
moderationOpts: ModerationOpts | undefined moderationOpts: ModerationOpts | undefined
@@ -1,9 +1,9 @@
import {memo, useState} from 'react' import {memo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {StackActions, useNavigation} from '@react-navigation/native' import {StackActions, useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
@@ -113,7 +113,7 @@ function DoneStep({
}: { }: {
convoId: string convoId: string
currentScreen: 'list' | 'conversation' currentScreen: 'list' | 'conversation'
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
+2 -2
View File
@@ -1,9 +1,9 @@
import {memo, useState} from 'react' import {memo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {StackActions, useNavigation} from '@react-navigation/native' import {StackActions, useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {useProfileShadow} from '#/state/cache/profile-shadow' import {useProfileShadow} from '#/state/cache/profile-shadow'
import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' import {useLeaveConvo} from '#/state/queries/messages/leave-conversation'
@@ -116,7 +116,7 @@ function DoneStep({
}: { }: {
convoId: string convoId: string
currentScreen: 'list' | 'conversation' currentScreen: 'list' | 'conversation'
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
+4 -2
View File
@@ -1,7 +1,8 @@
import {View} from 'react-native' import {View} from 'react-native'
import {ChatBskyGroupDefs} from '@atproto/api'
import {Plural, Trans} from '@lingui/react/macro' import {Plural, Trans} from '@lingui/react/macro'
import * as bsky from '#/types/bsky'
import {chat} from '#/lexicons'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
@@ -21,7 +22,8 @@ export function Card({size}: {size: 'large' | 'small'}) {
const t = useTheme() const t = useTheme()
const {preview, hasFixedHeight} = useChatInvite() const {preview, hasFixedHeight} = useChatInvite()
if (!ChatBskyGroupDefs.isJoinLinkPreviewView(preview)) return null if (!bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, preview))
return null
const ownerDisplayName = createSanitizedDisplayName(preview.owner) const ownerDisplayName = createSanitizedDisplayName(preview.owner)
const ownerHandle = sanitizeHandle(preview.owner.handle, '@') const ownerHandle = sanitizeHandle(preview.owner.handle, '@')
+4 -3
View File
@@ -1,8 +1,9 @@
import {setStringAsync} from 'expo-clipboard' import {setStringAsync} from 'expo-clipboard'
import {ChatBskyGroupDefs} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import * as bsky from '#/types/bsky'
import {chat} from '#/lexicons'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import { import {
type ChatInvitePreview, type ChatInvitePreview,
@@ -71,7 +72,7 @@ export function Root({
status = 'loading' status = 'loading'
} else if (error) { } else if (error) {
status = 'error' status = 'error'
} else if (ChatBskyGroupDefs.isJoinLinkPreviewView(preview)) { } else if (bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, preview)) {
status = 'available' status = 'available'
} else { } else {
// Resolved to a disabled/invalid/unrecognized preview - nothing to join. // Resolved to a disabled/invalid/unrecognized preview - nothing to join.
@@ -79,7 +80,7 @@ export function Root({
} }
let action: ChatInviteAction | undefined let action: ChatInviteAction | undefined
if (ChatBskyGroupDefs.isJoinLinkPreviewView(preview)) { if (bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, preview)) {
const convoId = preview.convo?.id const convoId = preview.convo?.id
const isFollowing = preview.owner.viewer?.followedBy ?? false const isFollowing = preview.owner.viewer?.followedBy ?? false
const hasRequested = !convoId && preview.viewer?.requestedAt != null const hasRequested = !convoId && preview.viewer?.requestedAt != null
+2 -2
View File
@@ -1,9 +1,9 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {useWindowDimensions, View} from 'react-native' import {useWindowDimensions, View} from 'react-native'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {chat} from '#/lexicons'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, tokens, useTheme} from '#/alf' import {atoms as a, tokens, useTheme} from '#/alf'
import * as ContextMenu from '#/components/ContextMenu' import * as ContextMenu from '#/components/ContextMenu'
@@ -21,7 +21,7 @@ export function EmojiReactionPicker({
message, message,
onEmojiSelect, onEmojiSelect,
}: { }: {
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
children?: TriggerProps['children'] children?: TriggerProps['children']
onEmojiSelect: (emoji: string) => void onEmojiSelect: (emoji: string) => void
}) { }) {
@@ -1,9 +1,9 @@
import {useState} from 'react' import {useState} from 'react'
import {Pressable, View} from 'react-native' import {Pressable, View} from 'react-native'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {DropdownMenu} from 'radix-ui' import {DropdownMenu} from 'radix-ui'
import {chat} from '#/lexicons'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a, flatten, useTheme} from '#/alf' import {atoms as a, flatten, useTheme} from '#/alf'
import * as EmojiPicker from '#/components/EmojiPicker' import * as EmojiPicker from '#/components/EmojiPicker'
@@ -17,7 +17,7 @@ export function EmojiReactionPicker({
children, children,
onEmojiSelect, onEmojiSelect,
}: { }: {
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
children?: EmojiPicker.TriggerProps['children'] children?: EmojiPicker.TriggerProps['children']
onEmojiSelect: (emoji: string) => void onEmojiSelect: (emoji: string) => void
}) { }) {
@@ -40,7 +40,7 @@ function MenuInner({
message, message,
onEmojiSelect, onEmojiSelect,
}: { }: {
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
onEmojiSelect: (emoji: string) => void onEmojiSelect: (emoji: string) => void
}) { }) {
const t = useTheme() const t = useTheme()
+2 -2
View File
@@ -2,12 +2,12 @@ import {memo, useCallback} from 'react'
import {Platform} from 'react-native' import {Platform} from 'react-native'
import {type GestureType} from 'react-native-gesture-handler' import {type GestureType} from 'react-native-gesture-handler'
import * as Clipboard from 'expo-clipboard' import * as Clipboard from 'expo-clipboard'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {RichText} from '@bsky.app/sdk/richtext' import {RichText} from '@bsky.app/sdk/richtext'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {chat} from '#/lexicons'
import {EMOJI_REACTION_LIMIT} from '#/lib/constants' import {EMOJI_REACTION_LIMIT} from '#/lib/constants'
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate' import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
import {asSdkFacets, richTextToString} from '#/lib/strings/rich-text-helpers' import {asSdkFacets, richTextToString} from '#/lib/strings/rich-text-helpers'
@@ -39,7 +39,7 @@ export let MessageContextMenu = ({
children, children,
swipeGesture, swipeGesture,
}: { }: {
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
senderProfile?: bsky.profile.AnyProfileView senderProfile?: bsky.profile.AnyProfileView
moderationOpts: ModerationOpts | undefined moderationOpts: ModerationOpts | undefined
children: TriggerProps['children'] children: TriggerProps['children']
+55 -41
View File
@@ -22,17 +22,13 @@ import Animated, {
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {
AppBskyEmbedRecord,
type ChatBskyActorDefs,
ChatBskyConvoDefs,
ChatBskyEmbedJoinLink,
} from '@atproto/api'
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {app, chat} from '#/lexicons'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted' import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
@@ -77,16 +73,19 @@ const SQUARED_BORDER_RADIUS = 4
const DISPLAY_NAME_INSET = 20 const DISPLAY_NAME_INSET = 20
export type MessageItemNeighbor = export type MessageItemNeighbor =
| ChatBskyConvoDefs.MessageView | chat.bsky.convo.defs.MessageView
| ChatBskyConvoDefs.DeletedMessageView | chat.bsky.convo.defs.DeletedMessageView
| null | null
function messageIsReply(message: MessageItemNeighbor): boolean { function messageIsReply(message: MessageItemNeighbor): boolean {
return ( return (
ChatBskyConvoDefs.isMessageView(message) && bsky.isType(chat.bsky.convo.defs.messageView, message) &&
(ChatBskyConvoDefs.isMessageView(message.replyTo) || (bsky.isType(chat.bsky.convo.defs.messageView, message.replyTo) ||
ChatBskyConvoDefs.isDeletedMessageView(message.replyTo) || bsky.isType(chat.bsky.convo.defs.deletedMessageView, message.replyTo) ||
ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(message.replyTo)) bsky.isType(
chat.bsky.convo.defs.messageBeforeUserJoinedGroupView,
message.replyTo,
))
) )
} }
@@ -98,7 +97,7 @@ function isWithinClusterBoundary({
direction, direction,
}: { }: {
isPending: boolean isPending: boolean
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
adjacentMessage: MessageItemNeighbor adjacentMessage: MessageItemNeighbor
isFromSameSender: boolean isFromSameSender: boolean
direction: 'prev' | 'next' direction: 'prev' | 'next'
@@ -110,7 +109,7 @@ function isWithinClusterBoundary({
return true return true
} }
if (!isFromSameSender) return true if (!isFromSameSender) return true
if (ChatBskyConvoDefs.isMessageView(adjacentMessage)) { if (bsky.isType(chat.bsky.convo.defs.messageView, adjacentMessage)) {
const currentSentAt = message.sentAt const currentSentAt = message.sentAt
const thisDate = new Date(currentSentAt) const thisDate = new Date(currentSentAt)
const adjDate = new Date(adjacentMessage.sentAt) const adjDate = new Date(adjacentMessage.sentAt)
@@ -137,7 +136,7 @@ let MessageItem = ({
isGroupChat?: boolean isGroupChat?: boolean
prevMessage: MessageItemNeighbor prevMessage: MessageItemNeighbor
nextMessage: MessageItemNeighbor nextMessage: MessageItemNeighbor
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
}): React.ReactNode => { }): React.ReactNode => {
const t = useTheme() const t = useTheme()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -155,13 +154,17 @@ let MessageItem = ({
// tombstone, or a before-joined placeholder. Narrow away the open-union // tombstone, or a before-joined placeholder. Narrow away the open-union
// fallback so we only render shapes we understand. // fallback so we only render shapes we understand.
const replyTo = const replyTo =
ChatBskyConvoDefs.isMessageView(message.replyTo) || bsky.isType(chat.bsky.convo.defs.messageView, message.replyTo) ||
ChatBskyConvoDefs.isDeletedMessageView(message.replyTo) || bsky.isType(chat.bsky.convo.defs.deletedMessageView, message.replyTo) ||
ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(message.replyTo) bsky.isType(
chat.bsky.convo.defs.messageBeforeUserJoinedGroupView,
message.replyTo,
)
? message.replyTo ? message.replyTo
: undefined : undefined
const replyToMessageId = const replyToMessageId =
replyTo && !ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(replyTo) replyTo &&
!bsky.isType(chat.bsky.convo.defs.messageBeforeUserJoinedGroupView, replyTo)
? replyTo.id ? replyTo.id
: undefined : undefined
const onPressReplyTo = replyToMessageId const onPressReplyTo = replyToMessageId
@@ -175,8 +178,14 @@ let MessageItem = ({
const isFromSelf = const isFromSelf =
message.sender?.did != null && message.sender.did === currentAccount?.did message.sender?.did != null && message.sender.did === currentAccount?.did
const prevIsMessage = ChatBskyConvoDefs.isMessageView(prevMessage) const prevIsMessage = bsky.isType(
const nextIsMessage = ChatBskyConvoDefs.isMessageView(nextMessage) chat.bsky.convo.defs.messageView,
prevMessage,
)
const nextIsMessage = bsky.isType(
chat.bsky.convo.defs.messageView,
nextMessage,
)
const isPrevFromSameSender = const isPrevFromSameSender =
prevIsMessage && prevIsMessage &&
@@ -204,7 +213,7 @@ let MessageItem = ({
}) })
const hasLargeGapFromPrev = const hasLargeGapFromPrev =
!ChatBskyConvoDefs.isMessageView(prevMessage) || !bsky.isType(chat.bsky.convo.defs.messageView, prevMessage) ||
new Date(message.sentAt).getTime() - new Date(message.sentAt).getTime() -
new Date(prevMessage.sentAt).getTime() > new Date(prevMessage.sentAt).getTime() >
MESSAGE_GAP_THRESHOLD_MS MESSAGE_GAP_THRESHOLD_MS
@@ -254,8 +263,8 @@ let MessageItem = ({
const isEmojiOnly = isOnlyEmoji(message.text) const isEmojiOnly = isOnlyEmoji(message.text)
const hasEmbed = const hasEmbed =
AppBskyEmbedRecord.isView(message.embed) || bsky.isType(app.bsky.embed.record.view, message.embed) ||
ChatBskyEmbedJoinLink.isView(message.embed) bsky.isType(chat.bsky.embed.joinLink.view, message.embed)
const hasEmbedAndText = hasEmbed && rt.text.length > 0 const hasEmbedAndText = hasEmbed && rt.text.length > 0
const targetBottomRadius = squaredBottomCorner const targetBottomRadius = squaredBottomCorner
@@ -516,7 +525,7 @@ let MessageItem = ({
message={message} message={message}
senderProfile={profile} senderProfile={profile}
moderationOpts={moderationOpts}> moderationOpts={moderationOpts}>
{AppBskyEmbedRecord.isView(message.embed) && ( {bsky.isType(app.bsky.embed.record.view, message.embed) && (
<MessageItemEmbed <MessageItemEmbed
embed={message.embed} embed={message.embed}
isFromSelf={isFromSelf} isFromSelf={isFromSelf}
@@ -528,7 +537,10 @@ let MessageItem = ({
highlightSV={highlightSV} highlightSV={highlightSV}
/> />
)} )}
{ChatBskyEmbedJoinLink.isView(message.embed) && ( {bsky.isType(
chat.bsky.embed.joinLink.view,
message.embed,
) && (
<MessageItemInviteEmbed <MessageItemInviteEmbed
embed={message.embed} embed={message.embed}
isFromSelf={isFromSelf} isFromSelf={isFromSelf}
@@ -666,7 +678,7 @@ function BlockedPlaceholder({
profile, profile,
style, style,
}: { }: {
profile: Shadow<ChatBskyActorDefs.ProfileViewBasic> profile: Shadow<chat.bsky.actor.defs.ProfileViewBasic>
style?: AnimatedStyle<ViewStyle> style?: AnimatedStyle<ViewStyle>
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
@@ -776,13 +788,13 @@ function ReplyCaption({
onPress, onPress,
}: { }: {
replyTo: replyTo:
| ChatBskyConvoDefs.MessageView | chat.bsky.convo.defs.MessageView
| ChatBskyConvoDefs.DeletedMessageView | chat.bsky.convo.defs.DeletedMessageView
| ChatBskyConvoDefs.MessageBeforeUserJoinedGroupView | chat.bsky.convo.defs.MessageBeforeUserJoinedGroupView
isFromSelf: boolean isFromSelf: boolean
isGroupChat: boolean isGroupChat: boolean
replierDisplayName: string | null replierDisplayName: string | null
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
onPress?: () => void onPress?: () => void
}) { }) {
const t = useTheme() const t = useTheme()
@@ -791,8 +803,8 @@ function ReplyCaption({
let caption: string = '' let caption: string = ''
if ( if (
ChatBskyConvoDefs.isMessageView(replyTo) || bsky.isType(chat.bsky.convo.defs.messageView, replyTo) ||
ChatBskyConvoDefs.isDeletedMessageView(replyTo) bsky.isType(chat.bsky.convo.defs.deletedMessageView, replyTo)
) { ) {
const originalSenderIsSelf = replyTo.sender.did === currentAccount?.did const originalSenderIsSelf = replyTo.sender.did === currentAccount?.did
const originalProfile = relatedProfiles.get(replyTo.sender.did) const originalProfile = relatedProfiles.get(replyTo.sender.did)
@@ -865,11 +877,11 @@ function ReplyQuote({
onPress, onPress,
}: { }: {
replyTo: replyTo:
| ChatBskyConvoDefs.MessageView | chat.bsky.convo.defs.MessageView
| ChatBskyConvoDefs.DeletedMessageView | chat.bsky.convo.defs.DeletedMessageView
| ChatBskyConvoDefs.MessageBeforeUserJoinedGroupView | chat.bsky.convo.defs.MessageBeforeUserJoinedGroupView
isFromSelf: boolean isFromSelf: boolean
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
onPress?: () => void onPress?: () => void
}) { }) {
const t = useTheme() const t = useTheme()
@@ -877,8 +889,8 @@ function ReplyQuote({
const getReplyPreviewText = useReplyPreviewText() const getReplyPreviewText = useReplyPreviewText()
const senderDid = const senderDid =
ChatBskyConvoDefs.isMessageView(replyTo) || bsky.isType(chat.bsky.convo.defs.messageView, replyTo) ||
ChatBskyConvoDefs.isDeletedMessageView(replyTo) bsky.isType(chat.bsky.convo.defs.deletedMessageView, replyTo)
? replyTo.sender.did ? replyTo.sender.did
: undefined : undefined
const senderProfile = useMaybeProfileShadow( const senderProfile = useMaybeProfileShadow(
@@ -908,9 +920,11 @@ function ReplyQuote({
comment: 'A reply summary in chat', comment: 'A reply summary in chat',
}) })
subtle = true subtle = true
} else if (ChatBskyConvoDefs.isMessageView(replyTo)) { } else if (bsky.isType(chat.bsky.convo.defs.messageView, replyTo)) {
;({text, subtle} = getReplyPreviewText(replyTo)) ;({text, subtle} = getReplyPreviewText(replyTo))
} else if (ChatBskyConvoDefs.isMessageBeforeUserJoinedGroupView(replyTo)) { } else if (
bsky.isType(chat.bsky.convo.defs.messageBeforeUserJoinedGroupView, replyTo)
) {
text = l({ text = l({
message: `(message sent before you joined)`, message: `(message sent before you joined)`,
comment: 'A reply summary in chat', comment: 'A reply summary in chat',
+3 -2
View File
@@ -5,8 +5,9 @@ import Animated, {
type SharedValue, type SharedValue,
useAnimatedStyle, useAnimatedStyle,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {type $Typed, type AppBskyEmbedRecord} from '@atproto/api'
import {type $Typed} from '@atproto/lex'
import {app} from '#/lexicons'
import {atoms as a, native, useTheme, web} from '#/alf' import {atoms as a, native, useTheme, web} from '#/alf'
import {Embed, PostEmbedViewContext} from '#/components/Post/Embed' import {Embed, PostEmbedViewContext} from '#/components/Post/Embed'
import {MessageContextProvider} from './MessageContext' import {MessageContextProvider} from './MessageContext'
@@ -22,7 +23,7 @@ let MessageItemEmbed = ({
squaredBottomCorner, squaredBottomCorner,
highlightSV, highlightSV,
}: { }: {
embed: $Typed<AppBskyEmbedRecord.View> embed: $Typed<app.bsky.embed.record.View>
isFromSelf: boolean isFromSelf: boolean
isGroupChat: boolean isGroupChat: boolean
squaredTopCorner: boolean squaredTopCorner: boolean
@@ -5,8 +5,9 @@ import Animated, {
type SharedValue, type SharedValue,
useAnimatedStyle, useAnimatedStyle,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {type $Typed, type ChatBskyEmbedJoinLink} from '@atproto/api'
import {type $Typed} from '@atproto/lex'
import {chat} from '#/lexicons'
import {useConvoActive} from '#/state/messages/convo' import {useConvoActive} from '#/state/messages/convo'
import {isKnownJoinLinkPreview} from '#/state/queries/join-links' import {isKnownJoinLinkPreview} from '#/state/queries/join-links'
import {atoms as a, native, useTheme, web} from '#/alf' import {atoms as a, native, useTheme, web} from '#/alf'
@@ -24,7 +25,7 @@ let MessageItemInviteEmbed = ({
squaredBottomCorner, squaredBottomCorner,
highlightSV, highlightSV,
}: { }: {
embed: $Typed<ChatBskyEmbedJoinLink.View> embed: $Typed<chat.bsky.embed.joinLink.View>
isFromSelf: boolean isFromSelf: boolean
isGroupChat: boolean isGroupChat: boolean
squaredTopCorner: boolean squaredTopCorner: boolean
+11 -11
View File
@@ -7,10 +7,10 @@ import {
useState, useState,
} from 'react' } from 'react'
import {LayoutAnimation} from 'react-native' import {LayoutAnimation} from 'react-native'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {chat} from '#/lexicons'
import {useConvoActive} from '#/state/messages/convo' import {useConvoActive} from '#/state/messages/convo'
import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache' import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache'
import {useDialogControl} from '#/components/Dialog' import {useDialogControl} from '#/components/Dialog'
@@ -23,12 +23,12 @@ import * as Toast from '#/components/Toast'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
type MessageDialogsContextType = { type MessageDialogsContextType = {
openDeleteMessage: (message: ChatBskyConvoDefs.MessageView) => void openDeleteMessage: (message: chat.bsky.convo.defs.MessageView) => void
openReportMessage: ( openReportMessage: (
message: ChatBskyConvoDefs.MessageView, message: chat.bsky.convo.defs.MessageView,
senderProfile: bsky.profile.AnyProfileView | undefined, senderProfile: bsky.profile.AnyProfileView | undefined,
) => void ) => void
openReactions: (message: ChatBskyConvoDefs.MessageView) => void openReactions: (message: chat.bsky.convo.defs.MessageView) => void
} }
const Context = createContext<MessageDialogsContextType | null>(null) const Context = createContext<MessageDialogsContextType | null>(null)
@@ -52,18 +52,18 @@ export function MessageOverlays({children}: {children: React.ReactNode}) {
const reactionsControl = useDialogControl() const reactionsControl = useDialogControl()
const [deleteTarget, setDeleteTarget] = const [deleteTarget, setDeleteTarget] =
useState<ChatBskyConvoDefs.MessageView | null>(null) useState<chat.bsky.convo.defs.MessageView | null>(null)
const [reportTarget, setReportTarget] = useState<{ const [reportTarget, setReportTarget] = useState<{
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
senderProfile: bsky.profile.AnyProfileView | undefined senderProfile: bsky.profile.AnyProfileView | undefined
} | null>(null) } | null>(null)
const [afterReportTarget, setAfterReportTarget] = const [afterReportTarget, setAfterReportTarget] =
useState<ChatBskyConvoDefs.MessageView | null>(null) useState<chat.bsky.convo.defs.MessageView | null>(null)
const [reactionsTarget, setReactionsTarget] = const [reactionsTarget, setReactionsTarget] =
useState<ChatBskyConvoDefs.MessageView | null>(null) useState<chat.bsky.convo.defs.MessageView | null>(null)
const openDeleteMessage = useCallback( const openDeleteMessage = useCallback(
(message: ChatBskyConvoDefs.MessageView) => { (message: chat.bsky.convo.defs.MessageView) => {
setDeleteTarget(message) setDeleteTarget(message)
deleteControl.open() deleteControl.open()
}, },
@@ -72,7 +72,7 @@ export function MessageOverlays({children}: {children: React.ReactNode}) {
const openReportMessage = useCallback( const openReportMessage = useCallback(
( (
message: ChatBskyConvoDefs.MessageView, message: chat.bsky.convo.defs.MessageView,
senderProfile: bsky.profile.AnyProfileView | undefined, senderProfile: bsky.profile.AnyProfileView | undefined,
) => { ) => {
setReportTarget({message, senderProfile}) setReportTarget({message, senderProfile})
@@ -82,7 +82,7 @@ export function MessageOverlays({children}: {children: React.ReactNode}) {
) )
const openReactions = useCallback( const openReactions = useCallback(
(message: ChatBskyConvoDefs.MessageView) => { (message: chat.bsky.convo.defs.MessageView) => {
setReactionsTarget(message) setReactionsTarget(message)
}, },
[], [],
+2 -2
View File
@@ -1,11 +1,11 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {app} from '#/lexicons'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability' import {useGetConvoAvailabilityQuery} from '#/state/queries/messages/get-convo-availability'
@@ -20,7 +20,7 @@ import {useAnalytics} from '#/analytics'
export function MessageProfileButton({ export function MessageProfileButton({
profile, profile,
}: { }: {
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
}) { }) {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme() const t = useTheme()
+6 -7
View File
@@ -1,3 +1,4 @@
import {chat} from '#/lexicons'
import { import {
createContext, createContext,
useCallback, useCallback,
@@ -7,7 +8,6 @@ import {
useRef, useRef,
useState, useState,
} from 'react' } from 'react'
import {type ChatBskyConvoDefs} from '@atproto/api'
/** /**
* How long a message stays highlighted after scrolling to it, before the flash * How long a message stays highlighted after scrolling to it, before the flash
@@ -28,8 +28,8 @@ type MessageRepliesContextType = {
/** /**
* The message currently staged for reply in the composer, or null. * The message currently staged for reply in the composer, or null.
*/ */
replyTo: ChatBskyConvoDefs.MessageView | null replyTo: chat.bsky.convo.defs.MessageView | null
setReply: (message: ChatBskyConvoDefs.MessageView) => void setReply: (message: chat.bsky.convo.defs.MessageView) => void
clearReply: () => void clearReply: () => void
/** /**
* Scroll the list to a message, if it's currently loaded, and flash it. No-op * Scroll the list to a message, if it's currently loaded, and flash it. No-op
@@ -66,9 +66,8 @@ export function MessageRepliesProvider({
*/ */
scrollToMessage: (messageId: string) => boolean scrollToMessage: (messageId: string) => boolean
}) { }) {
const [replyTo, setReplyTo] = useState<ChatBskyConvoDefs.MessageView | null>( const [replyTo, setReplyTo] =
null, useState<chat.bsky.convo.defs.MessageView | null>(null)
)
const [highlightedMessage, setHighlightedMessage] = const [highlightedMessage, setHighlightedMessage] =
useState<HighlightedMessage | null>(null) useState<HighlightedMessage | null>(null)
const highlightKey = useRef(0) const highlightKey = useRef(0)
@@ -76,7 +75,7 @@ export function MessageRepliesProvider({
null, null,
) )
const setReply = useCallback((message: ChatBskyConvoDefs.MessageView) => { const setReply = useCallback((message: chat.bsky.convo.defs.MessageView) => {
setReplyTo(message) setReplyTo(message)
}, []) }, [])
+8 -8
View File
@@ -6,9 +6,9 @@ import {
useWindowDimensions, useWindowDimensions,
View, View,
} from 'react-native' } from 'react-native'
import {type ChatBskyActorDefs, type ChatBskyConvoDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {chat} from '#/lexicons'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
@@ -28,7 +28,7 @@ import type * as bsky from '#/types/bsky'
type Reaction = { type Reaction = {
key: string key: string
value: string value: string
senders: ChatBskyConvoDefs.ReactionViewSender[] senders: chat.bsky.convo.defs.ReactionViewSender[]
count: number count: number
} }
@@ -41,8 +41,8 @@ export function ReactionsDialog({
onClose, onClose,
}: { }: {
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
onClose?: () => void onClose?: () => void
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
@@ -142,10 +142,10 @@ function ReactionRow({
control: Dialog.DialogControlProps control: Dialog.DialogControlProps
convo: ActiveConvoStates convo: ActiveConvoStates
currentAccount?: SessionAccount currentAccount?: SessionAccount
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
profile: bsky.profile.AnyProfileView profile: bsky.profile.AnyProfileView
reaction: ChatBskyConvoDefs.ReactionView reaction: chat.bsky.convo.defs.ReactionView
allReactions: ChatBskyConvoDefs.ReactionView[] allReactions: chat.bsky.convo.defs.ReactionView[]
selected: string selected: string
setSelected: React.Dispatch<React.SetStateAction<string>> setSelected: React.Dispatch<React.SetStateAction<string>>
}) { }) {
@@ -398,7 +398,7 @@ function ReactionTab({
} }
export function groupReactions( export function groupReactions(
reactions: ChatBskyConvoDefs.ReactionView[] | undefined, reactions: chat.bsky.convo.defs.ReactionView[] | undefined,
): Reaction[] { ): Reaction[] {
const grouped = new Map<string, Reaction>() const grouped = new Map<string, Reaction>()
for (const reaction of reactions ?? []) { for (const reaction of reactions ?? []) {
+2 -2
View File
@@ -7,10 +7,10 @@ import Animated, {
useDerivedValue, useDerivedValue,
withTiming, withTiming,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {type ChatBskyActorDefs} from '@atproto/api'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {chat} from '#/lexicons'
import {HITSLOP_10} from '#/lib/constants' import {HITSLOP_10} from '#/lib/constants'
import {type SystemMessageGroupItem} from '#/screens/Messages/components/groupSystemMessages' import {type SystemMessageGroupItem} from '#/screens/Messages/components/groupSystemMessages'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -29,7 +29,7 @@ export function SystemMessageGroup({
item: SystemMessageGroupItem item: SystemMessageGroupItem
expanded: boolean expanded: boolean
onToggle: (key: string) => void onToggle: (key: string) => void
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
}) { }) {
const t = useTheme() const t = useTheme()
const {t: l} = useLingui() const {t: l} = useLingui()
+2 -2
View File
@@ -1,7 +1,7 @@
import {View} from 'react-native' import {View} from 'react-native'
import {type ChatBskyActorDefs} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {chat} from '#/lexicons'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {type ConvoItem} from '#/state/messages/convo/types' import {type ConvoItem} from '#/state/messages/convo/types'
import {useInviteLinkDialog} from '#/screens/Messages/components/InviteLinkDialogProvider' import {useInviteLinkDialog} from '#/screens/Messages/components/InviteLinkDialogProvider'
@@ -16,7 +16,7 @@ export function SystemMessageItem({
relatedProfiles, relatedProfiles,
}: { }: {
item: ConvoItem & {type: 'system-message'} item: ConvoItem & {type: 'system-message'}
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
}) { }) {
const t = useTheme() const t = useTheme()
const {i18n, t: l} = useLingui() const {i18n, t: l} = useLingui()
+10 -15
View File
@@ -1,12 +1,7 @@
import {
AppBskyEmbedRecord,
type ChatBskyActorDefs,
ChatBskyConvoDefs,
ChatBskyEmbedJoinLink,
} from '@atproto/api'
import {type I18n} from '@lingui/core' import {type I18n} from '@lingui/core'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {app, chat} from '#/lexicons'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted' import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import { import {
@@ -14,12 +9,12 @@ import {
toBskyAppUrl, toBskyAppUrl,
toShortUrl, toShortUrl,
} from '#/lib/strings/url-helpers' } from '#/lib/strings/url-helpers'
import type * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export type UserMessageInfo = { export type UserMessageInfo = {
message: string | null message: string | null
sentAt: string sentAt: string
reportableMessage?: ChatBskyConvoDefs.MessageView reportableMessage?: chat.bsky.convo.defs.MessageView
isBlockedMessage: boolean isBlockedMessage: boolean
} }
@@ -36,7 +31,7 @@ export function isDidBlockedInConvo({
primaryProfile, primaryProfile,
}: { }: {
did: string | undefined did: string | undefined
members: ChatBskyActorDefs.ProfileViewBasic[] members: chat.bsky.actor.defs.ProfileViewBasic[]
primaryProfile?: bsky.profile.AnyProfileView primaryProfile?: bsky.profile.AnyProfileView
}): boolean { }): boolean {
if (!did) return false if (!did) return false
@@ -53,12 +48,12 @@ export function getMessageInfo({
primaryProfile, primaryProfile,
i18n, i18n,
}: { }: {
convo: ChatBskyConvoDefs.ConvoView convo: chat.bsky.convo.defs.ConvoView
currentAccountDid: string | undefined currentAccountDid: string | undefined
primaryProfile?: bsky.profile.AnyProfileView primaryProfile?: bsky.profile.AnyProfileView
i18n: I18n i18n: I18n
}): UserMessageInfo | null { }): UserMessageInfo | null {
if (!ChatBskyConvoDefs.isMessageView(convo.lastMessage)) { if (!bsky.isType(chat.bsky.convo.defs.messageView, convo.lastMessage)) {
return null return null
} }
@@ -67,7 +62,7 @@ export function getMessageInfo({
const senderDid = lastMessage.sender?.did const senderDid = lastMessage.sender?.did
const sender = convo.members.find(m => m.did === senderDid) const sender = convo.members.find(m => m.did === senderDid)
const name = sender ? createSanitizedDisplayName(sender) : null const name = sender ? createSanitizedDisplayName(sender) : null
const isGroup = ChatBskyConvoDefs.isGroupConvo(convo.kind) const isGroup = bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)
const reportableMessage = isFromMe ? undefined : lastMessage const reportableMessage = isFromMe ? undefined : lastMessage
const isBlockedMessage = isDidBlockedInConvo({ const isBlockedMessage = isDidBlockedInConvo({
@@ -105,10 +100,10 @@ export function getMessageInfo({
msg`(contains embedded content)`, msg`(contains embedded content)`,
) )
if (AppBskyEmbedRecord.isView(lastMessage.embed)) { if (bsky.isType(app.bsky.embed.record.view, lastMessage.embed)) {
const embed = lastMessage.embed const embed = lastMessage.embed
if (AppBskyEmbedRecord.isViewRecord(embed.record)) { if (bsky.isType(app.bsky.embed.record.viewRecord, embed.record)) {
const record = embed.record const record = embed.record
const path = postUriToRelativePath(record.uri, { const path = postUriToRelativePath(record.uri, {
handle: record.author.handle, handle: record.author.handle,
@@ -119,7 +114,7 @@ export function getMessageInfo({
} else { } else {
message = prefix(defaultEmbeddedContentMessage) message = prefix(defaultEmbeddedContentMessage)
} }
} else if (ChatBskyEmbedJoinLink.isView(lastMessage.embed)) { } else if (bsky.isType(chat.bsky.embed.joinLink.view, lastMessage.embed)) {
message = prefix(i18n._(msg`(chat invite link)`)) message = prefix(i18n._(msg`(chat invite link)`))
} else { } else {
message = prefix(defaultEmbeddedContentMessage) message = prefix(defaultEmbeddedContentMessage)
+9 -4
View File
@@ -1,10 +1,10 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {type I18n} from '@lingui/core' import {type I18n} from '@lingui/core'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {chat} from '#/lexicons'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {isDidBlockedInConvo} from '#/components/dms/getMessageInfo' import {isDidBlockedInConvo} from '#/components/dms/getMessageInfo'
import type * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export type UserReactionInfo = { export type UserReactionInfo = {
message: string message: string
@@ -18,12 +18,17 @@ export function getReactionInfo({
primaryProfile, primaryProfile,
i18n, i18n,
}: { }: {
convo: ChatBskyConvoDefs.ConvoView convo: chat.bsky.convo.defs.ConvoView
currentAccountDid: string | undefined currentAccountDid: string | undefined
primaryProfile?: bsky.profile.AnyProfileView primaryProfile?: bsky.profile.AnyProfileView
i18n: I18n i18n: I18n
}): UserReactionInfo | null { }): UserReactionInfo | null {
if (!ChatBskyConvoDefs.isMessageAndReactionView(convo.lastReaction)) { if (
!bsky.isType(
chat.bsky.convo.defs.messageAndReactionView,
convo.lastReaction,
)
) {
return null return null
} }
+44 -18
View File
@@ -1,7 +1,8 @@
import {type ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api'
import {type MessageDescriptor} from '@lingui/core' import {type MessageDescriptor} from '@lingui/core'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import * as bsky from '#/types/bsky'
import {chat} from '#/lexicons'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
import {ArrowBoxRight_Stroke2_Corner3_Rounded as JoinIcon} from '#/components/icons/ArrowBoxRight' import {ArrowBoxRight_Stroke2_Corner3_Rounded as JoinIcon} from '#/components/icons/ArrowBoxRight'
@@ -19,7 +20,7 @@ import {PencilLine_Stroke2_Corner0_Rounded as PencilIcon} from '#/components/ico
export type SystemMessageAction = export type SystemMessageAction =
| { | {
kind: 'profile' kind: 'profile'
profile: ChatBskyActorDefs.ProfileViewBasic profile: chat.bsky.actor.defs.ProfileViewBasic
displayName: string displayName: string
} }
| {kind: 'inviteLink'} | {kind: 'inviteLink'}
@@ -31,8 +32,8 @@ export type SystemMessageInfo = {
} }
function getProfileAction( function getProfileAction(
user: ChatBskyConvoDefs.SystemMessageReferredUser, user: chat.bsky.convo.defs.SystemMessageReferredUser,
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>, relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>,
): Extract<SystemMessageAction, {kind: 'profile'}> | null { ): Extract<SystemMessageAction, {kind: 'profile'}> | null {
const profile = relatedProfiles.get(user.did) const profile = relatedProfiles.get(user.did)
if (!profile) return null if (!profile) return null
@@ -44,11 +45,11 @@ function getProfileAction(
} }
export function getSystemMessageInfo( export function getSystemMessageInfo(
data: ChatBskyConvoDefs.SystemMessageView['data'], data: chat.bsky.convo.defs.SystemMessageView['data'],
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>, relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>,
opts = {short: false}, opts = {short: false},
): SystemMessageInfo | null { ): SystemMessageInfo | null {
if (ChatBskyConvoDefs.isSystemMessageDataAddMember(data)) { if (bsky.isType(chat.bsky.convo.defs.systemMessageDataAddMember, data)) {
const action = getProfileAction(data.member, relatedProfiles) const action = getProfileAction(data.member, relatedProfiles)
return { return {
Icon: JoinIcon, Icon: JoinIcon,
@@ -61,7 +62,9 @@ export function getSystemMessageInfo(
: msg`Someone was added to the group`, : msg`Someone was added to the group`,
action: action ?? undefined, action: action ?? undefined,
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataRemoveMember(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataRemoveMember, data)
) {
const action = getProfileAction(data.member, relatedProfiles) const action = getProfileAction(data.member, relatedProfiles)
return { return {
Icon: LeaveIcon, Icon: LeaveIcon,
@@ -74,7 +77,9 @@ export function getSystemMessageInfo(
: msg`Someone was removed from the group`, : msg`Someone was removed from the group`,
action: action ?? undefined, action: action ?? undefined,
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataMemberJoin(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataMemberJoin, data)
) {
const action = getProfileAction(data.member, relatedProfiles) const action = getProfileAction(data.member, relatedProfiles)
return { return {
Icon: JoinIcon, Icon: JoinIcon,
@@ -87,7 +92,9 @@ export function getSystemMessageInfo(
: msg`Someone joined the group`, : msg`Someone joined the group`,
action: action ?? undefined, action: action ?? undefined,
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataMemberLeave(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataMemberLeave, data)
) {
const action = getProfileAction(data.member, relatedProfiles) const action = getProfileAction(data.member, relatedProfiles)
return { return {
Icon: LeaveIcon, Icon: LeaveIcon,
@@ -100,13 +107,24 @@ export function getSystemMessageInfo(
: msg`Someone left the group`, : msg`Someone left the group`,
action: action ?? undefined, action: action ?? undefined,
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataLockConvo(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataLockConvo, data)
) {
return {Icon: LockIcon, message: msg`Chat locked`} return {Icon: LockIcon, message: msg`Chat locked`}
} else if (ChatBskyConvoDefs.isSystemMessageDataUnlockConvo(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataUnlockConvo, data)
) {
return {Icon: UnlockIcon, message: msg`Chat unlocked`} return {Icon: UnlockIcon, message: msg`Chat unlocked`}
} else if (ChatBskyConvoDefs.isSystemMessageDataLockConvoPermanently(data)) { } else if (
bsky.isType(
chat.bsky.convo.defs.systemMessageDataLockConvoPermanently,
data,
)
) {
return {Icon: LockIcon, message: msg`Chat ended`} return {Icon: LockIcon, message: msg`Chat ended`}
} else if (ChatBskyConvoDefs.isSystemMessageDataEditGroup(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataEditGroup, data)
) {
return { return {
Icon: PencilIcon, Icon: PencilIcon,
message: message:
@@ -114,25 +132,33 @@ export function getSystemMessageInfo(
? msg`Chat title changed to ${data.newName}` ? msg`Chat title changed to ${data.newName}`
: msg`Chat title changed`, : msg`Chat title changed`,
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataCreateJoinLink(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataCreateJoinLink, data)
) {
return { return {
Icon: ChainLinkIcon, Icon: ChainLinkIcon,
message: msg`Invite link created`, message: msg`Invite link created`,
action: {kind: 'inviteLink'}, action: {kind: 'inviteLink'},
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataEditJoinLink(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataEditJoinLink, data)
) {
return { return {
Icon: ChainLinkIcon, Icon: ChainLinkIcon,
message: msg`Invite link edited`, message: msg`Invite link edited`,
action: {kind: 'inviteLink'}, action: {kind: 'inviteLink'},
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataEnableJoinLink(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataEnableJoinLink, data)
) {
return { return {
Icon: ChainLinkIcon, Icon: ChainLinkIcon,
message: msg`Invite link enabled`, message: msg`Invite link enabled`,
action: {kind: 'inviteLink'}, action: {kind: 'inviteLink'},
} }
} else if (ChatBskyConvoDefs.isSystemMessageDataDisableJoinLink(data)) { } else if (
bsky.isType(chat.bsky.convo.defs.systemMessageDataDisableJoinLink, data)
) {
return { return {
Icon: ChainLinkBrokenIcon, Icon: ChainLinkBrokenIcon,
message: msg`Invite link disabled`, message: msg`Invite link disabled`,
+24 -17
View File
@@ -1,12 +1,7 @@
import {
AppBskyEmbedExternal,
AppBskyEmbedRecord,
type ChatBskyConvoDefs,
ChatBskyEmbedJoinLink,
ChatBskyGroupDefs,
} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import * as bsky from '#/types/bsky'
import {app, chat} from '#/lexicons'
import {BSKY_APP_HOST, toShortUrl} from '#/lib/strings/url-helpers' import {BSKY_APP_HOST, toShortUrl} from '#/lib/strings/url-helpers'
/** /**
@@ -21,13 +16,13 @@ type ReplyEmbedSummary =
| {type: 'unknown'} | {type: 'unknown'}
function summarizeReplyEmbed( function summarizeReplyEmbed(
embed: ChatBskyConvoDefs.MessageView['embed'], embed: chat.bsky.convo.defs.MessageView['embed'],
): ReplyEmbedSummary { ): ReplyEmbedSummary {
if (!AppBskyEmbedRecord.isView(embed)) return {type: 'unknown'} if (!bsky.isType(app.bsky.embed.record.view, embed)) return {type: 'unknown'}
const {record} = embed const {record} = embed
if (AppBskyEmbedRecord.isViewRecord(record)) { if (bsky.isType(app.bsky.embed.record.viewRecord, record)) {
const inner = record.embeds?.[0] const inner = record.embeds?.[0]
if (AppBskyEmbedExternal.isView(inner)) { if (bsky.isType(app.bsky.embed.external.view, inner)) {
return {type: 'external', uri: inner.external.uri} return {type: 'external', uri: inner.external.uri}
} }
return {type: 'post'} return {type: 'post'}
@@ -45,25 +40,32 @@ function summarizeReplyEmbed(
* callers can render it in a muted/italic style. * callers can render it in a muted/italic style.
*/ */
export function useReplyPreviewText(): ( export function useReplyPreviewText(): (
message: ChatBskyConvoDefs.MessageView, message: chat.bsky.convo.defs.MessageView,
) => {text: string; subtle: boolean} { ) => {text: string; subtle: boolean} {
const {t: l} = useLingui() const {t: l} = useLingui()
return (message: ChatBskyConvoDefs.MessageView) => { return (message: chat.bsky.convo.defs.MessageView) => {
const text = message.text const text = message.text
if (text.trim()) { if (text.trim()) {
return {text, subtle: false} return {text, subtle: false}
} }
if (ChatBskyEmbedJoinLink.isView(message.embed)) { if (bsky.isType(chat.bsky.embed.joinLink.view, message.embed)) {
const {joinLinkPreview} = message.embed const {joinLinkPreview} = message.embed
if (ChatBskyGroupDefs.isJoinLinkPreviewView(joinLinkPreview)) { if (
bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, joinLinkPreview)
) {
return { return {
text: `${BSKY_APP_HOST}/chat/${joinLinkPreview.code}`, text: `${BSKY_APP_HOST}/chat/${joinLinkPreview.code}`,
subtle: true, subtle: true,
} }
} }
if (ChatBskyGroupDefs.isDisabledJoinLinkPreviewView(joinLinkPreview)) { if (
bsky.isType(
chat.bsky.group.defs.disabledJoinLinkPreviewView,
joinLinkPreview,
)
) {
return { return {
text: l({ text: l({
message: '(disabled chat invite link)', message: '(disabled chat invite link)',
@@ -72,7 +74,12 @@ export function useReplyPreviewText(): (
subtle: true, subtle: true,
} }
} }
if (ChatBskyGroupDefs.isInvalidJoinLinkPreviewView(joinLinkPreview)) { if (
bsky.isType(
chat.bsky.group.defs.invalidJoinLinkPreviewView,
joinLinkPreview,
)
) {
return { return {
text: l({ text: l({
message: '(invalid chat invite link)', message: '(invalid chat invite link)',
+19 -33
View File
@@ -1,6 +1,7 @@
import {type $Typed, ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {type $Typed} from '@atproto/lex'
import {chat} from '#/lexicons'
import {EMOJI_REACTION_LIMIT} from '#/lib/constants' import {EMOJI_REACTION_LIMIT} from '#/lib/constants'
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted' import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
@@ -77,7 +78,7 @@ export function localDateString(date: Date) {
} }
export function hasAlreadyReacted( export function hasAlreadyReacted(
message: ChatBskyConvoDefs.MessageView, message: chat.bsky.convo.defs.MessageView,
myDid: string | undefined, myDid: string | undefined,
emoji: string, emoji: string,
): boolean { ): boolean {
@@ -98,9 +99,9 @@ export function hasAlreadyReacted(
* already render anonymously ("Someone reacted"). * already render anonymously ("Someone reacted").
*/ */
export function filterBlockedReactions( export function filterBlockedReactions(
reactions: ChatBskyConvoDefs.ReactionView[] | undefined, reactions: chat.bsky.convo.defs.ReactionView[] | undefined,
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic>, relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>,
): ChatBskyConvoDefs.ReactionView[] { ): chat.bsky.convo.defs.ReactionView[] {
if (!reactions) return [] if (!reactions) return []
return reactions.filter(reaction => { return reactions.filter(reaction => {
const profile = relatedProfiles.get(reaction.sender.did) const profile = relatedProfiles.get(reaction.sender.did)
@@ -109,7 +110,7 @@ export function filterBlockedReactions(
} }
export function hasReachedReactionLimit( export function hasReachedReactionLimit(
message: ChatBskyConvoDefs.MessageView, message: chat.bsky.convo.defs.MessageView,
myDid: string | undefined, myDid: string | undefined,
): boolean { ): boolean {
if (!message.reactions) { if (!message.reactions) {
@@ -170,25 +171,25 @@ export function canReact({
return true return true
} }
export type GroupConvoMember = ChatBskyActorDefs.ProfileViewBasic & { export type GroupConvoMember = chat.bsky.actor.defs.ProfileViewBasic & {
// can be missing if account deleted // can be missing if account deleted
kind?: $Typed<ChatBskyActorDefs.GroupConvoMember> kind?: $Typed<chat.bsky.actor.defs.GroupConvoMember>
} }
export type DirectConvoMember = ChatBskyActorDefs.ProfileViewBasic & { export type DirectConvoMember = chat.bsky.actor.defs.ProfileViewBasic & {
kind: $Typed<ChatBskyActorDefs.DirectConvoMember> kind: $Typed<chat.bsky.actor.defs.DirectConvoMember>
} }
export type ConvoWithDetails = {view: ChatBskyConvoDefs.ConvoView} & ( export type ConvoWithDetails = {view: chat.bsky.convo.defs.ConvoView} & (
| { | {
kind: 'group' kind: 'group'
details: $Typed<ChatBskyConvoDefs.GroupConvo> details: $Typed<chat.bsky.convo.defs.GroupConvo>
primaryMember?: GroupConvoMember // the owner - may have left, thus optional primaryMember?: GroupConvoMember // the owner - may have left, thus optional
members: Array<GroupConvoMember> members: Array<GroupConvoMember>
} }
| { | {
kind: 'direct' kind: 'direct'
details: $Typed<ChatBskyConvoDefs.DirectConvo> details: $Typed<chat.bsky.convo.defs.DirectConvo>
primaryMember: DirectConvoMember // the other user primaryMember: DirectConvoMember // the other user
members: Array<DirectConvoMember> members: Array<DirectConvoMember>
} }
@@ -199,24 +200,14 @@ export type ConvoWithDetails = {view: ChatBskyConvoDefs.ConvoView} & (
* and enforces the correct type for convo members. * and enforces the correct type for convo members.
*/ */
export function parseConvoView( export function parseConvoView(
convoView: ChatBskyConvoDefs.ConvoView, convoView: chat.bsky.convo.defs.ConvoView,
ownDid: string | undefined, ownDid: string | undefined,
): ConvoWithDetails | null { ): ConvoWithDetails | null {
if ( if (bsky.isType(chat.bsky.convo.defs.groupConvo, convoView.kind)) {
bsky.dangerousIsType<ChatBskyConvoDefs.GroupConvo>(
convoView.kind,
ChatBskyConvoDefs.isGroupConvo,
)
) {
let owner: GroupConvoMember | undefined = undefined let owner: GroupConvoMember | undefined = undefined
for (const member of convoView.members) { for (const member of convoView.members) {
if ( if (bsky.isType(chat.bsky.actor.defs.groupConvoMember, member.kind)) {
bsky.dangerousIsType<ChatBskyActorDefs.GroupConvoMember>(
member.kind,
ChatBskyActorDefs.isGroupConvoMember,
)
) {
if (member.kind.role === 'owner') { if (member.kind.role === 'owner') {
// have to do a type assertion here // have to do a type assertion here
// this works: {...member, kind: member.kind} // this works: {...member, kind: member.kind}
@@ -238,12 +229,7 @@ export function parseConvoView(
primaryMember: owner, primaryMember: owner,
members: convoView.members as Array<GroupConvoMember>, members: convoView.members as Array<GroupConvoMember>,
} }
} else if ( } else if (bsky.isType(chat.bsky.convo.defs.directConvo, convoView.kind)) {
bsky.dangerousIsType<ChatBskyConvoDefs.DirectConvo>(
convoView.kind,
ChatBskyConvoDefs.isDirectConvo,
)
) {
const otherUser = convoView.members.find(m => m.did !== ownDid) const otherUser = convoView.members.find(m => m.did !== ownDid)
if (!otherUser) { if (!otherUser) {
@@ -286,7 +272,7 @@ export function getConvoReportSubject(
const lastMessage = convo.view.lastMessage const lastMessage = convo.view.lastMessage
const reportableMessage = const reportableMessage =
ChatBskyConvoDefs.isMessageView(lastMessage) && bsky.isType(chat.bsky.convo.defs.messageView, lastMessage) &&
lastMessage.sender?.did !== ownDid lastMessage.sender?.did !== ownDid
? lastMessage ? lastMessage
: null : null
@@ -1,6 +1,7 @@
import {View} from 'react-native' import {View} from 'react-native'
import {AppBskyEmbedVideo} from '@atproto/api'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {type FeedPostSliceItem} from '#/state/queries/post-feed' import {type FeedPostSliceItem} from '#/state/queries/post-feed'
import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types' import {type VideoFeedSourceContext} from '#/screens/VideoFeed/types'
import {atoms as a, useGutters} from '#/alf' import {atoms as a, useGutters} from '#/alf'
@@ -21,7 +22,7 @@ export function PostFeedVideoGridRow({
const ax = useAnalytics() const ax = useAnalytics()
const gutters = useGutters(['base', 'base', 0, 'base']) const gutters = useGutters(['base', 'base', 0, 'base'])
const posts = slices const posts = slices
.filter(slice => AppBskyEmbedVideo.isView(slice.post.embed)) .filter(slice => bsky.isType(app.bsky.embed.video.view, slice.post.embed))
.map(slice => ({ .map(slice => ({
post: slice.post, post: slice.post,
moderation: slice.moderation, moderation: slice.moderation,
+2 -2
View File
@@ -5,12 +5,12 @@ import Animated, {
useAnimatedRef, useAnimatedRef,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {type AppBskyEmbedImages} from '@atproto/api'
import {utils} from '@bsky.app/alf' import {utils} from '@bsky.app/alf'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {type Dimensions} from '#/lib/media/types' import {type Dimensions} from '#/lib/media/types'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, useTheme, web} from '#/alf' import {atoms as a, useTheme, web} from '#/alf'
@@ -71,7 +71,7 @@ export function AutoSizedImage({
onContainerRef, onContainerRef,
onDimsChange, onDimsChange,
}: { }: {
image: AppBskyEmbedImages.ViewImage image: app.bsky.embed.images.ViewImage
crop?: 'none' | 'square' | 'constrained' crop?: 'none' | 'square' | 'constrained'
onPress?: ( onPress?: (
containerRef: AnimatedRef<any>, containerRef: AnimatedRef<any>,
+3 -3
View File
@@ -14,11 +14,11 @@ import Animated, {
useAnimatedRef, useAnimatedRef,
} from 'react-native-reanimated' } from 'react-native-reanimated'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {type AppBskyEmbedImages} from '@atproto/api'
import {utils} from '@bsky.app/alf' import {utils} from '@bsky.app/alf'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import {app} from '#/lexicons'
import {type Dimensions} from '#/lib/media/types' import {type Dimensions} from '#/lib/media/types'
import {mergeRefs} from '#/lib/merge-refs' import {mergeRefs} from '#/lib/merge-refs'
import {useA11y} from '#/state/a11y' import {useA11y} from '#/state/a11y'
@@ -46,7 +46,7 @@ export * from './const'
export * from './maybeApplyGalleryOffsetStyles' export * from './maybeApplyGalleryOffsetStyles'
interface GalleryProps { interface GalleryProps {
images: AppBskyEmbedImages.ViewImage[] images: app.bsky.embed.images.ViewImage[]
onPress?: ( onPress?: (
index: number, index: number,
containerRefs: AnimatedRef<any>[], containerRefs: AnimatedRef<any>[],
@@ -404,7 +404,7 @@ function GalleryImage({
onPreviewPress, onPreviewPress,
}: { }: {
contentHeight: number contentHeight: number
image: AppBskyEmbedImages.ViewImage image: app.bsky.embed.images.ViewImage
index: number index: number
imageCount: number imageCount: number
onWidthChange: (index: number, width: number) => void onWidthChange: (index: number, width: number) => void
@@ -1,12 +1,6 @@
import {
AppBskyEmbedGallery,
AppBskyEmbedImages,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
AppBskyFeedPost,
} from '@atproto/api'
import {type ModerationCause, type ModerationUI} from '@bsky.app/sdk/moderation' import {type ModerationCause, type ModerationUI} from '@bsky.app/sdk/moderation'
import {app} from '#/lexicons'
import {unique} from '#/lib/moderation' import {unique} from '#/lib/moderation'
import {type AppModerationCause} from '#/components/Pills' import {type AppModerationCause} from '#/components/Pills'
import {Features, features} from '#/analytics/features' import {Features, features} from '#/analytics/features'
@@ -22,17 +16,12 @@ export function maybeApplyGalleryOffsetStyles(
modui, modui,
additionalCauses, additionalCauses,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
modui: ModerationUI modui: ModerationUI
additionalCauses?: ModerationCause[] | AppModerationCause[] additionalCauses?: ModerationCause[] | AppModerationCause[]
}, },
) { ) {
if ( if (!bsky.isType(app.bsky.feed.post, post.record)) {
!bsky.dangerousIsType<AppBskyFeedPost.Record>(
post.record,
AppBskyFeedPost.isRecord,
)
) {
return return
} }
@@ -47,24 +36,11 @@ export function maybeApplyGalleryOffsetStyles(
* First check if we even have images * First check if we even have images
*/ */
const embed = post.record.embed const embed = post.record.embed
const isImageEmbed = const isImageEmbed = embed && bsky.isType(app.bsky.embed.images.main, embed)
embed &&
bsky.dangerousIsType<AppBskyEmbedImages.Main>(
embed,
AppBskyEmbedImages.isMain,
)
const isGalleryEmbed = const isGalleryEmbed =
embed && embed && bsky.isType(app.bsky.embed.gallery.main, embed)
bsky.dangerousIsType<AppBskyEmbedGallery.Main>(
embed,
AppBskyEmbedGallery.isMain,
)
const isRecordWithMedia = const isRecordWithMedia =
embed && embed && bsky.isType(app.bsky.embed.recordWithMedia.main, embed)
bsky.dangerousIsType<AppBskyEmbedRecordWithMedia.Main>(
embed,
AppBskyEmbedRecordWithMedia.isMain,
)
let hasImages = false let hasImages = false
if (isImageEmbed) { if (isImageEmbed) {
if (!isPostGalleryEmbedEnabled) return if (!isPostGalleryEmbedEnabled) return
@@ -78,23 +54,13 @@ export function maybeApplyGalleryOffsetStyles(
hasImages = true hasImages = true
} }
if (isRecordWithMedia) { if (isRecordWithMedia) {
if ( if (bsky.isType(app.bsky.embed.images.main, embed.media)) {
bsky.dangerousIsType<AppBskyEmbedImages.Main>(
embed.media,
AppBskyEmbedImages.isMain,
)
) {
if (!isPostGalleryEmbedEnabled) return if (!isPostGalleryEmbedEnabled) return
// one image, not a gallery // one image, not a gallery
if (embed.media.images.length === 1) return if (embed.media.images.length === 1) return
hasImages = true hasImages = true
} }
if ( if (bsky.isType(app.bsky.embed.gallery.main, embed.media)) {
bsky.dangerousIsType<AppBskyEmbedGallery.Main>(
embed.media,
AppBskyEmbedGallery.isMain,
)
) {
// single (or empty) gallery - no offset needed // single (or empty) gallery - no offset needed
if (embed.media.items.length <= 1) return if (embed.media.items.length <= 1) return
hasImages = true hasImages = true
+3 -3
View File
@@ -1,15 +1,15 @@
import {useRef} from 'react' import {useRef} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated' import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated'
import {type AppBskyEmbedImages} from '@atproto/api'
import {app} from '#/lexicons'
import {atoms as a, useBreakpoints} from '#/alf' import {atoms as a, useBreakpoints} from '#/alf'
import {type Dimensions} from '#/components/Lightbox/types' import {type Dimensions} from '#/components/Lightbox/types'
import {type PostEmbedViewContext} from '#/components/Post/Embed/types' import {type PostEmbedViewContext} from '#/components/Post/Embed/types'
import {GalleryItem} from './ImageLayoutGridItem' import {GalleryItem} from './ImageLayoutGridItem'
interface ImageLayoutGridProps { interface ImageLayoutGridProps {
images: AppBskyEmbedImages.ViewImage[] images: app.bsky.embed.images.ViewImage[]
onPress?: ( onPress?: (
index: number, index: number,
containerRefs: AnimatedRef<any>[], containerRefs: AnimatedRef<any>[],
@@ -45,7 +45,7 @@ export function ImageLayoutGrid({
} }
interface ImageLayoutGridInnerProps { interface ImageLayoutGridInnerProps {
images: AppBskyEmbedImages.ViewImage[] images: app.bsky.embed.images.ViewImage[]
onPress?: ( onPress?: (
index: number, index: number,
containerRefs: AnimatedRef<any>[], containerRefs: AnimatedRef<any>[],
@@ -1,12 +1,12 @@
import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native' import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
import {type AnimatedRef} from 'react-native-reanimated' import {type AnimatedRef} from 'react-native-reanimated'
import {Image, type ImageStyle} from 'expo-image' import {Image, type ImageStyle} from 'expo-image'
import {type AppBskyEmbedImages} from '@atproto/api'
import {utils} from '@bsky.app/alf' import {utils} from '@bsky.app/alf'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {type Dimensions} from '#/lib/media/types' import {type Dimensions} from '#/lib/media/types'
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
import {atoms as a, tokens, useTheme} from '#/alf' import {atoms as a, tokens, useTheme} from '#/alf'
@@ -18,7 +18,7 @@ import {Text} from '#/components/Typography'
type EventFunction = (index: number) => void type EventFunction = (index: number) => void
interface Props { interface Props {
images: AppBskyEmbedImages.ViewImage[] images: app.bsky.embed.images.ViewImage[]
index: number index: number
onPress?: ( onPress?: (
index: number, index: number,
@@ -1,10 +1,10 @@
import {useEffect} from 'react' import {useEffect} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {ChatBskyGroupDefs} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro' import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name' import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
import {useCallOnce} from '#/lib/once' import {useCallOnce} from '#/lib/once'
@@ -163,7 +163,10 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
errorMessage = l`The member limit has been reached.` errorMessage = l`The member limit has been reached.`
const preview = data?.joinLinkPreviews[0] const preview = data?.joinLinkPreviews[0]
if ( if (
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) && bsky.isType(
chat.bsky.group.defs.joinLinkPreviewView,
preview,
) &&
preview.convo?.id preview.convo?.id
) { ) {
ax.metric('groupchat:join:memberLimitReached', { ax.metric('groupchat:join:memberLimitReached', {
@@ -254,7 +257,7 @@ function GroupChatJoinDialogContent({code}: {code?: string}) {
const joinLinkPreview = data.joinLinkPreviews[0] const joinLinkPreview = data.joinLinkPreviews[0]
if (!ChatBskyGroupDefs.isJoinLinkPreviewView(joinLinkPreview)) { if (!bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, joinLinkPreview)) {
return ( return (
<> <>
<View style={[a.py_lg, a.align_center]}> <View style={[a.py_lg, a.align_center]}>
@@ -1,8 +1,8 @@
import {useMemo} from 'react' import {useMemo} from 'react'
import {Pressable, View} from 'react-native' import {Pressable, View} from 'react-native'
import {type AppBskyUnspeccedDefs} from '@atproto/api'
import {Plural, Trans, useLingui} from '@lingui/react/macro' import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {moderateProfile} from '#/lib/moderation/subjects' import {moderateProfile} from '#/lib/moderation/subjects'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import { import {
@@ -183,7 +183,7 @@ function TrendRow({
recId, recId,
onPress, onPress,
}: ViewStyleProp & { }: ViewStyleProp & {
trend: AppBskyUnspeccedDefs.TrendView trend: app.bsky.unspecced.defs.TrendView
rank: number rank: number
feedSliceIndex: number feedSliceIndex: number
recId?: string recId?: string
@@ -291,7 +291,7 @@ function TrendingTopicRowSkeleton({rank}: {rank: number}) {
} }
function useModerateTrendingActors( function useModerateTrendingActors(
actors: AppBskyUnspeccedDefs.TrendView['actors'], actors: app.bsky.unspecced.defs.TrendView['actors'],
) { ) {
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
@@ -1,9 +1,11 @@
import {useCallback, useEffect, useMemo} from 'react' import {useCallback, useEffect, useMemo} from 'react'
import {ScrollView, View} from 'react-native' import {ScrollView, View} from 'react-native'
import {AppBskyEmbedVideo, AtUri} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {VIDEO_FEED_URI} from '#/lib/constants' import {VIDEO_FEED_URI} from '#/lib/constants'
import {makeCustomFeedLink} from '#/lib/routes/links' import {makeCustomFeedLink} from '#/lib/routes/links'
import {useTrendingSettingsApi} from '#/state/preferences/trending' import {useTrendingSettingsApi} from '#/state/preferences/trending'
@@ -153,7 +155,7 @@ function VideoCards({
.flatMap(page => page.slices) .flatMap(page => page.slices)
.map(slice => slice.items[0]) .map(slice => slice.items[0])
.filter(Boolean) .filter(Boolean)
.filter(item => AppBskyEmbedVideo.isView(item.post.embed)) .filter(item => bsky.isType(app.bsky.embed.video.view, item.post.embed))
.slice(0, 8) .slice(0, 8)
}, [data]) }, [data])
+3 -4
View File
@@ -1,6 +1,5 @@
import {useState} from 'react' import {useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api'
import {XrpcResponseError} from '@atproto/lex' import {XrpcResponseError} from '@atproto/lex'
import {type AtUriString, type DidString} from '@atproto/syntax' import {type AtUriString, type DidString} from '@atproto/syntax'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
@@ -23,14 +22,14 @@ import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast' import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {IS_ANDROID} from '#/env' import {IS_ANDROID} from '#/env'
import {com} from '#/lexicons' import {com, tools} from '#/lexicons'
export function AppealForm({ export function AppealForm({
label, label,
control, control,
onPressBack, onPressBack,
}: { }: {
label: ComAtprotoLabelDefs.Label label: com.atproto.label.defs.Label
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
onPressBack: () => void onPressBack: () => void
}) { }) {
@@ -51,7 +50,7 @@ export function AppealForm({
await client.call( await client.call(
com.atproto.moderation.createReport, com.atproto.moderation.createReport,
{ {
reasonType: ToolsOzoneReportDefs.REASONAPPEAL, reasonType: tools.ozone.report.defs.reasonAppeal,
/* /*
* `useLabelSubject` derives one shape or the other from the label's * `useLabelSubject` derives one shape or the other from the label's
* `cid`: an at-uri plus cid for a record, or the label's `uri` reused * `cid`: an at-uri plus cid for a record, or the label's `uri` reused
+2 -3
View File
@@ -1,6 +1,5 @@
import {useState} from 'react' import {useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type ChatBskyConvoDefs} from '@atproto/api'
import {Trans, useLingui} from '@lingui/react/macro' import {Trans, useLingui} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
@@ -27,7 +26,7 @@ import {Text} from '#/components/Typography'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
import {type AnyProfileView} from '#/types/bsky/profile' import {type AnyProfileView} from '#/types/bsky/profile'
type Item = ChatBskyConvoDefs.ConvoView type Item = chat.bsky.convo.defs.ConvoView
type BlockDialogProps = { type BlockDialogProps = {
control: DialogControlProps control: DialogControlProps
@@ -252,7 +251,7 @@ function MutualGroupChat({
onOptimisticallyRemoveConvo, onOptimisticallyRemoveConvo,
onRestoreConvo, onRestoreConvo,
}: { }: {
view: ChatBskyConvoDefs.ConvoView view: chat.bsky.convo.defs.ConvoView
profileDid: string profileDid: string
currentConvoId?: string currentConvoId?: string
onOptimisticallyRemoveConvo: (convoId: string) => void onOptimisticallyRemoveConvo: (convoId: string) => void
+2 -2
View File
@@ -1,9 +1,9 @@
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {type ComAtprotoLabelDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Plural} from '@lingui/react/macro' import {Plural} from '@lingui/react/macro'
import {com} from '#/lexicons'
import {filterUserFacingLabels} from '#/lib/moderation' import {filterUserFacingLabels} from '#/lib/moderation'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
@@ -24,7 +24,7 @@ export function LabelsOnMe({
size, size,
style, style,
}: { }: {
labels: ComAtprotoLabelDefs.Label[] | undefined labels: com.atproto.label.defs.Label[] | undefined
size?: ButtonSize size?: ButtonSize
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
}) { }) {
@@ -1,10 +1,10 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type ComAtprotoLabelDefs} from '@atproto/api'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {com} from '#/lexicons'
import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo'
import {useLabelInfo} from '#/lib/moderation/useLabelInfo' import {useLabelInfo} from '#/lib/moderation/useLabelInfo'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
@@ -23,7 +23,7 @@ export {useDialogControl as useLabelsOnMeDialogControl} from '#/components/Dialo
export interface LabelsOnMeDialogProps { export interface LabelsOnMeDialogProps {
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
labels: ComAtprotoLabelDefs.Label[] labels: com.atproto.label.defs.Label[]
/** /**
* Whether the labels are being shown on the user's account or on a post. * Whether the labels are being shown on the user's account or on a post.
* With `content`, the list may include account-level labels, which get a * With `content`, the list may include account-level labels, which get a
@@ -47,7 +47,7 @@ function LabelsOnMeDialogInner(props: LabelsOnMeDialogProps) {
const {_} = useLingui() const {_} = useLingui()
const {currentAccount} = useSession() const {currentAccount} = useSession()
const [appealingLabel, setAppealingLabel] = useState< const [appealingLabel, setAppealingLabel] = useState<
ComAtprotoLabelDefs.Label | undefined com.atproto.label.defs.Label | undefined
>(undefined) >(undefined)
const {labels} = props const {labels} = props
const isAccount = props.type === 'account' const isAccount = props.type === 'account'
@@ -118,7 +118,7 @@ function Label({
control, control,
onPressAppeal, onPressAppeal,
}: { }: {
label: ComAtprotoLabelDefs.Label label: com.atproto.label.defs.Label
isSelfLabel: boolean isSelfLabel: boolean
/** /**
* Call out that the label applies to the whole account, for contexts that * Call out that the label applies to the whole account, for contexts that
@@ -126,7 +126,7 @@ function Label({
*/ */
showAccountCallout?: boolean showAccountCallout?: boolean
control: Dialog.DialogOuterProps['control'] control: Dialog.DialogOuterProps['control']
onPressAppeal: (label: ComAtprotoLabelDefs.Label) => void onPressAppeal: (label: com.atproto.label.defs.Label) => void
}) { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
+4 -4
View File
@@ -1,5 +1,4 @@
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {type AppBskyFeedDefs, type ComAtprotoLabelDefs} from '@atproto/api'
import {type ModerationCause, type ModerationUI} from '@bsky.app/sdk/moderation' import {type ModerationCause, type ModerationUI} from '@bsky.app/sdk/moderation'
import {plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
@@ -9,6 +8,7 @@ import {
getModerationCauseKey, getModerationCauseKey,
unique, unique,
} from '#/lib/moderation' } from '#/lib/moderation'
import {app, com} from '#/lexicons'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {atoms as a} from '#/alf' import {atoms as a} from '#/alf'
import { import {
@@ -24,7 +24,7 @@ export function PostAlerts({
style, style,
additionalCauses, additionalCauses,
}: { }: {
post?: AppBskyFeedDefs.PostView post?: app.bsky.feed.defs.PostView
modui: ModerationUI modui: ModerationUI
/** /**
* Expanded views (e.g. the thread anchor post) render larger pills and * Expanded views (e.g. the thread anchor post) render larger pills and
@@ -48,7 +48,7 @@ export function PostAlerts({
* author's only entry point to appeal labels on their content. * author's only entry point to appeal labels on their content.
*/ */
const isOwnPost = !!post && post.author.did === currentAccount?.did const isOwnPost = !!post && post.author.did === currentAccount?.did
const allLabels: ComAtprotoLabelDefs.Label[] = const allLabels: com.atproto.label.defs.Label[] =
isOwnPost && view === 'expanded' isOwnPost && view === 'expanded'
? [ ? [
...(post.labels ?? []), ...(post.labels ?? []),
@@ -134,7 +134,7 @@ function AdditionalLabels({
size, size,
hasPrecedingPills, hasPrecedingPills,
}: { }: {
labels: ComAtprotoLabelDefs.Label[] labels: com.atproto.label.defs.Label[]
size?: Pills.CommonProps['size'] size?: Pills.CommonProps['size']
/** /**
* The compact "+n" syntax only makes sense as a continuation of other * The compact "+n" syntax only makes sense as a continuation of other
+2 -2
View File
@@ -7,13 +7,13 @@ import {
View, View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ModerationCause, type ModerationUI} from '@bsky.app/sdk/moderation' import {type ModerationCause, type ModerationUI} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {app} from '#/lexicons'
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
import {addStyle} from '#/lib/styles' import {addStyle} from '#/lib/styles'
import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache' import {unstableCacheProfileView} from '#/state/queries/unstable-profile-cache'
@@ -31,7 +31,7 @@ interface Props extends React.ComponentProps<typeof Link> {
iconSize: number iconSize: number
iconStyles: StyleProp<ViewStyle> iconStyles: StyleProp<ViewStyle>
modui: ModerationUI modui: ModerationUI
profile: AppBskyActorDefs.ProfileViewBasic profile: app.bsky.actor.defs.ProfileViewBasic
interpretFilterAsBlur?: boolean interpretFilterAsBlur?: boolean
hiderStyle?: StyleProp<ViewStyle> hiderStyle?: StyleProp<ViewStyle>
} }
+125 -74
View File
@@ -1,9 +1,5 @@
import {applicationId} from 'expo-application' import {applicationId} from 'expo-application'
import { import {com, tools} from '#/lexicons'
ComAtprotoModerationDefs as RootReportDefs,
ToolsOzoneReportDefs as OzoneReportDefs,
} from '@atproto/api'
import {type ParsedReportSubject} from '#/components/moderation/ReportDialog/types' import {type ParsedReportSubject} from '#/components/moderation/ReportDialog/types'
import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env' import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env'
@@ -31,59 +27,96 @@ export const NEW_TO_OLD_REASON_MAPPING: Record<string, string> = {}
* @see https://github.com/bluesky-social/atproto/blob/4c15fb47cec26060bff2e710e95869a90c9d7fdd/packages/ozone/src/mod-service/profile.ts#L16-L64 * @see https://github.com/bluesky-social/atproto/blob/4c15fb47cec26060bff2e710e95869a90c9d7fdd/packages/ozone/src/mod-service/profile.ts#L16-L64
*/ */
export const NEW_TO_OLD_REASONS_MAP: Record< export const NEW_TO_OLD_REASONS_MAP: Record<
OzoneReportDefs.ReasonType, tools.ozone.report.defs.ReasonType,
RootReportDefs.ReasonType com.atproto.moderation.defs.ReasonType
> = { > = {
[OzoneReportDefs.REASONAPPEAL]: RootReportDefs.REASONAPPEAL, [tools.ozone.report.defs.reasonAppeal]:
[OzoneReportDefs.REASONOTHER]: RootReportDefs.REASONOTHER, com.atproto.moderation.defs.reasonAppeal,
[tools.ozone.report.defs.reasonOther]:
com.atproto.moderation.defs.reasonOther,
[OzoneReportDefs.REASONVIOLENCEANIMAL]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonViolenceAnimal]:
[OzoneReportDefs.REASONVIOLENCETHREATS]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONVIOLENCEGRAPHICCONTENT]: [tools.ozone.report.defs.reasonViolenceThreats]:
RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONVIOLENCEGLORIFICATION]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonViolenceGraphicContent]:
[OzoneReportDefs.REASONVIOLENCEEXTREMISTCONTENT]: com.atproto.moderation.defs.reasonViolation,
RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonViolenceGlorification]:
[OzoneReportDefs.REASONVIOLENCETRAFFICKING]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONVIOLENCEOTHER]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonViolenceExtremistContent]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonViolenceTrafficking]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonViolenceOther]:
com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONSEXUALABUSECONTENT]: RootReportDefs.REASONSEXUAL, [tools.ozone.report.defs.reasonSexualAbuseContent]:
[OzoneReportDefs.REASONSEXUALNCII]: RootReportDefs.REASONSEXUAL, com.atproto.moderation.defs.reasonSexual,
[OzoneReportDefs.REASONSEXUALDEEPFAKE]: RootReportDefs.REASONSEXUAL, [tools.ozone.report.defs.reasonSexualNCII]:
[OzoneReportDefs.REASONSEXUALANIMAL]: RootReportDefs.REASONSEXUAL, com.atproto.moderation.defs.reasonSexual,
[OzoneReportDefs.REASONSEXUALUNLABELED]: RootReportDefs.REASONSEXUAL, [tools.ozone.report.defs.reasonSexualDeepfake]:
[OzoneReportDefs.REASONSEXUALOTHER]: RootReportDefs.REASONSEXUAL, com.atproto.moderation.defs.reasonSexual,
[tools.ozone.report.defs.reasonSexualAnimal]:
com.atproto.moderation.defs.reasonSexual,
[tools.ozone.report.defs.reasonSexualUnlabeled]:
com.atproto.moderation.defs.reasonSexual,
[tools.ozone.report.defs.reasonSexualOther]:
com.atproto.moderation.defs.reasonSexual,
[OzoneReportDefs.REASONCHILDSAFETYCSAM]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonChildSafetyCSAM]:
[OzoneReportDefs.REASONCHILDSAFETYGROOM]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONCHILDSAFETYPRIVACY]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonChildSafetyGroom]:
[OzoneReportDefs.REASONCHILDSAFETYHARASSMENT]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONCHILDSAFETYOTHER]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonChildSafetyPrivacy]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonChildSafetyHarassment]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonChildSafetyOther]:
com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONHARASSMENTTROLL]: RootReportDefs.REASONRUDE, [tools.ozone.report.defs.reasonHarassmentTroll]:
[OzoneReportDefs.REASONHARASSMENTTARGETED]: RootReportDefs.REASONRUDE, com.atproto.moderation.defs.reasonRude,
[OzoneReportDefs.REASONHARASSMENTHATESPEECH]: RootReportDefs.REASONRUDE, [tools.ozone.report.defs.reasonHarassmentTargeted]:
[OzoneReportDefs.REASONHARASSMENTDOXXING]: RootReportDefs.REASONRUDE, com.atproto.moderation.defs.reasonRude,
[OzoneReportDefs.REASONHARASSMENTOTHER]: RootReportDefs.REASONRUDE, [tools.ozone.report.defs.reasonHarassmentHateSpeech]:
com.atproto.moderation.defs.reasonRude,
[tools.ozone.report.defs.reasonHarassmentDoxxing]:
com.atproto.moderation.defs.reasonRude,
[tools.ozone.report.defs.reasonHarassmentOther]:
com.atproto.moderation.defs.reasonRude,
[OzoneReportDefs.REASONMISLEADINGBOT]: RootReportDefs.REASONMISLEADING, [tools.ozone.report.defs.reasonMisleadingBot]:
[OzoneReportDefs.REASONMISLEADINGIMPERSONATION]: com.atproto.moderation.defs.reasonMisleading,
RootReportDefs.REASONMISLEADING, [tools.ozone.report.defs.reasonMisleadingImpersonation]:
[OzoneReportDefs.REASONMISLEADINGSPAM]: RootReportDefs.REASONSPAM, com.atproto.moderation.defs.reasonMisleading,
[OzoneReportDefs.REASONMISLEADINGSCAM]: RootReportDefs.REASONMISLEADING, [tools.ozone.report.defs.reasonMisleadingSpam]:
[OzoneReportDefs.REASONMISLEADINGELECTIONS]: RootReportDefs.REASONMISLEADING, com.atproto.moderation.defs.reasonSpam,
[OzoneReportDefs.REASONMISLEADINGOTHER]: RootReportDefs.REASONMISLEADING, [tools.ozone.report.defs.reasonMisleadingScam]:
com.atproto.moderation.defs.reasonMisleading,
[tools.ozone.report.defs.reasonMisleadingElections]:
com.atproto.moderation.defs.reasonMisleading,
[tools.ozone.report.defs.reasonMisleadingOther]:
com.atproto.moderation.defs.reasonMisleading,
[OzoneReportDefs.REASONRULESITESECURITY]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonRuleSiteSecurity]:
[OzoneReportDefs.REASONRULEPROHIBITEDSALES]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONRULEBANEVASION]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonRuleProhibitedSales]:
[OzoneReportDefs.REASONRULEOTHER]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonRuleBanEvasion]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonRuleOther]:
com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONSELFHARMCONTENT]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonSelfHarmContent]:
[OzoneReportDefs.REASONSELFHARMED]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONSELFHARMSTUNTS]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonSelfHarmED]:
[OzoneReportDefs.REASONSELFHARMSUBSTANCES]: RootReportDefs.REASONVIOLATION, com.atproto.moderation.defs.reasonViolation,
[OzoneReportDefs.REASONSELFHARMOTHER]: RootReportDefs.REASONVIOLATION, [tools.ozone.report.defs.reasonSelfHarmStunts]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonSelfHarmSubstances]:
com.atproto.moderation.defs.reasonViolation,
[tools.ozone.report.defs.reasonSelfHarmOther]:
com.atproto.moderation.defs.reasonViolation,
} }
/** /**
@@ -91,42 +124,60 @@ export const NEW_TO_OLD_REASONS_MAP: Record<
* @see https://github.com/bluesky-social/proposals/tree/main/0009-mod-report-granularity#backwards-compatibility * @see https://github.com/bluesky-social/proposals/tree/main/0009-mod-report-granularity#backwards-compatibility
*/ */
export const OLD_TO_NEW_REASONS_MAP: Record< export const OLD_TO_NEW_REASONS_MAP: Record<
Exclude<RootReportDefs.ReasonType, OzoneReportDefs.ReasonType>, Exclude<
OzoneReportDefs.ReasonType com.atproto.moderation.defs.ReasonType,
tools.ozone.report.defs.ReasonType
>,
tools.ozone.report.defs.ReasonType
> = { > = {
[RootReportDefs.REASONSPAM]: [OzoneReportDefs.REASONMISLEADINGSPAM], [com.atproto.moderation.defs.reasonSpam]: [
[RootReportDefs.REASONVIOLATION]: [OzoneReportDefs.REASONRULEOTHER], tools.ozone.report.defs.reasonMisleadingSpam,
[RootReportDefs.REASONMISLEADING]: [OzoneReportDefs.REASONMISLEADINGOTHER], ],
[RootReportDefs.REASONSEXUAL]: [OzoneReportDefs.REASONSEXUALUNLABELED], [com.atproto.moderation.defs.reasonViolation]: [
[RootReportDefs.REASONRUDE]: [OzoneReportDefs.REASONHARASSMENTOTHER], tools.ozone.report.defs.reasonRuleOther,
[RootReportDefs.REASONOTHER]: [OzoneReportDefs.REASONOTHER], ],
[RootReportDefs.REASONAPPEAL]: [OzoneReportDefs.REASONAPPEAL], [com.atproto.moderation.defs.reasonMisleading]: [
tools.ozone.report.defs.reasonMisleadingOther,
],
[com.atproto.moderation.defs.reasonSexual]: [
tools.ozone.report.defs.reasonSexualUnlabeled,
],
[com.atproto.moderation.defs.reasonRude]: [
tools.ozone.report.defs.reasonHarassmentOther,
],
[com.atproto.moderation.defs.reasonOther]: [
tools.ozone.report.defs.reasonOther,
],
[com.atproto.moderation.defs.reasonAppeal]: [
tools.ozone.report.defs.reasonAppeal,
],
} }
/** /**
* Set of report reasons that should optionally include additional details from * Set of report reasons that should optionally include additional details from
* the reporter. * the reporter.
*/ */
export const OTHER_REPORT_REASONS: Set<OzoneReportDefs.ReasonType> = new Set([ export const OTHER_REPORT_REASONS: Set<tools.ozone.report.defs.ReasonType> =
OzoneReportDefs.REASONVIOLENCEOTHER, new Set([
OzoneReportDefs.REASONSEXUALOTHER, tools.ozone.report.defs.reasonViolenceOther,
OzoneReportDefs.REASONCHILDSAFETYOTHER, tools.ozone.report.defs.reasonSexualOther,
OzoneReportDefs.REASONHARASSMENTOTHER, tools.ozone.report.defs.reasonChildSafetyOther,
OzoneReportDefs.REASONMISLEADINGOTHER, tools.ozone.report.defs.reasonHarassmentOther,
OzoneReportDefs.REASONRULEOTHER, tools.ozone.report.defs.reasonMisleadingOther,
OzoneReportDefs.REASONSELFHARMOTHER, tools.ozone.report.defs.reasonRuleOther,
OzoneReportDefs.REASONOTHER, tools.ozone.report.defs.reasonSelfHarmOther,
]) tools.ozone.report.defs.reasonOther,
])
/** /**
* Set of report reasons that should only be sent to Bluesky's moderation service. * Set of report reasons that should only be sent to Bluesky's moderation service.
*/ */
export const BSKY_LABELER_ONLY_REPORT_REASONS: Set<OzoneReportDefs.ReasonType> = export const BSKY_LABELER_ONLY_REPORT_REASONS: Set<tools.ozone.report.defs.ReasonType> =
new Set([ new Set([
OzoneReportDefs.REASONCHILDSAFETYCSAM, tools.ozone.report.defs.reasonChildSafetyCSAM,
OzoneReportDefs.REASONCHILDSAFETYGROOM, tools.ozone.report.defs.reasonChildSafetyGroom,
OzoneReportDefs.REASONCHILDSAFETYOTHER, tools.ozone.report.defs.reasonChildSafetyOther,
OzoneReportDefs.REASONVIOLENCEEXTREMISTCONTENT, tools.ozone.report.defs.reasonViolenceExtremistContent,
]) ])
/** /**

Some files were not shown because too many files have changed in this diff Show More