flip the legacy view 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:43 +03:00
parent 94ed6c7f0f
commit 6792677be8
39 changed files with 466 additions and 289 deletions
+8 -13
View File
@@ -44,20 +44,15 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {scheduleOnUI} from 'react-native-worklets' import {scheduleOnUI} from 'react-native-worklets'
import * as FileSystem from 'expo-file-system' import * as FileSystem from 'expo-file-system'
import {type ImagePickerAsset} from 'expo-image-picker' import {type ImagePickerAsset} from 'expo-image-picker'
import {
AppBskyDraftCreateDraft,
AppBskyUnspeccedDefs,
AtUri,
ChatBskyGroupDefs,
} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type AtUriString} from '@atproto/syntax' import {type AtUriString, AtUri} from '@atproto/syntax'
import {type RichText} from '@bsky.app/sdk/richtext' import {type RichText} 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 {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {useQueries, useQueryClient} from '@tanstack/react-query' import {useQueries, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import * as apilib from '#/lib/api/index' import * as apilib from '#/lib/api/index'
import {EmbeddingDisabledError} from '#/lib/api/resolve' import {EmbeddingDisabledError} from '#/lib/api/resolve'
import {useAppState} from '#/lib/appState' import {useAppState} from '#/lib/appState'
@@ -149,7 +144,7 @@ import {
IS_WEB_SAFARI, IS_WEB_SAFARI,
} from '#/env' } from '#/env'
import {type Gif} from '#/features/gifPicker/types' import {type Gif} from '#/features/gifPicker/types'
import {app} from '#/lexicons' import {app, chat} from '#/lexicons'
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
import { import {
draftToComposerPosts, draftToComposerPosts,
@@ -771,7 +766,7 @@ export const ComposePost = ({
const getDraftSaveError = useCallback( const getDraftSaveError = useCallback(
(e: unknown): string => { (e: unknown): string => {
if (e instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) { if (e instanceof app.bsky.draft.createDraft.DraftLimitReachedError) {
return l`You've reached the maximum number of drafts` return l`You've reached the maximum number of drafts`
} }
return l`Failed to save draft` return l`Failed to save draft`
@@ -1010,7 +1005,7 @@ export const ComposePost = ({
const hasUnavailableChatInvite = linkQueries.some( const hasUnavailableChatInvite = linkQueries.some(
q => q =>
q.data?.type === 'chat-invite' && q.data?.type === 'chat-invite' &&
!ChatBskyGroupDefs.isJoinLinkPreviewView(q.data.view), !bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, q.data.view),
) )
const canPost = const canPost =
@@ -1140,7 +1135,7 @@ export const ComposePost = ({
} }
if ( if (
!res.thread.every(p => !res.thread.every(p =>
AppBskyUnspeccedDefs.isThreadItemPost(p.value), bsky.isType(app.bsky.unspecced.defs.threadItemPost, p.value),
) )
) { ) {
throw new Error(`composer: app view returned non-post items`) throw new Error(`composer: app view returned non-post items`)
@@ -1212,7 +1207,7 @@ export const ComposePost = ({
const resolved = q.data const resolved = q.data
if ( if (
resolved?.type === 'chat-invite' && resolved?.type === 'chat-invite' &&
ChatBskyGroupDefs.isJoinLinkPreviewView(resolved.view) bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, resolved.view)
) { ) {
ax.metric('groupchat:inviteLink:shared', { ax.metric('groupchat:inviteLink:shared', {
convoId: resolved.view.convoId, convoId: resolved.view.convoId,
@@ -1251,7 +1246,7 @@ export const ComposePost = ({
void whenAppViewReady(client, initQuote.uri, res => { void whenAppViewReady(client, initQuote.uri, res => {
const anchor = res.thread.at(0) const anchor = res.thread.at(0)
if ( if (
AppBskyUnspeccedDefs.isThreadItemPost(anchor?.value) && bsky.isType(app.bsky.unspecced.defs.threadItemPost, anchor?.value) &&
anchor.value.post.quoteCount !== initQuote.quoteCount anchor.value.post.quoteCount !== initQuote.quoteCount
) { ) {
onPost?.(postUri) onPost?.(postUri)
+17 -22
View File
@@ -1,17 +1,12 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {LayoutAnimation, Pressable, View} from 'react-native' import {LayoutAnimation, Pressable, View} from 'react-native'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {
AppBskyEmbedGallery,
AppBskyEmbedImages,
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
AppBskyFeedPost,
} 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 * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
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'
import {type ComposerOptsPostRef} from '#/state/shell/composer' import {type ComposerOptsPostRef} from '#/state/shell/composer'
@@ -39,15 +34,15 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
const quoteEmbed = useMemo(() => { const quoteEmbed = useMemo(() => {
if ( if (
AppBskyEmbedRecord.isView(embed) && bsky.isType(app.bsky.embed.record.view, embed) &&
AppBskyEmbedRecord.isViewRecord(embed.record) && bsky.isType(app.bsky.embed.record.viewRecord, embed.record) &&
AppBskyFeedPost.isRecord(embed.record.value) bsky.isType(app.bsky.feed.post, embed.record.value)
) { ) {
return embed return embed
} else if ( } else if (
AppBskyEmbedRecordWithMedia.isView(embed) && bsky.isType(app.bsky.embed.recordWithMedia.view, embed) &&
AppBskyEmbedRecord.isViewRecord(embed.record.record) && bsky.isType(app.bsky.embed.record.viewRecord, embed.record.record) &&
AppBskyFeedPost.isRecord(embed.record.record.value) bsky.isType(app.bsky.feed.post, embed.record.record.value)
) { ) {
return embed.record return embed.record
} }
@@ -61,20 +56,20 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
: null : null
const {images, totalNumber} = useMemo(() => { const {images, totalNumber} = useMemo(() => {
if (AppBskyEmbedImages.isView(embed)) { if (bsky.isType(app.bsky.embed.images.view, embed)) {
return {images: embed.images, totalNumber: embed.images.length} return {images: embed.images, totalNumber: embed.images.length}
} else if (AppBskyEmbedGallery.isView(embed)) { } else if (bsky.isType(app.bsky.embed.gallery.view, embed)) {
return { return {
images: galleryItemsToImages(embed.items), images: galleryItemsToImages(embed.items),
totalNumber: embed.items.length, totalNumber: embed.items.length,
} }
} else if (AppBskyEmbedRecordWithMedia.isView(embed)) { } else if (bsky.isType(app.bsky.embed.recordWithMedia.view, embed)) {
if (AppBskyEmbedImages.isView(embed.media)) { if (bsky.isType(app.bsky.embed.images.view, embed.media)) {
return { return {
images: embed.media.images, images: embed.media.images,
totalNumber: embed.media.images.length, totalNumber: embed.media.images.length,
} }
} else if (AppBskyEmbedGallery.isView(embed.media)) { } else if (bsky.isType(app.bsky.embed.gallery.view, embed.media)) {
return { return {
images: galleryItemsToImages(embed.media.items), images: galleryItemsToImages(embed.media.items),
totalNumber: embed.media.items.length, totalNumber: embed.media.items.length,
@@ -145,12 +140,12 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
} }
function galleryItemsToImages( function galleryItemsToImages(
items: AppBskyEmbedGallery.View['items'], items: app.bsky.embed.gallery.View['items'],
): AppBskyEmbedImages.ViewImage[] { ): app.bsky.embed.images.ViewImage[] {
// The reply-to thumbnail only renders up to 4 tiles; slicing here keeps // The reply-to thumbnail only renders up to 4 tiles; slicing here keeps
// the existing layout switch valid for galleries up to 10 items. // the existing layout switch valid for galleries up to 10 items.
return items return items
.filter(AppBskyEmbedGallery.isViewImage) .filter(item => bsky.isType(app.bsky.embed.gallery.viewImage, item))
.slice(0, 4) .slice(0, 4)
.map(item => ({ .map(item => ({
thumb: item.thumbnail, thumb: item.thumbnail,
@@ -164,7 +159,7 @@ function ComposerReplyToImages({
images, images,
totalNumber, totalNumber,
}: { }: {
images: AppBskyEmbedImages.ViewImage[] images: app.bsky.embed.images.ViewImage[]
totalNumber: number totalNumber: number
}) { }) {
const t = useTheme() const t = useTheme()
+23 -19
View File
@@ -1,10 +1,12 @@
/** /**
* Type converters for Draft API - convert between ComposerState and server Draft types. * Type converters for Draft API - convert between ComposerState and server Draft types.
*/ */
import {AppBskyDraftDefs, AtUri} from '@atproto/api'
import {RichText} from '@bsky.app/sdk/richtext' import {RichText} from '@bsky.app/sdk/richtext'
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
import {AtUri} from '@atproto/syntax'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {type LinkResolvers, resolveLink} from '#/lib/api/resolve' import {type LinkResolvers, resolveLink} from '#/lib/api/resolve'
import {getDeviceName} from '#/lib/deviceName' import {getDeviceName} from '#/lib/deviceName'
import {getImageDim} from '#/lib/media/manip' import {getImageDim} from '#/lib/media/manip'
@@ -63,18 +65,18 @@ export async function composerStateToDraft(
clients: LinkResolvers, clients: LinkResolvers,
state: ComposerState, state: ComposerState,
): Promise<{ ): Promise<{
draft: AppBskyDraftDefs.Draft draft: app.bsky.draft.defs.Draft
localRefPaths: Map<string, string> localRefPaths: Map<string, string>
}> { }> {
const localRefPaths = new Map<string, string>() const localRefPaths = new Map<string, string>()
const posts: AppBskyDraftDefs.DraftPost[] = await Promise.all( const posts: app.bsky.draft.defs.DraftPost[] = await Promise.all(
state.thread.posts.map(post => { state.thread.posts.map(post => {
return postDraftToServerPost(clients, post, localRefPaths) return postDraftToServerPost(clients, post, localRefPaths)
}), }),
) )
const draft: AppBskyDraftDefs.Draft = { const draft: app.bsky.draft.defs.Draft = {
$type: 'app.bsky.draft.defs#draft', $type: 'app.bsky.draft.defs#draft',
deviceId: getDeviceId(), deviceId: getDeviceId(),
deviceName: getDeviceName().slice(0, 100), // max length of 100 in lex deviceName: getDeviceName().slice(0, 100), // max length of 100 in lex
@@ -99,8 +101,8 @@ async function postDraftToServerPost(
clients: LinkResolvers, clients: LinkResolvers,
post: PostDraft, post: PostDraft,
localRefPaths: Map<string, string>, localRefPaths: Map<string, string>,
): Promise<AppBskyDraftDefs.DraftPost> { ): Promise<app.bsky.draft.defs.DraftPost> {
const draftPost: AppBskyDraftDefs.DraftPost = { const draftPost: app.bsky.draft.defs.DraftPost = {
$type: 'app.bsky.draft.defs#draftPost', $type: 'app.bsky.draft.defs#draftPost',
text: post.richtext.text, text: post.richtext.text,
} }
@@ -176,7 +178,7 @@ async function postDraftToServerPost(
function serializeImages( function serializeImages(
images: ComposerImage[], images: ComposerImage[],
localRefPaths: Map<string, string>, localRefPaths: Map<string, string>,
): AppBskyDraftDefs.DraftEmbedGalleryItems { ): app.bsky.draft.defs.DraftEmbedGalleryItems {
return images.map(image => { return images.map(image => {
const sourcePath = image.transformed?.path || image.source.path const sourcePath = image.transformed?.path || image.source.path
// Reuse existing localRefPath if present (editing draft), otherwise generate new // Reuse existing localRefPath if present (editing draft), otherwise generate new
@@ -208,7 +210,7 @@ function serializeImages(
async function serializeVideo( async function serializeVideo(
videoState: VideoState, videoState: VideoState,
localRefPaths: Map<string, string>, localRefPaths: Map<string, string>,
): Promise<AppBskyDraftDefs.DraftEmbedVideo | undefined> { ): Promise<app.bsky.draft.defs.DraftEmbedVideo | undefined> {
// Only save videos that have been compressed (have a video file) // Only save videos that have been compressed (have a video file)
if (!videoState.video) { if (!videoState.video) {
return undefined return undefined
@@ -221,7 +223,7 @@ async function serializeVideo(
localRefPaths.set(localRefPath, videoState.video.uri) localRefPaths.set(localRefPath, videoState.video.uri)
// Read caption file contents as text // Read caption file contents as text
const captions: AppBskyDraftDefs.DraftEmbedCaption[] = [] const captions: app.bsky.draft.defs.DraftEmbedCaption[] = []
for (const caption of videoState.captions) { for (const caption of videoState.captions) {
if (caption.lang) { if (caption.lang) {
const content = await caption.file.text() const content = await caption.file.text()
@@ -252,7 +254,7 @@ function serializeGif(gifMedia: {
type: 'gif' type: 'gif'
gif: Gif gif: Gif
alt: string alt: string
}): AppBskyDraftDefs.DraftEmbedExternal | undefined { }): app.bsky.draft.defs.DraftEmbedExternal | undefined {
const gif = gifMedia.gif const gif = gifMedia.gif
const gifFormat = gif.media_formats.gif || gif.media_formats.tinygif const gifFormat = gif.media_formats.gif || gif.media_formats.tinygif
@@ -282,7 +284,7 @@ function serializeGif(gifMedia: {
* both the `embedImages` and `embedGallery` paths in draftToComposerPosts. * both the `embedImages` and `embedGallery` paths in draftToComposerPosts.
*/ */
async function restoreDraftImages( async function restoreDraftImages(
draftImages: AppBskyDraftDefs.DraftEmbedImage[], draftImages: app.bsky.draft.defs.DraftEmbedImage[],
loadedMedia: Map<string, string>, loadedMedia: Map<string, string>,
): Promise<ComposerImage[]> { ): Promise<ComposerImage[]> {
const imagePromises = draftImages.map(async img => { const imagePromises = draftImages.map(async img => {
@@ -338,7 +340,7 @@ export function draftViewToSummary({
view, view,
analytics, analytics,
}: { }: {
view: AppBskyDraftDefs.DraftView view: app.bsky.draft.defs.DraftView
analytics: AnalyticsContextType analytics: AnalyticsContextType
}): DraftSummary { }): DraftSummary {
const meta = { const meta = {
@@ -378,7 +380,7 @@ export function draftViewToSummary({
// Process gallery // Process gallery
if (post.embedGallery) { if (post.embedGallery) {
for (const item of post.embedGallery.items) { for (const item of post.embedGallery.items) {
if (!AppBskyDraftDefs.isDraftEmbedImage(item)) continue if (!bsky.isType(app.bsky.draft.defs.draftEmbedImage, item)) continue
meta.mediaCount++ meta.mediaCount++
meta.hasMedia = true meta.hasMedia = true
const exists = storage.mediaExists(item.localRef.path) const exists = storage.mediaExists(item.localRef.path)
@@ -494,7 +496,7 @@ function parseGifFromUrl(
* by initiating video processing for each entry. * by initiating video processing for each entry.
*/ */
export async function draftToComposerPosts( export async function draftToComposerPosts(
draft: AppBskyDraftDefs.Draft, draft: app.bsky.draft.defs.Draft,
loadedMedia: Map<string, string>, loadedMedia: Map<string, string>,
): Promise<{posts: PostDraft[]; restoredVideos: Map<number, RestoredVideo>}> { ): Promise<{posts: PostDraft[]; restoredVideos: Map<number, RestoredVideo>}> {
const restoredVideos = new Map<number, RestoredVideo>() const restoredVideos = new Map<number, RestoredVideo>()
@@ -523,8 +525,8 @@ export async function draftToComposerPosts(
) )
} }
if (post.embedGallery && post.embedGallery.items.length > 0) { if (post.embedGallery && post.embedGallery.items.length > 0) {
const galleryImages = post.embedGallery.items.filter( const galleryImages = post.embedGallery.items.filter(item =>
AppBskyDraftDefs.isDraftEmbedImage, bsky.isType(app.bsky.draft.defs.draftEmbedImage, item),
) )
restoredImages.push( restoredImages.push(
...(await restoreDraftImages(galleryImages, loadedMedia)), ...(await restoreDraftImages(galleryImages, loadedMedia)),
@@ -644,7 +646,7 @@ export async function draftToComposerPosts(
* Convert server threadgate rules back to UI settings. * Convert server threadgate rules back to UI settings.
*/ */
export function threadgateToUISettings( export function threadgateToUISettings(
threadgateAllow?: AppBskyDraftDefs.Draft['threadgateAllow'], threadgateAllow?: app.bsky.draft.defs.Draft['threadgateAllow'],
): Array<{type: string; list?: string}> { ): Array<{type: string; list?: string}> {
if (!threadgateAllow) { if (!threadgateAllow) {
return [] return []
@@ -678,7 +680,9 @@ export function threadgateToUISettings(
* Extract all localRef paths from a draft. * Extract all localRef paths from a draft.
* Used to identify which media files belong to a draft for cleanup. * Used to identify which media files belong to a draft for cleanup.
*/ */
export function extractLocalRefs(draft: AppBskyDraftDefs.Draft): Set<string> { export function extractLocalRefs(
draft: app.bsky.draft.defs.Draft,
): Set<string> {
const refs = new Set<string>() const refs = new Set<string>()
for (const post of draft.posts) { for (const post of draft.posts) {
if (post.embedImages) { if (post.embedImages) {
@@ -688,7 +692,7 @@ export function extractLocalRefs(draft: AppBskyDraftDefs.Draft): Set<string> {
} }
if (post.embedGallery) { if (post.embedGallery) {
for (const item of post.embedGallery.items) { for (const item of post.embedGallery.items) {
if (!AppBskyDraftDefs.isDraftEmbedImage(item)) continue if (!bsky.isType(app.bsky.draft.defs.draftEmbedImage, item)) continue
refs.add(item.localRef.path) refs.add(item.localRef.path)
} }
} }
@@ -1,10 +1,10 @@
import {AppBskyDraftDefs} from '@atproto/api'
import { import {
useInfiniteQuery, useInfiniteQuery,
useMutation, useMutation,
useQueryClient, useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {isNetworkError} from '#/lib/strings/errors' import {isNetworkError} from '#/lib/strings/errors'
import {matchXrpcError} from '#/lib/xrpc-error' import {matchXrpcError} from '#/lib/xrpc-error'
import {useAppviewClient, useChatClient} from '#/state/session' import {useAppviewClient, useChatClient} from '#/state/session'
@@ -52,7 +52,9 @@ export function useDraftsQuery() {
* Load a draft's local media for editing. * Load a draft's local media for editing.
* Takes the full Draft object (from DraftSummary) to avoid re-fetching. * Takes the full Draft object (from DraftSummary) to avoid re-fetching.
*/ */
export async function loadDraftMedia(draft: AppBskyDraftDefs.Draft): Promise<{ export async function loadDraftMedia(
draft: app.bsky.draft.defs.Draft,
): Promise<{
loadedMedia: Map<string, string> loadedMedia: Map<string, string>
}> { }> {
// Load local media files // Load local media files
@@ -81,7 +83,7 @@ export async function loadDraftMedia(draft: AppBskyDraftDefs.Draft): Promise<{
// Load gallery // Load gallery
if (post.embedGallery) { if (post.embedGallery) {
for (const item of post.embedGallery.items) { for (const item of post.embedGallery.items) {
if (!AppBskyDraftDefs.isDraftEmbedImage(item)) continue if (!bsky.isType(app.bsky.draft.defs.draftEmbedImage, item)) continue
try { try {
const url = await storage.loadMediaFromLocal(item.localRef.path) const url = await storage.loadMediaFromLocal(item.localRef.path)
loadedMedia.set(item.localRef.path, url) loadedMedia.set(item.localRef.path, url)
@@ -242,7 +244,7 @@ export function useDeleteDraftMutation() {
draftId, draftId,
}: { }: {
draftId: string draftId: string
draft: AppBskyDraftDefs.Draft draft: app.bsky.draft.defs.Draft
}) => { }) => {
// Delete from server first - if this fails, we keep local media for retry // Delete from server first - if this fails, we keep local media for retry
await client.call(app.bsky.draft.deleteDraft, {id: draftId}) await client.call(app.bsky.draft.deleteDraft, {id: draftId})
@@ -257,7 +259,8 @@ export function useDeleteDraftMutation() {
} }
if (post.embedGallery) { if (post.embedGallery) {
for (const item of post.embedGallery.items) { for (const item of post.embedGallery.items) {
if (!AppBskyDraftDefs.isDraftEmbedImage(item)) continue if (!bsky.isType(app.bsky.draft.defs.draftEmbedImage, item))
continue
await storage.deleteMediaFromLocal(item.localRef.path) await storage.deleteMediaFromLocal(item.localRef.path)
} }
} }
+2 -2
View File
@@ -1,8 +1,8 @@
import {app} from '#/lexicons'
/** /**
* Types for draft display and local media tracking. * Types for draft display and local media tracking.
* Server draft types come from @atproto/api. * Server draft types come from @atproto/api.
*/ */
import {type AppBskyDraftDefs} from '@atproto/api'
/** /**
* Reference to locally cached media file for display * Reference to locally cached media file for display
@@ -55,7 +55,7 @@ export type DraftSummary = {
/** ISO timestamp of last update */ /** ISO timestamp of last update */
updatedAt: string updatedAt: string
/** The full draft data from the server */ /** The full draft data from the server */
draft: AppBskyDraftDefs.Draft draft: app.bsky.draft.defs.Draft
/** All posts in the draft for full display */ /** All posts in the draft for full display */
posts: DraftPostDisplay[] posts: DraftPostDisplay[]
/** Metadata about the draft for display purposes */ /** Metadata about the draft for display purposes */
+4 -5
View File
@@ -1,5 +1,4 @@
import {type ImagePickerAsset} from 'expo-image-picker' import {type ImagePickerAsset} from 'expo-image-picker'
import {type AppBskyActorDefs, type AppBskyDraftDefs} from '@atproto/api'
import {type AtUriString, toDatetimeString} from '@atproto/syntax' import {type AtUriString, toDatetimeString} from '@atproto/syntax'
import {RichText} from '@bsky.app/sdk/richtext' import {RichText} from '@bsky.app/sdk/richtext'
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
@@ -141,8 +140,8 @@ export type ComposerAction =
type: 'restore_from_draft' type: 'restore_from_draft'
draftId: string draftId: string
posts: PostDraft[] posts: PostDraft[]
threadgateAllow: AppBskyDraftDefs.Draft['threadgateAllow'] threadgateAllow: app.bsky.draft.defs.Draft['threadgateAllow']
postgateEmbeddingRules: AppBskyDraftDefs.Draft['postgateEmbeddingRules'] postgateEmbeddingRules: app.bsky.draft.defs.Draft['postgateEmbeddingRules']
/** Map of localRefPath -> loaded media path/URL */ /** Map of localRefPath -> loaded media path/URL */
loadedMedia: Map<string, string> loadedMedia: Map<string, string>
@@ -152,7 +151,7 @@ export type ComposerAction =
| { | {
type: 'clear' type: 'clear'
initInteractionSettings: initInteractionSettings:
| AppBskyActorDefs.PostInteractionSettingsPref | app.bsky.actor.defs.PostInteractionSettingsPref
| undefined | undefined
} }
| { | {
@@ -632,7 +631,7 @@ export function createComposerState({
initImageUris: ComposerOpts['imageUris'] initImageUris: ComposerOpts['imageUris']
initQuoteUri: string | undefined initQuoteUri: string | undefined
initInteractionSettings: initInteractionSettings:
| AppBskyActorDefs.PostInteractionSettingsPref | app.bsky.actor.defs.PostInteractionSettingsPref
| undefined | undefined
}): ComposerState { }): ComposerState {
let media: ImagesMedia | GalleryMedia | undefined let media: ImagesMedia | GalleryMedia | undefined
@@ -1,8 +1,8 @@
import {View} from 'react-native' import {View} from 'react-native'
import Animated, {FadeInDown, FadeOut} from 'react-native-reanimated' import Animated, {FadeInDown, FadeOut} from 'react-native-reanimated'
import {type AppBskyActorDefs} from '@atproto/api'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {app} from '#/lexicons'
import {PressableScale} from '#/lib/custom-animations/PressableScale' import {PressableScale} from '#/lib/custom-animations/PressableScale'
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'
@@ -70,7 +70,7 @@ function AutocompleteProfileCard({
totalItems, totalItems,
onPress, onPress,
}: { }: {
profile: AppBskyActorDefs.ProfileViewBasic profile: app.bsky.actor.defs.ProfileViewBasic
itemIndex: number itemIndex: number
totalItems: number totalItems: number
onPress: () => void onPress: () => void
@@ -1,6 +1,5 @@
import {forwardRef, useEffect, useImperativeHandle, useState} from 'react' import {forwardRef, useEffect, useImperativeHandle, useState} from 'react'
import {Pressable, View} from 'react-native' import {Pressable, 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 {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {ReactRenderer} from '@tiptap/react' import {ReactRenderer} from '@tiptap/react'
@@ -11,6 +10,7 @@ import {
} from '@tiptap/suggestion' } from '@tiptap/suggestion'
import tippy, {type Instance as TippyInstance} from 'tippy.js' import tippy, {type Instance as TippyInstance} from 'tippy.js'
import {app} from '#/lexicons'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {type ActorAutocompleteFn} from '#/state/queries/actor-autocomplete' import {type ActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
@@ -205,7 +205,7 @@ function AutocompleteProfileCard({
onHover, onHover,
moderationOpts, moderationOpts,
}: { }: {
profile: AppBskyActorDefs.ProfileViewBasic profile: app.bsky.actor.defs.ProfileViewBasic
isSelected: boolean isSelected: boolean
onPress: () => void onPress: () => void
onHover: () => void onHover: () => void
@@ -14,7 +14,7 @@
* the facet-set. * the facet-set.
*/ */
import {URL_REGEX} from '@atproto/api' import {URL_REGEX} from '@bsky.app/sdk/richtext'
import {Mark} from '@tiptap/core' import {Mark} from '@tiptap/core'
import {type Node as ProsemirrorNode} from '@tiptap/pm/model' import {type Node as ProsemirrorNode} from '@tiptap/pm/model'
import {Plugin, PluginKey} from '@tiptap/pm/state' import {Plugin, PluginKey} from '@tiptap/pm/state'
@@ -18,7 +18,7 @@ import {
CASHTAG_REGEX, CASHTAG_REGEX,
TAG_REGEX, TAG_REGEX,
TRAILING_PUNCTUATION_REGEX, TRAILING_PUNCTUATION_REGEX,
} from '@atproto/api' } from '@bsky.app/sdk/richtext'
import {Mark} from '@tiptap/core' import {Mark} from '@tiptap/core'
import {type Node as ProsemirrorNode} from '@tiptap/pm/model' import {type Node as ProsemirrorNode} from '@tiptap/pm/model'
import {Plugin, PluginKey} from '@tiptap/pm/state' import {Plugin, PluginKey} from '@tiptap/pm/state'
+3 -3
View File
@@ -7,12 +7,12 @@ import {
useState, useState,
} from 'react' } from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {type AppBskyActorDefs, 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 {type NavigationProp, useNavigation} from '@react-navigation/native' import {type NavigationProp, useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {app} from '#/lexicons'
import {DISCOVER_FEED_URI, VIDEO_FEED_URIS} from '#/lib/constants' import {DISCOVER_FEED_URI, VIDEO_FEED_URIS} from '#/lib/constants'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers' import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers'
@@ -59,7 +59,7 @@ export function FeedPage({
isPageAdjacent: boolean isPageAdjacent: boolean
renderEmptyState: () => JSX.Element renderEmptyState: () => JSX.Element
renderEndOfFeed?: () => JSX.Element renderEndOfFeed?: () => JSX.Element
savedFeedConfig?: AppBskyActorDefs.SavedFeed savedFeedConfig?: app.bsky.actor.defs.SavedFeed
feedInfo: FeedSourceInfo feedInfo: FeedSourceInfo
}) { }) {
const ax = useAnalytics() const ax = useAnalytics()
@@ -77,7 +77,7 @@ export function FeedPage({
const isVideoFeed = useMemo(() => { const isVideoFeed = useMemo(() => {
const isBskyVideoFeed = VIDEO_FEED_URIS.includes(feedInfo.uri) const isBskyVideoFeed = VIDEO_FEED_URIS.includes(feedInfo.uri)
const feedIsVideoMode = const feedIsVideoMode =
feedInfo.contentMode === AppBskyFeedDefs.CONTENTMODEVIDEO feedInfo.contentMode === app.bsky.feed.defs.contentModeVideo
const _isVideoFeed = isBskyVideoFeed || feedIsVideoMode const _isVideoFeed = isBskyVideoFeed || feedIsVideoMode
return IS_NATIVE && _isVideoFeed return IS_NATIVE && _isVideoFeed
}, [feedInfo]) }, [feedInfo])
+7 -9
View File
@@ -1,14 +1,12 @@
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {
type $Typed,
AppBskyFeedDefs,
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 {Plural, Trans} from '@lingui/react/macro' import {Plural, Trans} from '@lingui/react/macro'
import {type $Typed} from '@atproto/lex'
import {AtUri} from '@atproto/syntax'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
import { import {
type FeedSourceInfo, type FeedSourceInfo,
@@ -27,8 +25,8 @@ import {MissingFeed} from './MissingFeed'
type FeedSourceCardProps = { type FeedSourceCardProps = {
feedUri: string feedUri: string
feedData?: feedData?:
| $Typed<AppBskyFeedDefs.GeneratorView> | $Typed<app.bsky.feed.defs.GeneratorView>
| $Typed<AppBskyGraphDefs.ListView> | $Typed<app.bsky.graph.defs.ListView>
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
showSaveBtn?: boolean showSaveBtn?: boolean
showDescription?: boolean showDescription?: boolean
@@ -46,7 +44,7 @@ export function FeedSourceCard({
}: FeedSourceCardProps) { }: FeedSourceCardProps) {
if (feedData) { if (feedData) {
let feed: FeedSourceInfo let feed: FeedSourceInfo
if (AppBskyFeedDefs.isGeneratorView(feedData)) { if (bsky.isType(app.bsky.feed.defs.generatorView, feedData)) {
feed = hydrateFeedGenerator(feedData) feed = hydrateFeedGenerator(feedData)
} else { } else {
feed = hydrateList(feedData) feed = hydrateList(feedData)
+1 -1
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 {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 {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 {getFeedTypeFromUri} from '#/state/queries/feed' import {getFeedTypeFromUri} from '#/state/queries/feed'
+2 -2
View File
@@ -6,11 +6,11 @@ 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 {app} from '#/lexicons'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
@@ -43,7 +43,7 @@ type Item =
| typeof LOAD_MORE_ERROR_ITEM | typeof LOAD_MORE_ERROR_ITEM
| { | {
kind: 'list_item' kind: 'list_item'
listItem: AppBskyGraphDefs.ListItemView listItem: app.bsky.graph.defs.ListItemView
} }
export function ListMembers({ export function ListMembers({
+5 -2
View File
@@ -7,10 +7,10 @@ import {
View, View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type AppBskyGraphDefs as GraphDefs} 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 {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {s} from '#/lib/styles' import {s} from '#/lib/styles'
@@ -38,7 +38,10 @@ export function MyLists({
filter: MyListsFilter filter: MyListsFilter
inline?: boolean inline?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element renderItem?: (
list: app.bsky.graph.defs.ListView,
index: number,
) => JSX.Element
testID?: string testID?: string
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
@@ -8,17 +8,8 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native' } from 'react-native'
import {
type AppBskyActorDefs,
type AppBskyFeedDefs,
AppBskyFeedPost,
type AppBskyGraphDefs,
AppBskyGraphFollow,
AppBskyGraphStarterpack,
AtUri,
} from '@atproto/api'
import {TID} from '@atproto/common-web' import {TID} from '@atproto/common-web'
import {type DidString} from '@atproto/syntax' import {type DidString, AtUri} from '@atproto/syntax'
import { import {
type ModerationDecision, type ModerationDecision,
type ModerationOpts, type ModerationOpts,
@@ -76,13 +67,13 @@ 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_WEB} from '#/env' import {IS_WEB} from '#/env'
import {chat} from '#/lexicons' import {app, chat} from '#/lexicons'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
const MAX_AUTHORS = 5 const MAX_AUTHORS = 5
interface Author { interface Author {
profile: AppBskyActorDefs.ProfileView profile: app.bsky.actor.defs.ProfileView
href: string href: string
moderation: ModerationDecision moderation: ModerationDecision
} }
@@ -195,10 +186,7 @@ let NotificationFeedItem = ({
if (item.type !== 'follow') return false if (item.type !== 'follow') return false
if ( if (
item.notification.author.viewer?.following && item.notification.author.viewer?.following &&
bsky.dangerousIsType<AppBskyGraphFollow.Record>( bsky.isType(app.bsky.graph.follow, item.notification.record)
item.notification.record,
AppBskyGraphFollow.isRecord,
)
) { ) {
let followingTimestamp let followingTimestamp
try { try {
@@ -734,7 +722,7 @@ export {NotificationFeedItem}
function FollowedViaStarterPack({ function FollowedViaStarterPack({
starterPack, starterPack,
}: { }: {
starterPack: AppBskyGraphDefs.StarterPackViewBasic starterPack: app.bsky.graph.defs.StarterPackViewBasic
}) { }) {
const t = useTheme() const t = useTheme()
const link = useStarterPackLink({view: starterPack}) const link = useStarterPackLink({view: starterPack})
@@ -768,12 +756,9 @@ function FollowedViaStarterPack({
} }
function getStarterPackName( function getStarterPackName(
starterPack: AppBskyGraphDefs.StarterPackViewBasic, starterPack: app.bsky.graph.defs.StarterPackViewBasic,
) { ) {
return bsky.dangerousIsType<AppBskyGraphStarterpack.Record>( return bsky.isType(app.bsky.graph.starterpack, starterPack.record)
starterPack.record,
AppBskyGraphStarterpack.isRecord,
)
? starterPack.record.name ? starterPack.record.name
: undefined : undefined
} }
@@ -807,7 +792,11 @@ function ExpandListPressable({
} }
} }
function FollowBackButton({profile}: {profile: AppBskyActorDefs.ProfileView}) { function FollowBackButton({
profile,
}: {
profile: app.bsky.actor.defs.ProfileView
}) {
const {t: l} = useLingui() const {t: l} = useLingui()
const {currentAccount, hasSession} = useSession() const {currentAccount, hasSession} = useSession()
const profileShadow = useProfileShadow(profile) const profileShadow = useProfileShadow(profile)
@@ -913,7 +902,7 @@ function FollowBackButton({profile}: {profile: AppBskyActorDefs.ProfileView}) {
) )
} }
function SayHelloBtn({profile}: {profile: AppBskyActorDefs.ProfileView}) { function SayHelloBtn({profile}: {profile: app.bsky.actor.defs.ProfileView}) {
const {t: l} = useLingui() const {t: l} = useLingui()
const client = useChatClient() const client = useChatClient()
const {currentAccount} = useSession() const {currentAccount} = useSession()
@@ -1147,15 +1136,9 @@ function ExpandedAuthorProfileCard({
) )
} }
function AdditionalPostText({post}: {post?: AppBskyFeedDefs.PostView}) { function AdditionalPostText({post}: {post?: app.bsky.feed.defs.PostView}) {
const t = useTheme() const t = useTheme()
if ( if (post && bsky.isType(app.bsky.feed.post, post?.record)) {
post &&
bsky.dangerousIsType<AppBskyFeedPost.Record>(
post?.record,
AppBskyFeedPost.isRecord,
)
) {
const text = post.record.text const text = post.record.text
return ( return (
+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
} }
+6 -9
View File
@@ -1,9 +1,9 @@
import {useCallback, useState} from 'react' import {useCallback, useState} from 'react'
import {type AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} 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 {app} from '#/lexicons'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking' import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
import {moderatePost} from '#/lib/moderation/subjects' import {moderatePost} from '#/lib/moderation/subjects'
@@ -22,9 +22,9 @@ function renderItem({
index, index,
}: { }: {
item: { item: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
moderation: ModerationDecision moderation: ModerationDecision
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
} }
index: number index: number
}) { }) {
@@ -32,9 +32,9 @@ function renderItem({
} }
function keyExtractor(item: { function keyExtractor(item: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
moderation: ModerationDecision moderation: ModerationDecision
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
}) { }) {
return item.post.uri return item.post.uri
} }
@@ -69,10 +69,7 @@ export function PostQuotes({uri}: {uri: string}) {
.flatMap(page => .flatMap(page =>
page.posts.map(post => { page.posts.map(post => {
if ( if (
!bsky.dangerousIsType<AppBskyFeedPost.Record>( !bsky.isType(app.bsky.feed.post, post.record) ||
post.record,
AppBskyFeedPost.isRecord,
) ||
!moderationOpts !moderationOpts
) { ) {
return null return null
+3 -3
View File
@@ -1,8 +1,8 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {type AppBskyActorDefs as ActorDefs} 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'
@@ -16,7 +16,7 @@ function renderItem({
item, item,
index, index,
}: { }: {
item: ActorDefs.ProfileView item: app.bsky.actor.defs.ProfileView
index: number index: number
}) { }) {
return ( return (
@@ -28,7 +28,7 @@ function renderItem({
) )
} }
function keyExtractor(item: ActorDefs.ProfileView) { function keyExtractor(item: app.bsky.actor.defs.ProfileView) {
return item.did return item.did
} }
+7 -8
View File
@@ -1,10 +1,11 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native' import {type StyleProp, StyleSheet, View, type ViewStyle} from 'react-native'
import {type AppBskyFeedDefs, AppBskyFeedPost, AtUri} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} from '@bsky.app/sdk/moderation'
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {MAX_POST_LINES} from '#/lib/constants' import {MAX_POST_LINES} from '#/lib/constants'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {moderatePost} from '#/lib/moderation/subjects' import {moderatePost} from '#/lib/moderation/subjects'
@@ -45,18 +46,16 @@ export function Post({
style, style,
onBeforePress, onBeforePress,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
showReplyLine?: boolean showReplyLine?: boolean
hideTopBorder?: boolean hideTopBorder?: boolean
style?: StyleProp<ViewStyle> style?: StyleProp<ViewStyle>
onBeforePress?: () => void onBeforePress?: () => void
}) { }) {
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const record = useMemo<AppBskyFeedPost.Record | undefined>( const record = useMemo<app.bsky.feed.post.Main | undefined>(
() => () =>
bsky.validate(post.record, AppBskyFeedPost.validateRecord) bsky.matches(app.bsky.feed.post, post.record) ? post.record : undefined,
? post.record
: undefined,
[post], [post],
) )
const postShadowed = usePostShadow(post) const postShadowed = usePostShadow(post)
@@ -106,8 +105,8 @@ function PostInner({
style, style,
onBeforePress: outerOnBeforePress, onBeforePress: outerOnBeforePress,
}: { }: {
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
richText: RichTextAPI richText: RichTextAPI
moderation: ModerationDecision moderation: ModerationDecision
showReplyLine?: boolean showReplyLine?: boolean
+20 -18
View File
@@ -18,18 +18,12 @@ import {
View, View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {
type AppBskyActorDefs,
AppBskyEmbedExternal,
AppBskyEmbedGallery,
AppBskyEmbedImages,
AppBskyEmbedVideo,
type AppBskyFeedDefs,
} from '@atproto/api'
import {type RichText as RichTextType} from '@bsky.app/sdk/richtext' import {type RichText as RichTextType} from '@bsky.app/sdk/richtext'
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 * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants'
import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
@@ -256,7 +250,7 @@ let PostFeed = ({
desktopFixedHeightOffset?: number desktopFixedHeightOffset?: number
ListHeaderComponent?: () => React.ReactElement ListHeaderComponent?: () => React.ReactElement
extraData?: Record<string, unknown> extraData?: Record<string, unknown>
savedFeedConfig?: AppBskyActorDefs.SavedFeed savedFeedConfig?: app.bsky.actor.defs.SavedFeed
initialNumToRender?: number initialNumToRender?: number
isVideoFeed?: boolean isVideoFeed?: boolean
lastFetchDate?: () => number lastFetchDate?: () => number
@@ -290,7 +284,7 @@ let PostFeed = ({
() => new Set<string>(), () => new Set<string>(),
) )
const onPressShowLess = useCallback( const onPressShowLess = useCallback(
(interaction: AppBskyFeedDefs.Interaction) => { (interaction: app.bsky.feed.defs.Interaction) => {
if (interaction.item) { if (interaction.item) {
const uri = interaction.item const uri = interaction.item
setHasPressedShowLessUris(prev => new Set([...prev, uri])) setHasPressedShowLessUris(prev => new Set([...prev, uri]))
@@ -492,7 +486,7 @@ let PostFeed = ({
) )
if ( if (
item && item &&
AppBskyEmbedVideo.isView(item.post.embed) && bsky.isType(app.bsky.embed.video.view, item.post.embed) &&
!blockedOrMutedAuthors.includes(item.post.author.did) !blockedOrMutedAuthors.includes(item.post.author.did)
) { ) {
videos.push({ videos.push({
@@ -1030,13 +1024,13 @@ let PostFeed = ({
// Events that should fire exactly once for every new post, regardless of // Events that should fire exactly once for every new post, regardless of
// its position within a slice or video grid row. // its position within a slice or video grid row.
const onPostSeen = (post: AppBskyFeedDefs.PostView) => { const onPostSeen = (post: app.bsky.feed.defs.PostView) => {
if (seenPerPostUrisRef.current.has(post.uri)) return if (seenPerPostUrisRef.current.has(post.uri)) return
seenPerPostUrisRef.current.add(post.uri) seenPerPostUrisRef.current.add(post.uri)
// Standard site embed view tracking // Standard site embed view tracking
if ( if (
AppBskyEmbedExternal.isView(post.embed) && bsky.isType(app.bsky.embed.external.view, post.embed) &&
isStandardSiteEmbed(post.embed.external) isStandardSiteEmbed(post.embed.external)
) { ) {
ax.metric('embed:standardSite:view', {url: post.embed.external.uri}) ax.metric('embed:standardSite:view', {url: post.embed.external.uri})
@@ -1044,13 +1038,21 @@ let PostFeed = ({
// Photo embed impression tracking // Photo embed impression tracking
if ( if (
AppBskyEmbedImages.isView(post.embed) || bsky.isType(app.bsky.embed.images.view, post.embed) ||
AppBskyEmbedGallery.isView(post.embed) bsky.isType(app.bsky.embed.gallery.view, post.embed)
) { ) {
const totalImages = AppBskyEmbedGallery.isView(post.embed) const totalImages = bsky.isType(
? post.embed.items.filter(AppBskyEmbedGallery.isViewImage).length app.bsky.embed.gallery.view,
post.embed,
)
? post.embed.items.filter(item =>
bsky.isType(app.bsky.embed.gallery.viewImage, item),
).length
: post.embed.images.length : post.embed.images.length
const useExpandedLayout = AppBskyEmbedGallery.isView(post.embed) const useExpandedLayout = bsky.isType(
app.bsky.embed.gallery.view,
post.embed,
)
? totalImages > 4 ? totalImages > 4
: ax.features.enabled(ax.features.PostGalleryEmbedEnable) : ax.features.enabled(ax.features.PostGalleryEmbedEnable)
const layout = const layout =
+6 -9
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 AppBskyActorDefs,
AppBskyFeedGetAuthorFeed,
AtUri,
} from '@atproto/api'
import {msg as msgLingui} from '@lingui/core/macro' import {msg as msgLingui} 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 {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
@@ -45,7 +42,7 @@ export function PostFeedErrorMessage({
feedDesc: FeedDescriptor feedDesc: FeedDescriptor
error?: Error error?: Error
onPressTryAgain: () => void onPressTryAgain: () => void
savedFeedConfig?: AppBskyActorDefs.SavedFeed savedFeedConfig?: app.bsky.actor.defs.SavedFeed
}) { }) {
const {_: _l} = useLingui() const {_: _l} = useLingui()
const knownError = useMemo( const knownError = useMemo(
@@ -96,7 +93,7 @@ function FeedgenErrorMessage({
feedDesc: FeedDescriptor feedDesc: FeedDescriptor
knownError: KnownError knownError: KnownError
rawError?: Error rawError?: Error
savedFeedConfig?: AppBskyActorDefs.SavedFeed savedFeedConfig?: app.bsky.actor.defs.SavedFeed
}) { }) {
const pal = usePalette('default') const pal = usePalette('default')
const {_: _l} = useLingui() const {_: _l} = useLingui()
@@ -242,8 +239,8 @@ function detectKnownError(
return undefined return undefined
} }
if ( if (
error instanceof AppBskyFeedGetAuthorFeed.BlockedActorError || error instanceof app.bsky.feed.getAuthorFeed.BlockedActorError ||
error instanceof AppBskyFeedGetAuthorFeed.BlockedByActorError error instanceof app.bsky.feed.getAuthorFeed.BlockedByActorError
) { ) {
return KnownError.Block return KnownError.Block
} }
+28 -32
View File
@@ -1,16 +1,11 @@
import {memo, useCallback, useMemo, useState} from 'react' import {memo, useCallback, useMemo, useState} from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import {
type AppBskyActorDefs,
AppBskyFeedDefs,
AppBskyFeedPost,
AppBskyFeedThreadgate,
AtUri,
} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} from '@bsky.app/sdk/moderation'
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext' import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {type ReasonFeedSource} from '#/lib/api/feed/types' import {type ReasonFeedSource} from '#/lib/api/feed/types'
import {MAX_POST_LINES} from '#/lib/constants' import {MAX_POST_LINES} from '#/lib/constants'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
@@ -58,15 +53,15 @@ import * as bsky from '#/types/bsky'
import {PostFeedReason} from './PostFeedReason' import {PostFeedReason} from './PostFeedReason'
interface FeedItemProps { interface FeedItemProps {
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
reason: reason:
| AppBskyFeedDefs.ReasonRepost | app.bsky.feed.defs.ReasonRepost
| AppBskyFeedDefs.ReasonPin | app.bsky.feed.defs.ReasonPin
| ReasonFeedSource | ReasonFeedSource
| {[k: string]: unknown; $type: string} | {[k: string]: unknown; $type: string}
| undefined | undefined
moderation: ModerationDecision moderation: ModerationDecision
parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined parentAuthor: app.bsky.actor.defs.ProfileViewBasic | undefined
showReplyTo: boolean showReplyTo: boolean
isThreadChild?: boolean isThreadChild?: boolean
isThreadLastChild?: boolean isThreadLastChild?: boolean
@@ -96,9 +91,9 @@ export function PostFeedItem({
rootPost, rootPost,
onShowLess, onShowLess,
}: FeedItemProps & { }: FeedItemProps & {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
rootPost: AppBskyFeedDefs.PostView rootPost: app.bsky.feed.defs.PostView
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
}): React.ReactNode { }): React.ReactNode {
const postShadowed = usePostShadow(post) const postShadowed = usePostShadow(post)
const richText = useMemo( const richText = useMemo(
@@ -160,9 +155,9 @@ let FeedItemInner = ({
onShowLess, onShowLess,
}: FeedItemProps & { }: FeedItemProps & {
richText: RichTextAPI richText: RichTextAPI
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<app.bsky.feed.defs.PostView>
rootPost: AppBskyFeedDefs.PostView rootPost: app.bsky.feed.defs.PostView
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
}): React.ReactNode => { }): React.ReactNode => {
const ax = useAnalytics() const ax = useAnalytics()
const queryClient = useQueryClient() const queryClient = useQueryClient()
@@ -258,7 +253,9 @@ let FeedItemInner = ({
feedSourceInfo, feedSourceInfo,
post: { post: {
post, post,
reason: AppBskyFeedDefs.isReasonRepost(reason) ? reason : undefined, reason: bsky.isType(app.bsky.feed.defs.reasonRepost, reason)
? reason
: undefined,
feedContext, feedContext,
reqId, reqId,
}, },
@@ -282,9 +279,9 @@ let FeedItemInner = ({
* If `post[0]` in this slice is the actual root post (not an orphan thread), * If `post[0]` in this slice is the actual root post (not an orphan thread),
* then we may have a threadgate record to reference * then we may have a threadgate record to reference
*/ */
const threadgateRecord = bsky.dangerousIsType<AppBskyFeedThreadgate.Record>( const threadgateRecord = bsky.isType(
app.bsky.feed.threadgate,
rootPost.threadgate?.record, rootPost.threadgate?.record,
AppBskyFeedThreadgate.isRecord,
) )
? rootPost.threadgate.record ? rootPost.threadgate.record
: undefined : undefined
@@ -292,7 +289,11 @@ let FeedItemInner = ({
const {isActive: live} = useActorStatus(post.author) const {isActive: live} = useActorStatus(post.author)
const viaRepost = useMemo(() => { const viaRepost = useMemo(() => {
if (AppBskyFeedDefs.isReasonRepost(reason) && reason.uri && reason.cid) { if (
bsky.isType(app.bsky.feed.defs.reasonRepost, reason) &&
reason.uri &&
reason.cid
) {
return { return {
uri: reason.uri, uri: reason.uri,
cid: reason.cid, cid: reason.cid,
@@ -305,10 +306,7 @@ let FeedItemInner = ({
}) })
const additionalPostAlerts: AppModerationCause[] = useMemo(() => { const additionalPostAlerts: AppModerationCause[] = useMemo(() => {
const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri) const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri)
const rootPostUri = bsky.dangerousIsType<AppBskyFeedPost.Record>( const rootPostUri = bsky.isType(app.bsky.feed.post, post.record)
post.record,
AppBskyFeedPost.isRecord,
)
? post.record?.reply?.root?.uri || post.uri ? post.record?.reply?.root?.uri || post.uri
: undefined : undefined
const isControlledByViewer = const isControlledByViewer =
@@ -465,10 +463,10 @@ let PostContent = ({
}: { }: {
moderation: ModerationDecision moderation: ModerationDecision
richText: RichTextAPI richText: RichTextAPI
postEmbed: AppBskyFeedDefs.PostView['embed'] postEmbed: app.bsky.feed.defs.PostView['embed']
postAuthor: AppBskyFeedDefs.PostView['author'] postAuthor: app.bsky.feed.defs.PostView['author']
onOpenEmbed: () => void onOpenEmbed: () => void
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
additionalPostAlerts?: AppModerationCause[] additionalPostAlerts?: AppModerationCause[]
feedDescriptor?: string feedDescriptor?: string
}): React.ReactNode => { }): React.ReactNode => {
@@ -476,11 +474,9 @@ let PostContent = ({
() => countLines(richText.text) >= MAX_POST_LINES, () => countLines(richText.text) >= MAX_POST_LINES,
) )
const record = useMemo<AppBskyFeedPost.Record | undefined>( const record = useMemo<app.bsky.feed.post.Main | undefined>(
() => () =>
bsky.validate(post.record, AppBskyFeedPost.validateRecord) bsky.matches(app.bsky.feed.post, post.record) ? post.record : undefined,
? post.record
: undefined,
[post], [post],
) )
+6 -5
View File
@@ -1,10 +1,11 @@
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import {AppBskyFeedDefs} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} 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 * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types' import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types'
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'
@@ -24,8 +25,8 @@ export function PostFeedReason({
}: { }: {
reason: reason:
| ReasonFeedSource | ReasonFeedSource
| AppBskyFeedDefs.ReasonRepost | app.bsky.feed.defs.ReasonRepost
| AppBskyFeedDefs.ReasonPin | app.bsky.feed.defs.ReasonPin
| {[k: string]: unknown; $type: string} | {[k: string]: unknown; $type: string}
moderation?: ModerationDecision moderation?: ModerationDecision
onOpenReposter?: () => void onOpenReposter?: () => void
@@ -64,7 +65,7 @@ export function PostFeedReason({
) )
} }
if (AppBskyFeedDefs.isReasonRepost(reason)) { if (bsky.isType(app.bsky.feed.defs.reasonRepost, reason)) {
const isOwner = reason.by.did === currentAccount?.did const isOwner = reason.by.did === currentAccount?.did
const reposter = createSanitizedDisplayName( const reposter = createSanitizedDisplayName(
reason.by, reason.by,
@@ -103,7 +104,7 @@ export function PostFeedReason({
) )
} }
if (AppBskyFeedDefs.isReasonPin(reason)) { if (bsky.isType(app.bsky.feed.defs.reasonPin, reason)) {
return ( return (
<View style={styles.includeReason}> <View style={styles.includeReason}>
<PinIcon <PinIcon
+1 -1
View File
@@ -1,9 +1,9 @@
import {useMemo} from 'react' import {useMemo} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import Svg, {Circle, Line} from 'react-native-svg' import Svg, {Circle, Line} from 'react-native-svg'
import {AtUri} from '@atproto/api'
import {useLingui} from '@lingui/react/macro' import {useLingui} from '@lingui/react/macro'
import {AtUri} from '@atproto/syntax'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {atoms as a, select, useTheme} from '#/alf' import {atoms as a, select, useTheme} from '#/alf'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
+5 -5
View File
@@ -1,8 +1,8 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react' import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {type AppBskyActorDefs as ActorDefs} 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 {app} from '#/lexicons'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
@@ -27,7 +27,7 @@ function renderItem({
index, index,
contextProfileDid, contextProfileDid,
}: { }: {
item: ActorDefs.ProfileView item: app.bsky.actor.defs.ProfileView
index: number index: number
contextProfileDid: string | undefined contextProfileDid: string | undefined
}) { }) {
@@ -42,7 +42,7 @@ function renderItem({
) )
} }
function keyExtractor(item: ActorDefs.ProfileView) { function keyExtractor(item: app.bsky.actor.defs.ProfileView) {
return item.did return item.did
} }
@@ -138,7 +138,7 @@ export function ProfileFollowers({name}: {name: string}) {
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const renderItemWithContext = useCallback( const renderItemWithContext = useCallback(
({item, index}: {item: ActorDefs.ProfileView; index: number}) => ({item, index}: {item: app.bsky.actor.defs.ProfileView; index: number}) =>
renderItem({item, index, contextProfileDid: resolvedDid}), renderItem({item, index, contextProfileDid: resolvedDid}),
[resolvedDid], [resolvedDid],
) )
@@ -160,7 +160,7 @@ export function ProfileFollowers({name}: {name: string}) {
seenItemsRef.current.clear() seenItemsRef.current.clear()
}, [resolvedDid]) }, [resolvedDid])
const onItemSeen = useCallback( const onItemSeen = useCallback(
(item: ActorDefs.ProfileView) => { (item: app.bsky.actor.defs.ProfileView) => {
if (seenItemsRef.current.has(item.did)) { if (seenItemsRef.current.has(item.did)) {
return return
} }
+5 -5
View File
@@ -1,8 +1,8 @@
import {useCallback, useEffect, useMemo, useRef, useState} from 'react' import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {type AppBskyActorDefs as ActorDefs} 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 {app} from '#/lexicons'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {type NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
@@ -23,7 +23,7 @@ function renderItem({
index, index,
contextProfileDid, contextProfileDid,
}: { }: {
item: ActorDefs.ProfileView item: app.bsky.actor.defs.ProfileView
index: number index: number
contextProfileDid: string | undefined contextProfileDid: string | undefined
}) { }) {
@@ -38,7 +38,7 @@ function renderItem({
) )
} }
function keyExtractor(item: ActorDefs.ProfileView) { function keyExtractor(item: app.bsky.actor.defs.ProfileView) {
return item.did return item.did
} }
@@ -143,7 +143,7 @@ export function ProfileFollows({name}: {name: string}) {
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const renderItemWithContext = useCallback( const renderItemWithContext = useCallback(
({item, index}: {item: ActorDefs.ProfileView; index: number}) => ({item, index}: {item: app.bsky.actor.defs.ProfileView; index: number}) =>
renderItem({item, index, contextProfileDid: resolvedDid}), renderItem({item, index, contextProfileDid: resolvedDid}),
[resolvedDid], [resolvedDid],
) )
@@ -165,7 +165,7 @@ export function ProfileFollows({name}: {name: string}) {
seenItemsRef.current.clear() seenItemsRef.current.clear()
}, [resolvedDid]) }, [resolvedDid])
const onItemSeen = useCallback( const onItemSeen = useCallback(
(item: ActorDefs.ProfileView) => { (item: app.bsky.actor.defs.ProfileView) => {
if (seenItemsRef.current.has(item.did)) { if (seenItemsRef.current.has(item.did)) {
return return
} }
+2 -2
View File
@@ -1,9 +1,9 @@
import {memo, useCallback, useMemo} from 'react' import {memo, useCallback, useMemo} from 'react'
import {type AppBskyActorDefs} 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 {app} from '#/lexicons'
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'
@@ -71,7 +71,7 @@ import {useDevMode} from '#/storage/hooks/dev-mode'
let ProfileMenu = ({ let ProfileMenu = ({
profile, profile,
}: { }: {
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> profile: Shadow<app.bsky.actor.defs.ProfileViewDetailed>
}): React.ReactNode => { }): React.ReactNode => {
const t = useTheme() const t = useTheme()
const ax = useAnalytics() const ax = useAnalytics()
@@ -1,12 +1,12 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {Pressable, View} from 'react-native' import {Pressable, View} from 'react-native'
import Animated, {useAnimatedRef} from 'react-native-reanimated' import Animated, {useAnimatedRef} from 'react-native-reanimated'
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 {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
@@ -37,7 +37,7 @@ export function ProfileSubpageHeader({
title: string | undefined title: string | undefined
avatar: string | undefined avatar: string | undefined
isOwner: boolean | undefined isOwner: boolean | undefined
purpose: AppBskyGraphDefs.ListPurpose | undefined purpose: app.bsky.graph.defs.ListPurpose | undefined
creator: creator:
| { | {
did: string did: string
+2 -2
View File
@@ -1,11 +1,11 @@
import {memo, useCallback} from 'react' import {memo, useCallback} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} 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 {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {app} from '#/lexicons'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {forceLTR} from '#/lib/strings/bidi' import {forceLTR} from '#/lib/strings/bidi'
import {NON_BREAKING_SPACE} from '#/lib/strings/constants' import {NON_BREAKING_SPACE} from '#/lib/strings/constants'
@@ -25,7 +25,7 @@ import {TimeElapsed} from './TimeElapsed'
import {PreviewableUserAvatar} from './UserAvatar' import {PreviewableUserAvatar} from './UserAvatar'
interface PostMetaOpts { interface PostMetaOpts {
author: AppBskyActorDefs.ProfileViewBasic author: app.bsky.actor.defs.ProfileViewBasic
moderation: ModerationDecision | undefined moderation: ModerationDecision | undefined
postHref: string postHref: string
timestamp: string timestamp: string
+2 -2
View File
@@ -1,6 +1,6 @@
import {type StyleProp, type TextStyle} from 'react-native' import {type StyleProp, type TextStyle} from 'react-native'
import {type AppBskyActorGetProfile} from '@atproto/api'
import {app} from '#/lexicons'
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'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
@@ -19,7 +19,7 @@ export function UserInfoText({
style, style,
}: { }: {
did: string did: string
attr?: keyof AppBskyActorGetProfile.OutputSchema attr?: keyof app.bsky.actor.getProfile.$OutputBody
loading?: string loading?: string
failed?: string failed?: string
prefix?: string prefix?: string
+247 -48
View File
@@ -1,17 +1,12 @@
import {useMemo, useState} from 'react' import {useMemo, useState} from 'react'
import {View} from 'react-native' import {View} from 'react-native'
import {useSharedValue} from 'react-native-reanimated' import {useSharedValue} from 'react-native-reanimated'
import {
type AppBskyActorDefs,
type AppBskyFeedDefs,
type AppBskyFeedPost,
type ComAtprotoLabelDefs,
mock,
} from '@atproto/api'
import { import {
interpretLabelValueDefinition, interpretLabelValueDefinition,
type LabelPreference, type LabelPreference,
LABELS, LABELS,
moderatePost,
moderateProfile,
type ModerationBehavior, type ModerationBehavior,
type ModerationDecision, type ModerationDecision,
type ModerationOpts, type ModerationOpts,
@@ -20,7 +15,6 @@ import {RichText} 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 {moderatePost, moderateProfile} from '#/lib/moderation/subjects'
import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings' import {useGlobalLabelStrings} from '#/lib/moderation/useGlobalLabelStrings'
import { import {
type CommonNavigatorParams, type CommonNavigatorParams,
@@ -54,6 +48,7 @@ import {
import * as Layout from '#/components/Layout' import * as Layout from '#/components/Layout'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {H1, H3, P, Text} from '#/components/Typography' import {H1, H3, P, Text} from '#/components/Typography'
import {type app, type com} from '#/lexicons'
import {ScreenHider} from '../../components/moderation/ScreenHider' import {ScreenHider} from '../../components/moderation/ScreenHider'
import {NotificationFeedItem} from '../com/notifications/NotificationFeedItem' import {NotificationFeedItem} from '../com/notifications/NotificationFeedItem'
import {PagerHeaderProvider} from '../com/pager/PagerHeaderContext' import {PagerHeaderProvider} from '../com/pager/PagerHeaderContext'
@@ -63,6 +58,211 @@ const LABEL_VALUES: (keyof typeof LABELS)[] = Object.keys(
LABELS, LABELS,
) as (keyof typeof LABELS)[] ) as (keyof typeof LABELS)[]
const FAKE_CID = 'bafyreiclp443lavogvhj3d2ob2cxbfuscni2k5jk7bebjzg7khl3esabwq'
/*
* Local test-data builders for this dev-only moderation debug screen. These
* replace the `mock` object the old api package used to export (the SDK does
* not ship one). Each builder returns a plain `#/lexicons` object literal with
* the same field values the old `mock` builders produced. Branded string slots
* (`did`/`at-uri`/`cid`/`lang`) are cast, since this is trusted mock data.
*/
const mock = {
post({
text,
facets,
reply,
embed,
}: {
text: string
facets?: app.bsky.feed.post.Main['facets']
reply?: app.bsky.feed.post.Main['reply']
embed?: app.bsky.feed.post.Main['embed']
}): app.bsky.feed.post.Main {
return {
$type: 'app.bsky.feed.post',
text,
facets,
reply,
embed,
langs: ['en'],
createdAt:
new Date().toISOString() as app.bsky.feed.post.Main['createdAt'],
}
},
postView({
record,
author,
embed,
replyCount,
repostCount,
likeCount,
viewer,
labels,
}: {
record: app.bsky.feed.post.Main
author: app.bsky.actor.defs.ProfileViewBasic
embed?: app.bsky.feed.defs.PostView['embed']
replyCount?: number
repostCount?: number
likeCount?: number
viewer?: app.bsky.feed.defs.ViewerState
labels?: com.atproto.label.defs.Label[]
}): app.bsky.feed.defs.PostView {
return {
$type: 'app.bsky.feed.defs#postView',
uri: `at://${author.did}/app.bsky.feed.post/fake`,
cid: FAKE_CID,
author,
record,
embed,
replyCount,
repostCount,
likeCount,
indexedAt:
new Date().toISOString() as app.bsky.feed.defs.PostView['indexedAt'],
viewer,
labels,
}
},
embedRecordView({
record,
author,
labels,
}: {
record: app.bsky.feed.post.Main
author: app.bsky.actor.defs.ProfileViewBasic
labels?: com.atproto.label.defs.Label[]
}): app.bsky.embed.record.View {
return {
$type: 'app.bsky.embed.record#view',
record: {
$type: 'app.bsky.embed.record#viewRecord',
uri: `at://${author.did}/app.bsky.feed.post/fake`,
cid: FAKE_CID,
author,
value: record,
labels,
indexedAt:
new Date().toISOString() as app.bsky.embed.record.ViewRecord['indexedAt'],
},
}
},
profileViewBasic({
handle,
displayName,
description,
viewer,
labels,
}: {
handle: string
displayName?: string
description?: string
viewer?: app.bsky.actor.defs.ViewerState
labels?: com.atproto.label.defs.Label[]
}): app.bsky.actor.defs.ProfileViewBasic & {description?: string} {
return {
did: `did:web:${handle}`,
handle: handle as app.bsky.actor.defs.ProfileViewBasic['handle'],
displayName,
description,
viewer,
labels,
}
},
actorViewerState({
muted,
mutedByList,
blockedBy,
blocking,
blockingByList,
following,
followedBy,
}: {
muted?: boolean
mutedByList?: app.bsky.graph.defs.ListViewBasic
blockedBy?: boolean
blocking?: string
blockingByList?: app.bsky.graph.defs.ListViewBasic
following?: string
followedBy?: string
}): app.bsky.actor.defs.ViewerState {
return {
muted,
mutedByList,
blockedBy,
blocking: blocking as app.bsky.actor.defs.ViewerState['blocking'],
blockingByList,
following: following as app.bsky.actor.defs.ViewerState['following'],
followedBy: followedBy as app.bsky.actor.defs.ViewerState['followedBy'],
}
},
replyNotification({
author,
record,
labels,
}: {
record: app.bsky.feed.post.Main
author: app.bsky.actor.defs.ProfileViewBasic
labels?: com.atproto.label.defs.Label[]
}): app.bsky.notification.listNotifications.Notification {
return {
uri: `at://${author.did}/app.bsky.feed.post/fake`,
cid: FAKE_CID,
author: author as app.bsky.actor.defs.ProfileView,
reason: 'reply',
reasonSubject: `at://${author.did}/app.bsky.feed.post/fake-parent`,
record,
isRead: false,
indexedAt:
new Date().toISOString() as app.bsky.notification.listNotifications.Notification['indexedAt'],
labels,
}
},
followNotification({
author,
subjectDid,
labels,
}: {
author: app.bsky.actor.defs.ProfileViewBasic
subjectDid: string
labels?: com.atproto.label.defs.Label[]
}): app.bsky.notification.listNotifications.Notification {
return {
uri: `at://${author.did}/app.bsky.graph.follow/fake`,
cid: FAKE_CID,
author: author as app.bsky.actor.defs.ProfileView,
reason: 'follow',
record: {
$type: 'app.bsky.graph.follow',
createdAt: new Date().toISOString(),
subject: subjectDid,
},
isRead: false,
indexedAt:
new Date().toISOString() as app.bsky.notification.listNotifications.Notification['indexedAt'],
labels,
}
},
label({
val,
uri,
src,
}: {
val: string
uri: string
src?: string
}): com.atproto.label.defs.Label {
return {
src: (src ||
'did:plc:fake-labeler') as com.atproto.label.defs.Label['src'],
uri: uri as com.atproto.label.defs.Label['uri'],
val,
cts: new Date().toISOString() as com.atproto.label.defs.Label['cts'],
}
},
}
export const DebugModScreen = ({}: NativeStackScreenProps< export const DebugModScreen = ({}: NativeStackScreenProps<
CommonNavigatorParams, CommonNavigatorParams,
'DebugMod' 'DebugMod'
@@ -74,7 +274,7 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
const [target, setTarget] = useState<string[]>(['account']) const [target, setTarget] = useState<string[]>(['account'])
const [visibility, setVisiblity] = useState<string[]>(['warn']) const [visibility, setVisiblity] = useState<string[]>(['warn'])
const [customLabelDef, setCustomLabelDef] = const [customLabelDef, setCustomLabelDef] =
useState<ComAtprotoLabelDefs.LabelValueDefinition>({ useState<com.atproto.label.defs.LabelValueDefinition>({
identifier: 'custom', identifier: 'custom',
blurs: 'content', blurs: 'content',
severity: 'alert', severity: 'alert',
@@ -141,7 +341,7 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
blockingByList: undefined, blockingByList: undefined,
}), }),
}) })
mockedProfile.did = did mockedProfile.did = did as app.bsky.actor.defs.ProfileViewBasic['did']
mockedProfile.avatar = 'https://bsky.social/about/images/favicon-32x32.png' mockedProfile.avatar = 'https://bsky.social/about/images/favicon-32x32.png'
// @ts-expect-error ProfileViewBasic is close enough -esb // @ts-expect-error ProfileViewBasic is close enough -esb
mockedProfile.banner = mockedProfile.banner =
@@ -165,36 +365,35 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
}), }),
] ]
: undefined, : undefined,
embed: embed: (target[0] === 'embed'
target[0] === 'embed' ? mock.embedRecordView({
? mock.embedRecordView({ record: mock.post({
record: mock.post({ text: 'Embed',
text: 'Embed', }),
}), labels:
labels: scenario[0] === 'label' && target[0] === 'embed'
scenario[0] === 'label' && target[0] === 'embed' ? [
? [ mock.label({
mock.label({ src: isSelfLabel ? did : undefined,
src: isSelfLabel ? did : undefined, val: label[0],
val: label[0], uri: `at://${did}/app.bsky.feed.post/fake`,
uri: `at://${did}/app.bsky.feed.post/fake`, }),
}), ]
] : undefined,
: undefined, author: profile,
author: profile, })
}) : {
: { $type: 'app.bsky.embed.images#view',
$type: 'app.bsky.embed.images#view', images: [
images: [ {
{ thumb:
thumb: 'https://bsky.social/about/images/social-card-default-gradient.png',
'https://bsky.social/about/images/social-card-default-gradient.png', fullsize:
fullsize: 'https://bsky.social/about/images/social-card-default-gradient.png',
'https://bsky.social/about/images/social-card-default-gradient.png', alt: '',
alt: '', },
}, ],
], }) as app.bsky.feed.defs.PostView['embed'],
},
}) })
}, [scenario, label, target, profile, isSelfLabel, did]) }, [scenario, label, target, profile, isSelfLabel, did])
@@ -227,7 +426,7 @@ export const DebugModScreen = ({}: NativeStackScreenProps<
}) })
const [item] = groupNotifications([notif]) const [item] = groupNotifications([notif])
item.subject = mock.postView({ item.subject = mock.postView({
record: notif.record as AppBskyFeedPost.Record, record: notif.record as app.bsky.feed.post.Main,
author: profile, author: profile,
labels: notif.labels, labels: notif.labels,
}) })
@@ -634,9 +833,9 @@ function CustomLabelForm({
def, def,
setDef, setDef,
}: { }: {
def: ComAtprotoLabelDefs.LabelValueDefinition def: com.atproto.label.defs.LabelValueDefinition
setDef: React.Dispatch< setDef: React.Dispatch<
React.SetStateAction<ComAtprotoLabelDefs.LabelValueDefinition> React.SetStateAction<com.atproto.label.defs.LabelValueDefinition>
> >
}) { }) {
const t = useTheme() const t = useTheme()
@@ -838,7 +1037,7 @@ function MockPostFeedItem({
post, post,
moderation, moderation,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
moderation: ModerationDecision moderation: ModerationDecision
}) { }) {
const t = useTheme() const t = useTheme()
@@ -852,7 +1051,7 @@ function MockPostFeedItem({
return ( return (
<PostFeedItem <PostFeedItem
post={post} post={post}
record={post.record as AppBskyFeedPost.Record} record={post.record as app.bsky.feed.post.Main}
moderation={moderation} moderation={moderation}
parentAuthor={undefined} parentAuthor={undefined}
showReplyTo={false} showReplyTo={false}
@@ -869,7 +1068,7 @@ function MockPostThreadItem({
moderationOpts, moderationOpts,
isReply, isReply,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
isReply?: boolean isReply?: boolean
}) { }) {
@@ -924,7 +1123,7 @@ function MockAccountCard({
profile, profile,
moderation, moderation,
}: { }: {
profile: AppBskyActorDefs.ProfileViewBasic profile: app.bsky.actor.defs.ProfileViewBasic
moderation: ModerationDecision moderation: ModerationDecision
}) { }) {
const t = useTheme() const t = useTheme()
@@ -948,7 +1147,7 @@ function MockAccountScreen({
moderation, moderation,
moderationOpts, moderationOpts,
}: { }: {
profile: AppBskyActorDefs.ProfileViewBasic profile: app.bsky.actor.defs.ProfileViewBasic
moderation: ModerationDecision moderation: ModerationDecision
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
}) { }) {
+2 -2
View File
@@ -1,11 +1,11 @@
import {useCallback, useMemo, useRef, useState} from 'react' import {useCallback, useMemo, useRef, useState} from 'react'
import {ActivityIndicator, StyleSheet, View} from 'react-native' import {ActivityIndicator, StyleSheet, View} 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 debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import {app} from '#/lexicons'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
@@ -91,7 +91,7 @@ type FlatlistSlice =
type: 'popularFeed' type: 'popularFeed'
key: string key: string
feedUri: string feedUri: string
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
} }
| { | {
type: 'popularFeedsLoadingMore' type: 'popularFeedsLoadingMore'
+1 -1
View File
@@ -1,10 +1,10 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {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 {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {AtUri} from '@atproto/syntax'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import { import {
type CommonNavigatorParams, type CommonNavigatorParams,
@@ -1,9 +1,9 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {app} from '#/lexicons'
import {type CommonNavigatorParams} from '#/lib/routes/types' import {type CommonNavigatorParams} from '#/lib/routes/types'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -68,7 +68,7 @@ export function ModerationBlockedAccounts({}: Props) {
item, item,
index, index,
}: { }: {
item: ActorDefs.ProfileView item: app.bsky.actor.defs.ProfileView
index: number index: number
}) => { }) => {
if (!moderationOpts) return null if (!moderationOpts) return null
@@ -113,7 +113,7 @@ export function ModerationBlockedAccounts({}: Props) {
) : ( ) : (
<List <List
data={profiles} data={profiles}
keyExtractor={(item: ActorDefs.ProfileView) => item.did} keyExtractor={(item: app.bsky.actor.defs.ProfileView) => item.did}
refreshing={isPTRing} refreshing={isPTRing}
onRefresh={onRefresh} onRefresh={onRefresh}
onEndReached={onEndReached} onEndReached={onEndReached}
+1 -1
View File
@@ -1,10 +1,10 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {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 {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {AtUri} from '@atproto/syntax'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import { import {
type CommonNavigatorParams, type CommonNavigatorParams,
+2 -2
View File
@@ -1,9 +1,9 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {type StyleProp, View, type ViewStyle} from 'react-native' import {type StyleProp, View, type ViewStyle} from 'react-native'
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
import {Trans} from '@lingui/react/macro' import {Trans} from '@lingui/react/macro'
import {type NativeStackScreenProps} from '@react-navigation/native-stack' import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {app} from '#/lexicons'
import {type CommonNavigatorParams} from '#/lib/routes/types' import {type CommonNavigatorParams} from '#/lib/routes/types'
import {cleanError} from '#/lib/strings/errors' import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -68,7 +68,7 @@ export function ModerationMutedAccounts({}: Props) {
item, item,
index, index,
}: { }: {
item: ActorDefs.ProfileView item: app.bsky.actor.defs.ProfileView
index: number index: number
}) => { }) => {
if (!moderationOpts) return null if (!moderationOpts) return null
+2 -2
View File
@@ -2,7 +2,6 @@ import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {StyleSheet} from 'react-native' import {StyleSheet} from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context' import {SafeAreaView} from 'react-native-safe-area-context'
import {ScrollForwarderView} from 'react-native-scroll-forwarder' import {ScrollForwarderView} from 'react-native-scroll-forwarder'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
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'
@@ -11,6 +10,7 @@ import {Trans} from '@lingui/react/macro'
import {useFocusEffect, useNavigation} from '@react-navigation/native' import {useFocusEffect, useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import {app} from '#/lexicons'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification' import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {useSetTitle} from '#/lib/hooks/useSetTitle' import {useSetTitle} from '#/lib/hooks/useSetTitle'
@@ -166,7 +166,7 @@ function ProfileScreenLoaded({
moderationOpts, moderationOpts,
hideBackButton, hideBackButton,
}: { }: {
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
hideBackButton: boolean hideBackButton: boolean
isPlaceholderProfile: boolean isPlaceholderProfile: boolean
+3 -3
View File
@@ -1,10 +1,10 @@
import {useCallback, useMemo, useState} from 'react' import {useCallback, useMemo, useState} from 'react'
import {StyleSheet, View} from 'react-native' import {StyleSheet, View} from 'react-native'
import {type AppBskyActorDefs} from '@atproto/api'
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 {useNavigation, useNavigationState} from '@react-navigation/native' import {useNavigation, useNavigationState} from '@react-navigation/native'
import {app} from '#/lexicons'
import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer' import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {getCurrentRoute, isTab} from '#/lib/routes/helpers' import {getCurrentRoute, isTab} from '#/lib/routes/helpers'
@@ -239,7 +239,7 @@ function SwitchMenuItems({
accounts: accounts:
| { | {
account: SessionAccount account: SessionAccount
profile?: AppBskyActorDefs.ProfileViewDetailed profile?: app.bsky.actor.defs.ProfileViewDetailed
}[] }[]
| undefined | undefined
signOutPromptControl: DialogControlProps signOutPromptControl: DialogControlProps
@@ -350,7 +350,7 @@ function SwitchMenuItem({
profile, profile,
}: { }: {
account: SessionAccount account: SessionAccount
profile: AppBskyActorDefs.ProfileViewDetailed | undefined profile: app.bsky.actor.defs.ProfileViewDetailed | undefined
}) { }) {
const {t: l} = useLingui() const {t: l} = useLingui()
const {onPressSwitchAccount, pendingDid} = useAccountSwitcher() const {onPressSwitchAccount, pendingDid} = useAccountSwitcher()