[SDK] Remove @atproto/api (#11386)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,14 +44,8 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
import {scheduleOnUI} from 'react-native-worklets'
|
||||
import * as FileSystem from 'expo-file-system'
|
||||
import {type ImagePickerAsset} from 'expo-image-picker'
|
||||
import {
|
||||
AppBskyDraftCreateDraft,
|
||||
AppBskyUnspeccedDefs,
|
||||
AtUri,
|
||||
ChatBskyGroupDefs,
|
||||
} from '@atproto/api'
|
||||
import {type Client} from '@atproto/lex'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {type Client, type UriString} from '@atproto/lex'
|
||||
import {AtUri, type AtUriString} from '@atproto/syntax'
|
||||
import {type RichText} from '@bsky.app/sdk/richtext'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
@@ -78,6 +72,7 @@ import {useCallOnce} from '#/lib/once'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {colors} from '#/lib/styles'
|
||||
import {matchXrpcError} from '#/lib/xrpc-error'
|
||||
import {logger} from '#/logger'
|
||||
import {useDialogStateControlContext} from '#/state/dialogs'
|
||||
import {emitPostCreated} from '#/state/events'
|
||||
@@ -149,7 +144,8 @@ import {
|
||||
IS_WEB_SAFARI,
|
||||
} from '#/env'
|
||||
import {type Gif} from '#/features/gifPicker/types'
|
||||
import {app} from '#/lexicons'
|
||||
import {app, chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
|
||||
import {
|
||||
draftToComposerPosts,
|
||||
@@ -771,7 +767,9 @@ export const ComposePost = ({
|
||||
|
||||
const getDraftSaveError = useCallback(
|
||||
(e: unknown): string => {
|
||||
if (e instanceof AppBskyDraftCreateDraft.DraftLimitReachedError) {
|
||||
if (
|
||||
matchXrpcError(e, app.bsky.draft.createDraft) === 'DraftLimitReached'
|
||||
) {
|
||||
return l`You've reached the maximum number of drafts`
|
||||
}
|
||||
return l`Failed to save draft`
|
||||
@@ -1010,7 +1008,7 @@ export const ComposePost = ({
|
||||
const hasUnavailableChatInvite = linkQueries.some(
|
||||
q =>
|
||||
q.data?.type === 'chat-invite' &&
|
||||
!ChatBskyGroupDefs.isJoinLinkPreviewView(q.data.view),
|
||||
!bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, q.data.view),
|
||||
)
|
||||
|
||||
const canPost =
|
||||
@@ -1140,7 +1138,7 @@ export const ComposePost = ({
|
||||
}
|
||||
if (
|
||||
!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`)
|
||||
@@ -1212,7 +1210,7 @@ export const ComposePost = ({
|
||||
const resolved = q.data
|
||||
if (
|
||||
resolved?.type === 'chat-invite' &&
|
||||
ChatBskyGroupDefs.isJoinLinkPreviewView(resolved.view)
|
||||
bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, resolved.view)
|
||||
) {
|
||||
ax.metric('groupchat:inviteLink:shared', {
|
||||
convoId: resolved.view.convoId,
|
||||
@@ -1251,7 +1249,7 @@ export const ComposePost = ({
|
||||
void whenAppViewReady(client, initQuote.uri, res => {
|
||||
const anchor = res.thread.at(0)
|
||||
if (
|
||||
AppBskyUnspeccedDefs.isThreadItemPost(anchor?.value) &&
|
||||
bsky.isType(app.bsky.unspecced.defs.threadItemPost, anchor?.value) &&
|
||||
anchor.value.post.quoteCount !== initQuote.quoteCount
|
||||
) {
|
||||
onPost?.(postUri)
|
||||
@@ -1681,7 +1679,7 @@ let ComposerPost = memo(function ComposerPost({
|
||||
|
||||
const onNewLink = useCallback(
|
||||
(uri: string) => {
|
||||
dispatchPost({type: 'embed_add_uri', uri})
|
||||
dispatchPost({type: 'embed_add_uri', uri: uri as UriString})
|
||||
},
|
||||
[dispatchPost],
|
||||
)
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {LayoutAnimation, Pressable, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {
|
||||
AppBskyEmbedGallery,
|
||||
AppBskyEmbedImages,
|
||||
AppBskyEmbedRecord,
|
||||
AppBskyEmbedRecordWithMedia,
|
||||
AppBskyFeedPost,
|
||||
} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -20,6 +13,8 @@ import {atoms as a, useTheme, utils, web} from '#/alf'
|
||||
import {QuoteEmbed} from '#/components/Post/Embed'
|
||||
import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {parseEmbed} from '#/types/bsky/post'
|
||||
|
||||
export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
@@ -39,15 +34,15 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
|
||||
const quoteEmbed = useMemo(() => {
|
||||
if (
|
||||
AppBskyEmbedRecord.isView(embed) &&
|
||||
AppBskyEmbedRecord.isViewRecord(embed.record) &&
|
||||
AppBskyFeedPost.isRecord(embed.record.value)
|
||||
bsky.isType(app.bsky.embed.record.view, embed) &&
|
||||
bsky.isType(app.bsky.embed.record.viewRecord, embed.record) &&
|
||||
bsky.isType(app.bsky.feed.post, embed.record.value)
|
||||
) {
|
||||
return embed
|
||||
} else if (
|
||||
AppBskyEmbedRecordWithMedia.isView(embed) &&
|
||||
AppBskyEmbedRecord.isViewRecord(embed.record.record) &&
|
||||
AppBskyFeedPost.isRecord(embed.record.record.value)
|
||||
bsky.isType(app.bsky.embed.recordWithMedia.view, embed) &&
|
||||
bsky.isType(app.bsky.embed.record.viewRecord, embed.record.record) &&
|
||||
bsky.isType(app.bsky.feed.post, embed.record.record.value)
|
||||
) {
|
||||
return embed.record
|
||||
}
|
||||
@@ -61,20 +56,20 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
: null
|
||||
|
||||
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}
|
||||
} else if (AppBskyEmbedGallery.isView(embed)) {
|
||||
} else if (bsky.isType(app.bsky.embed.gallery.view, embed)) {
|
||||
return {
|
||||
images: galleryItemsToImages(embed.items),
|
||||
totalNumber: embed.items.length,
|
||||
}
|
||||
} else if (AppBskyEmbedRecordWithMedia.isView(embed)) {
|
||||
if (AppBskyEmbedImages.isView(embed.media)) {
|
||||
} else if (bsky.isType(app.bsky.embed.recordWithMedia.view, embed)) {
|
||||
if (bsky.isType(app.bsky.embed.images.view, embed.media)) {
|
||||
return {
|
||||
images: embed.media.images,
|
||||
totalNumber: embed.media.images.length,
|
||||
}
|
||||
} else if (AppBskyEmbedGallery.isView(embed.media)) {
|
||||
} else if (bsky.isType(app.bsky.embed.gallery.view, embed.media)) {
|
||||
return {
|
||||
images: galleryItemsToImages(embed.media.items),
|
||||
totalNumber: embed.media.items.length,
|
||||
@@ -145,12 +140,12 @@ export function ComposerReplyTo({replyTo}: {replyTo: ComposerOptsPostRef}) {
|
||||
}
|
||||
|
||||
function galleryItemsToImages(
|
||||
items: AppBskyEmbedGallery.View['items'],
|
||||
): AppBskyEmbedImages.ViewImage[] {
|
||||
items: app.bsky.embed.gallery.View['items'],
|
||||
): app.bsky.embed.images.ViewImage[] {
|
||||
// The reply-to thumbnail only renders up to 4 tiles; slicing here keeps
|
||||
// the existing layout switch valid for galleries up to 10 items.
|
||||
return items
|
||||
.filter(AppBskyEmbedGallery.isViewImage)
|
||||
.filter(item => bsky.isType(app.bsky.embed.gallery.viewImage, item))
|
||||
.slice(0, 4)
|
||||
.map(item => ({
|
||||
thumb: item.thumbnail,
|
||||
@@ -164,7 +159,7 @@ function ComposerReplyToImages({
|
||||
images,
|
||||
totalNumber,
|
||||
}: {
|
||||
images: AppBskyEmbedImages.ViewImage[]
|
||||
images: app.bsky.embed.images.ViewImage[]
|
||||
totalNumber: number
|
||||
}) {
|
||||
const t = useTheme()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {useMemo} from 'react'
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {type UriString} from '@atproto/lex'
|
||||
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {
|
||||
@@ -32,9 +33,9 @@ export const ExternalEmbedGif = ({
|
||||
() =>
|
||||
data && {
|
||||
title: data.title ?? data.uri,
|
||||
uri: data.uri,
|
||||
uri: data.uri as UriString,
|
||||
description: data.description ?? '',
|
||||
thumb: data.thumb?.source.path,
|
||||
thumb: data.thumb?.source.path as UriString | undefined,
|
||||
},
|
||||
[data],
|
||||
)
|
||||
@@ -80,7 +81,7 @@ export const ExternalEmbedLink = ({
|
||||
hasQuote,
|
||||
onRemove,
|
||||
}: {
|
||||
uri: string
|
||||
uri: UriString
|
||||
hasQuote: boolean
|
||||
onRemove: () => void
|
||||
}) => {
|
||||
@@ -100,7 +101,8 @@ export const ExternalEmbedLink = ({
|
||||
description:
|
||||
data.view?.external?.description || data.description,
|
||||
// prefer opengraph data to atproto record-derived image
|
||||
thumb: data.thumb?.source.path || data.view?.external?.thumb,
|
||||
thumb: (data.thumb?.source.path ||
|
||||
data.view?.external?.thumb) as UriString | undefined,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
@@ -111,7 +113,7 @@ export const ExternalEmbedLink = ({
|
||||
title: data.title || uri,
|
||||
uri,
|
||||
description: data.description,
|
||||
thumb: data.thumb?.source.path,
|
||||
thumb: data.thumb?.source.path as UriString | undefined,
|
||||
}}
|
||||
hideAlt
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Type converters for Draft API - convert between ComposerState and server Draft types.
|
||||
*/
|
||||
import {AppBskyDraftDefs, AtUri} from '@atproto/api'
|
||||
import {type UriString} from '@atproto/lex'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
@@ -22,6 +23,8 @@ import {type VideoState} from '#/view/com/composer/state/video'
|
||||
import {type AnalyticsContextType} from '#/analytics'
|
||||
import {getDeviceId} from '#/analytics/identifiers'
|
||||
import {type Gif} from '#/features/gifPicker/types'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {logger} from './logger'
|
||||
import {type DraftPostDisplay, type DraftSummary} from './schema'
|
||||
import * as storage from './storage'
|
||||
@@ -63,18 +66,18 @@ export async function composerStateToDraft(
|
||||
clients: LinkResolvers,
|
||||
state: ComposerState,
|
||||
): Promise<{
|
||||
draft: AppBskyDraftDefs.Draft
|
||||
draft: app.bsky.draft.defs.Draft
|
||||
localRefPaths: 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 => {
|
||||
return postDraftToServerPost(clients, post, localRefPaths)
|
||||
}),
|
||||
)
|
||||
|
||||
const draft: AppBskyDraftDefs.Draft = {
|
||||
const draft: app.bsky.draft.defs.Draft = {
|
||||
$type: 'app.bsky.draft.defs#draft',
|
||||
deviceId: getDeviceId(),
|
||||
deviceName: getDeviceName().slice(0, 100), // max length of 100 in lex
|
||||
@@ -99,8 +102,8 @@ async function postDraftToServerPost(
|
||||
clients: LinkResolvers,
|
||||
post: PostDraft,
|
||||
localRefPaths: Map<string, string>,
|
||||
): Promise<AppBskyDraftDefs.DraftPost> {
|
||||
const draftPost: AppBskyDraftDefs.DraftPost = {
|
||||
): Promise<app.bsky.draft.defs.DraftPost> {
|
||||
const draftPost: app.bsky.draft.defs.DraftPost = {
|
||||
$type: 'app.bsky.draft.defs#draftPost',
|
||||
text: post.richtext.text,
|
||||
}
|
||||
@@ -176,7 +179,7 @@ async function postDraftToServerPost(
|
||||
function serializeImages(
|
||||
images: ComposerImage[],
|
||||
localRefPaths: Map<string, string>,
|
||||
): AppBskyDraftDefs.DraftEmbedGalleryItems {
|
||||
): app.bsky.draft.defs.DraftEmbedGalleryItems {
|
||||
return images.map(image => {
|
||||
const sourcePath = image.transformed?.path || image.source.path
|
||||
// Reuse existing localRefPath if present (editing draft), otherwise generate new
|
||||
@@ -208,7 +211,7 @@ function serializeImages(
|
||||
async function serializeVideo(
|
||||
videoState: VideoState,
|
||||
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)
|
||||
if (!videoState.video) {
|
||||
return undefined
|
||||
@@ -221,7 +224,7 @@ async function serializeVideo(
|
||||
localRefPaths.set(localRefPath, videoState.video.uri)
|
||||
|
||||
// Read caption file contents as text
|
||||
const captions: AppBskyDraftDefs.DraftEmbedCaption[] = []
|
||||
const captions: app.bsky.draft.defs.DraftEmbedCaption[] = []
|
||||
for (const caption of videoState.captions) {
|
||||
if (caption.lang) {
|
||||
const content = await caption.file.text()
|
||||
@@ -252,7 +255,7 @@ function serializeGif(gifMedia: {
|
||||
type: 'gif'
|
||||
gif: Gif
|
||||
alt: string
|
||||
}): AppBskyDraftDefs.DraftEmbedExternal | undefined {
|
||||
}): app.bsky.draft.defs.DraftEmbedExternal | undefined {
|
||||
const gif = gifMedia.gif
|
||||
const gifFormat = gif.media_formats.gif || gif.media_formats.tinygif
|
||||
|
||||
@@ -273,7 +276,7 @@ function serializeGif(gifMedia: {
|
||||
|
||||
return {
|
||||
$type: 'app.bsky.draft.defs#draftEmbedExternal',
|
||||
uri: url.toString(),
|
||||
uri: url.toString() as UriString,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +285,7 @@ function serializeGif(gifMedia: {
|
||||
* both the `embedImages` and `embedGallery` paths in draftToComposerPosts.
|
||||
*/
|
||||
async function restoreDraftImages(
|
||||
draftImages: AppBskyDraftDefs.DraftEmbedImage[],
|
||||
draftImages: app.bsky.draft.defs.DraftEmbedImage[],
|
||||
loadedMedia: Map<string, string>,
|
||||
): Promise<ComposerImage[]> {
|
||||
const imagePromises = draftImages.map(async img => {
|
||||
@@ -338,7 +341,7 @@ export function draftViewToSummary({
|
||||
view,
|
||||
analytics,
|
||||
}: {
|
||||
view: AppBskyDraftDefs.DraftView
|
||||
view: app.bsky.draft.defs.DraftView
|
||||
analytics: AnalyticsContextType
|
||||
}): DraftSummary {
|
||||
const meta = {
|
||||
@@ -378,7 +381,7 @@ export function draftViewToSummary({
|
||||
// Process gallery
|
||||
if (post.embedGallery) {
|
||||
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.hasMedia = true
|
||||
const exists = storage.mediaExists(item.localRef.path)
|
||||
@@ -494,7 +497,7 @@ function parseGifFromUrl(
|
||||
* by initiating video processing for each entry.
|
||||
*/
|
||||
export async function draftToComposerPosts(
|
||||
draft: AppBskyDraftDefs.Draft,
|
||||
draft: app.bsky.draft.defs.Draft,
|
||||
loadedMedia: Map<string, string>,
|
||||
): Promise<{posts: PostDraft[]; restoredVideos: Map<number, RestoredVideo>}> {
|
||||
const restoredVideos = new Map<number, RestoredVideo>()
|
||||
@@ -523,8 +526,8 @@ export async function draftToComposerPosts(
|
||||
)
|
||||
}
|
||||
if (post.embedGallery && post.embedGallery.items.length > 0) {
|
||||
const galleryImages = post.embedGallery.items.filter(
|
||||
AppBskyDraftDefs.isDraftEmbedImage,
|
||||
const galleryImages = post.embedGallery.items.filter(item =>
|
||||
bsky.isType(app.bsky.draft.defs.draftEmbedImage, item),
|
||||
)
|
||||
restoredImages.push(
|
||||
...(await restoreDraftImages(galleryImages, loadedMedia)),
|
||||
@@ -605,7 +608,7 @@ export async function draftToComposerPosts(
|
||||
const record = post.embedRecords[0]
|
||||
const urip = new AtUri(record.record.uri)
|
||||
const url = `https://bsky.app/profile/${urip.host}/post/${urip.rkey}`
|
||||
embed.quote = {type: 'link', uri: url}
|
||||
embed.quote = {type: 'link', uri: url as UriString}
|
||||
}
|
||||
|
||||
// Restore link embed (only if not a GIF)
|
||||
@@ -644,7 +647,7 @@ export async function draftToComposerPosts(
|
||||
* Convert server threadgate rules back to UI settings.
|
||||
*/
|
||||
export function threadgateToUISettings(
|
||||
threadgateAllow?: AppBskyDraftDefs.Draft['threadgateAllow'],
|
||||
threadgateAllow?: app.bsky.draft.defs.Draft['threadgateAllow'],
|
||||
): Array<{type: string; list?: string}> {
|
||||
if (!threadgateAllow) {
|
||||
return []
|
||||
@@ -678,7 +681,9 @@ export function threadgateToUISettings(
|
||||
* Extract all localRef paths from a draft.
|
||||
* 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>()
|
||||
for (const post of draft.posts) {
|
||||
if (post.embedImages) {
|
||||
@@ -688,7 +693,7 @@ export function extractLocalRefs(draft: AppBskyDraftDefs.Draft): Set<string> {
|
||||
}
|
||||
if (post.embedGallery) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {AppBskyDraftDefs} from '@atproto/api'
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
useMutation,
|
||||
@@ -12,6 +11,7 @@ import {type ComposerState} from '#/view/com/composer/state/composer'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {getDeviceId} from '#/analytics/identifiers'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {composerStateToDraft, draftViewToSummary} from './api'
|
||||
import {logger} from './logger'
|
||||
import * as storage from './storage'
|
||||
@@ -52,7 +52,9 @@ export function useDraftsQuery() {
|
||||
* Load a draft's local media for editing.
|
||||
* 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>
|
||||
}> {
|
||||
// Load local media files
|
||||
@@ -81,7 +83,7 @@ export async function loadDraftMedia(draft: AppBskyDraftDefs.Draft): Promise<{
|
||||
// Load gallery
|
||||
if (post.embedGallery) {
|
||||
for (const item of post.embedGallery.items) {
|
||||
if (!AppBskyDraftDefs.isDraftEmbedImage(item)) continue
|
||||
if (!bsky.isType(app.bsky.draft.defs.draftEmbedImage, item)) continue
|
||||
try {
|
||||
const url = await storage.loadMediaFromLocal(item.localRef.path)
|
||||
loadedMedia.set(item.localRef.path, url)
|
||||
@@ -142,11 +144,10 @@ export function useSaveDraftMutation() {
|
||||
composerState,
|
||||
)
|
||||
/*
|
||||
* `composerStateToDraft` builds the draft against the `@atproto/api`
|
||||
* types, whose string fields are unbranded, so it is asserted once here
|
||||
* to the vendored input type.
|
||||
* `composerStateToDraft` builds the draft with unbranded string fields,
|
||||
* so it is asserted once here to the generated input type.
|
||||
*/
|
||||
const draft = apiDraft as unknown as app.bsky.draft.defs.Draft
|
||||
const draft = apiDraft
|
||||
|
||||
logger.debug('saving draft', {
|
||||
existingDraftId,
|
||||
@@ -242,7 +243,7 @@ export function useDeleteDraftMutation() {
|
||||
draftId,
|
||||
}: {
|
||||
draftId: string
|
||||
draft: AppBskyDraftDefs.Draft
|
||||
draft: app.bsky.draft.defs.Draft
|
||||
}) => {
|
||||
// Delete from server first - if this fails, we keep local media for retry
|
||||
await client.call(app.bsky.draft.deleteDraft, {id: draftId})
|
||||
@@ -257,7 +258,8 @@ export function useDeleteDraftMutation() {
|
||||
}
|
||||
if (post.embedGallery) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {type app} from '#/lexicons'
|
||||
/**
|
||||
* Types for draft display and local media tracking.
|
||||
* Server draft types come from @atproto/api.
|
||||
* Server draft types come from the generated `#/lexicons` modules.
|
||||
*/
|
||||
import {type AppBskyDraftDefs} from '@atproto/api'
|
||||
|
||||
/**
|
||||
* Reference to locally cached media file for display
|
||||
@@ -55,7 +55,7 @@ export type DraftSummary = {
|
||||
/** ISO timestamp of last update */
|
||||
updatedAt: string
|
||||
/** The full draft data from the server */
|
||||
draft: AppBskyDraftDefs.Draft
|
||||
draft: app.bsky.draft.defs.Draft
|
||||
/** All posts in the draft for full display */
|
||||
posts: DraftPostDisplay[]
|
||||
/** Metadata about the draft for display purposes */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {type ImagePickerAsset} from 'expo-image-picker'
|
||||
import {type AppBskyActorDefs, type AppBskyDraftDefs} from '@atproto/api'
|
||||
import {type UriString} from '@atproto/lex'
|
||||
import {type AtUriString, toDatetimeString} from '@atproto/syntax'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
@@ -56,7 +56,12 @@ type GifMedia = {
|
||||
|
||||
type Link = {
|
||||
type: 'link'
|
||||
uri: string
|
||||
/*
|
||||
* A URL the user typed or pasted, validated by the link resolver rather than
|
||||
* at construction, so it carries lex's `uri` brand for the record and view
|
||||
* slots it flows into.
|
||||
*/
|
||||
uri: UriString
|
||||
}
|
||||
|
||||
// This structure doesn't exactly correspond to the data model.
|
||||
@@ -91,7 +96,7 @@ export type PostAction =
|
||||
}
|
||||
| {type: 'embed_remove_video'}
|
||||
| {type: 'embed_update_video'; videoAction: VideoAction}
|
||||
| {type: 'embed_add_uri'; uri: string}
|
||||
| {type: 'embed_add_uri'; uri: UriString}
|
||||
| {type: 'embed_remove_quote'}
|
||||
| {type: 'embed_remove_link'}
|
||||
| {type: 'embed_add_gif'; gif: Gif}
|
||||
@@ -141,8 +146,8 @@ export type ComposerAction =
|
||||
type: 'restore_from_draft'
|
||||
draftId: string
|
||||
posts: PostDraft[]
|
||||
threadgateAllow: AppBskyDraftDefs.Draft['threadgateAllow']
|
||||
postgateEmbeddingRules: AppBskyDraftDefs.Draft['postgateEmbeddingRules']
|
||||
threadgateAllow: app.bsky.draft.defs.Draft['threadgateAllow']
|
||||
postgateEmbeddingRules: app.bsky.draft.defs.Draft['postgateEmbeddingRules']
|
||||
|
||||
/** Map of localRefPath -> loaded media path/URL */
|
||||
loadedMedia: Map<string, string>
|
||||
@@ -152,7 +157,7 @@ export type ComposerAction =
|
||||
| {
|
||||
type: 'clear'
|
||||
initInteractionSettings:
|
||||
| AppBskyActorDefs.PostInteractionSettingsPref
|
||||
| app.bsky.actor.defs.PostInteractionSettingsPref
|
||||
| undefined
|
||||
}
|
||||
| {
|
||||
@@ -320,14 +325,13 @@ export function composerReducer(
|
||||
* Draft records are still typed against the legacy client, so the
|
||||
* stored rules arrive unbranded. Wave B migrates the draft types.
|
||||
*/
|
||||
embeddingRules:
|
||||
postgateEmbeddingRules as app.bsky.feed.postgate.Main['embeddingRules'],
|
||||
embeddingRules: postgateEmbeddingRules,
|
||||
}),
|
||||
threadgate: threadgateRecordToAllowUISetting({
|
||||
$type: 'app.bsky.feed.threadgate',
|
||||
post: '' as AtUriString,
|
||||
createdAt: toDatetimeString(new Date()),
|
||||
allow: threadgateAllow as app.bsky.feed.threadgate.Main['allow'],
|
||||
allow: threadgateAllow,
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -632,7 +636,7 @@ export function createComposerState({
|
||||
initImageUris: ComposerOpts['imageUris']
|
||||
initQuoteUri: string | undefined
|
||||
initInteractionSettings:
|
||||
| AppBskyActorDefs.PostInteractionSettingsPref
|
||||
| app.bsky.actor.defs.PostInteractionSettingsPref
|
||||
| undefined
|
||||
}): ComposerState {
|
||||
let media: ImagesMedia | GalleryMedia | undefined
|
||||
@@ -646,7 +650,7 @@ export function createComposerState({
|
||||
if (path) {
|
||||
quote = {
|
||||
type: 'link',
|
||||
uri: toBskyAppUrl(path),
|
||||
uri: toBskyAppUrl(path) as UriString,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,7 +704,7 @@ export function createComposerState({
|
||||
if (suggestedExtUri) {
|
||||
link = {
|
||||
type: 'link',
|
||||
uri: suggestedExtUri,
|
||||
uri: suggestedExtUri as UriString,
|
||||
}
|
||||
}
|
||||
const suggestedPostUri = suggestLinkCardUri(
|
||||
@@ -717,7 +721,7 @@ export function createComposerState({
|
||||
if (!quote) {
|
||||
quote = {
|
||||
type: 'link',
|
||||
uri: suggestedPostUri,
|
||||
uri: suggestedPostUri as UriString,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -750,15 +754,13 @@ export function createComposerState({
|
||||
* Preferences are still typed against the legacy client, so the stored
|
||||
* rules arrive unbranded. Wave B migrates `getPreferences`.
|
||||
*/
|
||||
embeddingRules: (initInteractionSettings?.postgateEmbeddingRules ||
|
||||
[]) as app.bsky.feed.postgate.Main['embeddingRules'],
|
||||
embeddingRules: initInteractionSettings?.postgateEmbeddingRules || [],
|
||||
}),
|
||||
threadgate: threadgateRecordToAllowUISetting({
|
||||
$type: 'app.bsky.feed.threadgate',
|
||||
post: '' as AtUriString,
|
||||
createdAt: toDatetimeString(new Date()),
|
||||
allow:
|
||||
initInteractionSettings?.threadgateAllowRules as app.bsky.feed.threadgate.Main['allow'],
|
||||
allow: initInteractionSettings?.threadgateAllowRules,
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {View} from 'react-native'
|
||||
import Animated, {FadeInDown, FadeOut} from 'react-native-reanimated'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {PressableScale} from '#/lib/custom-animations/PressableScale'
|
||||
@@ -11,6 +10,7 @@ import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {atoms as a, platform, useTheme} from '#/alf'
|
||||
import {ProfileBadges} from '#/components/ProfileBadges'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
export function Autocomplete({
|
||||
prefix,
|
||||
@@ -70,7 +70,7 @@ function AutocompleteProfileCard({
|
||||
totalItems,
|
||||
onPress,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewBasic
|
||||
profile: app.bsky.actor.defs.ProfileViewBasic
|
||||
itemIndex: number
|
||||
totalItems: number
|
||||
onPress: () => void
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {forwardRef, useEffect, useImperativeHandle, useState} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {ReactRenderer} from '@tiptap/react'
|
||||
@@ -16,6 +15,7 @@ import {type ActorAutocompleteFn} from '#/state/queries/actor-autocomplete'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
interface MentionListRef {
|
||||
onKeyDown: (props: SuggestionKeyDownProps) => boolean
|
||||
@@ -205,7 +205,7 @@ function AutocompleteProfileCard({
|
||||
onHover,
|
||||
moderationOpts,
|
||||
}: {
|
||||
profile: AppBskyActorDefs.ProfileViewBasic
|
||||
profile: app.bsky.actor.defs.ProfileViewBasic
|
||||
isSelected: boolean
|
||||
onPress: () => void
|
||||
onHover: () => void
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* the facet-set.
|
||||
*/
|
||||
|
||||
import {URL_REGEX} from '@atproto/api'
|
||||
import {URL_REGEX} from '@bsky.app/sdk/richtext'
|
||||
import {Mark} from '@tiptap/core'
|
||||
import {type Node as ProsemirrorNode} from '@tiptap/pm/model'
|
||||
import {Plugin, PluginKey} from '@tiptap/pm/state'
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
CASHTAG_REGEX,
|
||||
TAG_REGEX,
|
||||
TRAILING_PUNCTUATION_REGEX,
|
||||
} from '@atproto/api'
|
||||
} from '@bsky.app/sdk/richtext'
|
||||
import {Mark} from '@tiptap/core'
|
||||
import {type Node as ProsemirrorNode} from '@tiptap/pm/model'
|
||||
import {Plugin, PluginKey} from '@tiptap/pm/state'
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {type NavigationProp, useNavigation} from '@react-navigation/native'
|
||||
@@ -38,6 +37,7 @@ import {useHeaderOffset} from '#/components/hooks/useHeaderOffset'
|
||||
import {EditBig_Stroke2_Corner2_Rounded as EditBigIcon} from '#/components/icons/EditBig'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
|
||||
const POLL_FREQ = 60e3 // 60sec
|
||||
|
||||
@@ -59,7 +59,7 @@ export function FeedPage({
|
||||
isPageAdjacent: boolean
|
||||
renderEmptyState: () => JSX.Element
|
||||
renderEndOfFeed?: () => JSX.Element
|
||||
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
||||
savedFeedConfig?: app.bsky.actor.defs.SavedFeed
|
||||
feedInfo: FeedSourceInfo
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
@@ -77,7 +77,7 @@ export function FeedPage({
|
||||
const isVideoFeed = useMemo(() => {
|
||||
const isBskyVideoFeed = VIDEO_FEED_URIS.includes(feedInfo.uri)
|
||||
const feedIsVideoMode =
|
||||
feedInfo.contentMode === AppBskyFeedDefs.CONTENTMODEVIDEO
|
||||
feedInfo.contentMode === app.bsky.feed.defs.contentModeVideo.value
|
||||
const _isVideoFeed = isBskyVideoFeed || feedIsVideoMode
|
||||
return IS_NATIVE && _isVideoFeed
|
||||
}, [feedInfo])
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {
|
||||
type $Typed,
|
||||
AppBskyFeedDefs,
|
||||
type AppBskyGraphDefs,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
import {type $Typed} from '@atproto/lex'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
@@ -22,13 +18,15 @@ import {atoms as a, useTheme} from '#/alf'
|
||||
import {Link} from '#/components/Link'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {MissingFeed} from './MissingFeed'
|
||||
|
||||
type FeedSourceCardProps = {
|
||||
feedUri: string
|
||||
feedData?:
|
||||
| $Typed<AppBskyFeedDefs.GeneratorView>
|
||||
| $Typed<AppBskyGraphDefs.ListView>
|
||||
| $Typed<app.bsky.feed.defs.GeneratorView>
|
||||
| $Typed<app.bsky.graph.defs.ListView>
|
||||
style?: StyleProp<ViewStyle>
|
||||
showSaveBtn?: boolean
|
||||
showDescription?: boolean
|
||||
@@ -46,7 +44,7 @@ export function FeedSourceCard({
|
||||
}: FeedSourceCardProps) {
|
||||
if (feedData) {
|
||||
let feed: FeedSourceInfo
|
||||
if (AppBskyFeedDefs.isGeneratorView(feedData)) {
|
||||
if (bsky.isType(app.bsky.feed.defs.generatorView, feedData)) {
|
||||
feed = hydrateFeedGenerator(feedData)
|
||||
} else {
|
||||
feed = hydrateList(feedData)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
View,
|
||||
type ViewStyle,
|
||||
} from 'react-native'
|
||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -26,6 +25,7 @@ import {useDialogControl} from '#/components/Dialog'
|
||||
import {UserAddRemoveListsDialog} from '#/components/dialogs/lists/UserAddRemoveListsDialog'
|
||||
import {ListFooter} from '#/components/Lists'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {type app} from '#/lexicons'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
|
||||
const LOADING_ITEM = {kind: 'loading', _reactKey: '__loading__'} as const
|
||||
@@ -43,7 +43,7 @@ type Item =
|
||||
| typeof LOAD_MORE_ERROR_ITEM
|
||||
| {
|
||||
kind: 'list_item'
|
||||
listItem: AppBskyGraphDefs.ListItemView
|
||||
listItem: app.bsky.graph.defs.ListItemView
|
||||
}
|
||||
|
||||
export function ListMembers({
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
View,
|
||||
type ViewStyle,
|
||||
} from 'react-native'
|
||||
import {type AppBskyGraphDefs as GraphDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
@@ -21,6 +20,7 @@ import {atoms as a, useTheme} from '#/alf'
|
||||
import {BulletList_Stroke1_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList'
|
||||
import * as ListCard from '#/components/ListCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {type app} from '#/lexicons'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {List} from '../util/List'
|
||||
|
||||
@@ -38,7 +38,10 @@ export function MyLists({
|
||||
filter: MyListsFilter
|
||||
inline?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element
|
||||
renderItem?: (
|
||||
list: app.bsky.graph.defs.ListView,
|
||||
index: number,
|
||||
) => JSX.Element
|
||||
testID?: string
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
|
||||
@@ -8,18 +8,10 @@ import {
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
type AppBskyGraphDefs,
|
||||
AppBskyGraphFollow,
|
||||
AppBskyGraphStarterpack,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
import {TID} from '@atproto/common-web'
|
||||
import {type DidString} from '@atproto/syntax'
|
||||
import {AtUri, type DidString} from '@atproto/syntax'
|
||||
import {
|
||||
moderateProfile,
|
||||
type ModerationDecision,
|
||||
type ModerationOpts,
|
||||
} from '@bsky.app/sdk/moderation'
|
||||
@@ -30,7 +22,6 @@ import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
import {useAnimatedValue} from '#/lib/hooks/useAnimatedValue'
|
||||
import {moderateProfile} from '#/lib/moderation/subjects'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {forceLTR} from '#/lib/strings/bidi'
|
||||
@@ -76,13 +67,13 @@ import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {chat} from '#/lexicons'
|
||||
import {app, chat} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
const MAX_AUTHORS = 5
|
||||
|
||||
interface Author {
|
||||
profile: AppBskyActorDefs.ProfileView
|
||||
profile: app.bsky.actor.defs.ProfileView
|
||||
href: string
|
||||
moderation: ModerationDecision
|
||||
}
|
||||
@@ -195,10 +186,7 @@ let NotificationFeedItem = ({
|
||||
if (item.type !== 'follow') return false
|
||||
if (
|
||||
item.notification.author.viewer?.following &&
|
||||
bsky.dangerousIsType<AppBskyGraphFollow.Record>(
|
||||
item.notification.record,
|
||||
AppBskyGraphFollow.isRecord,
|
||||
)
|
||||
bsky.isType(app.bsky.graph.follow, item.notification.record)
|
||||
) {
|
||||
let followingTimestamp
|
||||
try {
|
||||
@@ -734,7 +722,7 @@ export {NotificationFeedItem}
|
||||
function FollowedViaStarterPack({
|
||||
starterPack,
|
||||
}: {
|
||||
starterPack: AppBskyGraphDefs.StarterPackViewBasic
|
||||
starterPack: app.bsky.graph.defs.StarterPackViewBasic
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const link = useStarterPackLink({view: starterPack})
|
||||
@@ -768,12 +756,9 @@ function FollowedViaStarterPack({
|
||||
}
|
||||
|
||||
function getStarterPackName(
|
||||
starterPack: AppBskyGraphDefs.StarterPackViewBasic,
|
||||
starterPack: app.bsky.graph.defs.StarterPackViewBasic,
|
||||
) {
|
||||
return bsky.dangerousIsType<AppBskyGraphStarterpack.Record>(
|
||||
starterPack.record,
|
||||
AppBskyGraphStarterpack.isRecord,
|
||||
)
|
||||
return bsky.isType(app.bsky.graph.starterpack, starterPack.record)
|
||||
? starterPack.record.name
|
||||
: 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 {currentAccount, hasSession} = useSession()
|
||||
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 client = useChatClient()
|
||||
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()
|
||||
if (
|
||||
post &&
|
||||
bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post?.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
)
|
||||
) {
|
||||
if (post && bsky.isType(app.bsky.feed.post, post?.record)) {
|
||||
const text = post.record.text
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {type AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
@@ -11,8 +10,15 @@ import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
|
||||
import {List} from '#/view/com/util/List'
|
||||
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
function renderItem({item, index}: {item: GetLikes.Like; index: number}) {
|
||||
function renderItem({
|
||||
item,
|
||||
index,
|
||||
}: {
|
||||
item: app.bsky.feed.getLikes.Like
|
||||
index: number
|
||||
}) {
|
||||
return (
|
||||
<ProfileCardWithFollowBtn
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import {useCallback, useState} from 'react'
|
||||
import {type AppBskyFeedDefs, AppBskyFeedPost} from '@atproto/api'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {moderatePost, type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
|
||||
import {usePostViewTracking} from '#/lib/hooks/usePostViewTracking'
|
||||
import {moderatePost} from '#/lib/moderation/subjects'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
@@ -14,6 +12,7 @@ import {usePostQuotesQuery} from '#/state/queries/post-quotes'
|
||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {Post} from '#/view/com/post/Post'
|
||||
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {List} from '../util/List'
|
||||
|
||||
@@ -22,9 +21,9 @@ function renderItem({
|
||||
index,
|
||||
}: {
|
||||
item: {
|
||||
post: AppBskyFeedDefs.PostView
|
||||
post: app.bsky.feed.defs.PostView
|
||||
moderation: ModerationDecision
|
||||
record: AppBskyFeedPost.Record
|
||||
record: app.bsky.feed.post.Main
|
||||
}
|
||||
index: number
|
||||
}) {
|
||||
@@ -32,9 +31,9 @@ function renderItem({
|
||||
}
|
||||
|
||||
function keyExtractor(item: {
|
||||
post: AppBskyFeedDefs.PostView
|
||||
post: app.bsky.feed.defs.PostView
|
||||
moderation: ModerationDecision
|
||||
record: AppBskyFeedPost.Record
|
||||
record: app.bsky.feed.post.Main
|
||||
}) {
|
||||
return item.post.uri
|
||||
}
|
||||
@@ -69,10 +68,7 @@ export function PostQuotes({uri}: {uri: string}) {
|
||||
.flatMap(page =>
|
||||
page.posts.map(post => {
|
||||
if (
|
||||
!bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
) ||
|
||||
!bsky.isType(app.bsky.feed.post, post.record) ||
|
||||
!moderationOpts
|
||||
) {
|
||||
return null
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
@@ -11,12 +10,13 @@ import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
|
||||
import {List} from '#/view/com/util/List'
|
||||
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
function renderItem({
|
||||
item,
|
||||
index,
|
||||
}: {
|
||||
item: ActorDefs.ProfileView
|
||||
item: app.bsky.actor.defs.ProfileView
|
||||
index: number
|
||||
}) {
|
||||
return (
|
||||
@@ -28,7 +28,7 @@ function renderItem({
|
||||
)
|
||||
}
|
||||
|
||||
function keyExtractor(item: ActorDefs.ProfileView) {
|
||||
function keyExtractor(item: app.bsky.actor.defs.ProfileView) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import {useCallback, useMemo, useState} from 'react'
|
||||
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 {AtUri} from '@atproto/syntax'
|
||||
import {moderatePost, type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {moderatePost} from '#/lib/moderation/subjects'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -36,6 +34,7 @@ import {TranslatedPost} from '#/components/Post/Translated'
|
||||
import {PostControls} from '#/components/PostControls'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {SubtleHover} from '#/components/SubtleHover'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
export function Post({
|
||||
@@ -45,18 +44,16 @@ export function Post({
|
||||
style,
|
||||
onBeforePress,
|
||||
}: {
|
||||
post: AppBskyFeedDefs.PostView
|
||||
post: app.bsky.feed.defs.PostView
|
||||
showReplyLine?: boolean
|
||||
hideTopBorder?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
onBeforePress?: () => void
|
||||
}) {
|
||||
const moderationOpts = useModerationOpts()
|
||||
const record = useMemo<AppBskyFeedPost.Record | undefined>(
|
||||
const record = useMemo<app.bsky.feed.post.Main | undefined>(
|
||||
() =>
|
||||
bsky.validate(post.record, AppBskyFeedPost.validateRecord)
|
||||
? post.record
|
||||
: undefined,
|
||||
bsky.matches(app.bsky.feed.post, post.record) ? post.record : undefined,
|
||||
[post],
|
||||
)
|
||||
const postShadowed = usePostShadow(post)
|
||||
@@ -65,7 +62,7 @@ export function Post({
|
||||
record
|
||||
? new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: asSdkFacets(record.facets),
|
||||
facets: record.facets,
|
||||
})
|
||||
: undefined,
|
||||
[record],
|
||||
@@ -106,8 +103,8 @@ function PostInner({
|
||||
style,
|
||||
onBeforePress: outerOnBeforePress,
|
||||
}: {
|
||||
post: Shadow<AppBskyFeedDefs.PostView>
|
||||
record: AppBskyFeedPost.Record
|
||||
post: Shadow<app.bsky.feed.defs.PostView>
|
||||
record: app.bsky.feed.post.Main
|
||||
richText: RichTextAPI
|
||||
moderation: ModerationDecision
|
||||
showReplyLine?: boolean
|
||||
|
||||
@@ -18,14 +18,6 @@ import {
|
||||
View,
|
||||
type ViewStyle,
|
||||
} 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 {useLingui} from '@lingui/react/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -82,6 +74,8 @@ import {
|
||||
isStatusValidForViewers,
|
||||
useLiveNowConfig,
|
||||
} from '#/features/liveNow'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {ComposerPrompt} from '../feeds/ComposerPrompt'
|
||||
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
|
||||
import {FeedShutdownMsg} from './FeedShutdownMsg'
|
||||
@@ -256,7 +250,7 @@ let PostFeed = ({
|
||||
desktopFixedHeightOffset?: number
|
||||
ListHeaderComponent?: () => React.ReactElement
|
||||
extraData?: Record<string, unknown>
|
||||
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
||||
savedFeedConfig?: app.bsky.actor.defs.SavedFeed
|
||||
initialNumToRender?: number
|
||||
isVideoFeed?: boolean
|
||||
lastFetchDate?: () => number
|
||||
@@ -290,7 +284,7 @@ let PostFeed = ({
|
||||
() => new Set<string>(),
|
||||
)
|
||||
const onPressShowLess = useCallback(
|
||||
(interaction: AppBskyFeedDefs.Interaction) => {
|
||||
(interaction: app.bsky.feed.defs.Interaction) => {
|
||||
if (interaction.item) {
|
||||
const uri = interaction.item
|
||||
setHasPressedShowLessUris(prev => new Set([...prev, uri]))
|
||||
@@ -492,7 +486,7 @@ let PostFeed = ({
|
||||
)
|
||||
if (
|
||||
item &&
|
||||
AppBskyEmbedVideo.isView(item.post.embed) &&
|
||||
bsky.isType(app.bsky.embed.video.view, item.post.embed) &&
|
||||
!blockedOrMutedAuthors.includes(item.post.author.did)
|
||||
) {
|
||||
videos.push({
|
||||
@@ -1030,13 +1024,13 @@ let PostFeed = ({
|
||||
|
||||
// Events that should fire exactly once for every new post, regardless of
|
||||
// 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
|
||||
seenPerPostUrisRef.current.add(post.uri)
|
||||
|
||||
// Standard site embed view tracking
|
||||
if (
|
||||
AppBskyEmbedExternal.isView(post.embed) &&
|
||||
bsky.isType(app.bsky.embed.external.view, post.embed) &&
|
||||
isStandardSiteEmbed(post.embed.external)
|
||||
) {
|
||||
ax.metric('embed:standardSite:view', {url: post.embed.external.uri})
|
||||
@@ -1044,13 +1038,21 @@ let PostFeed = ({
|
||||
|
||||
// Photo embed impression tracking
|
||||
if (
|
||||
AppBskyEmbedImages.isView(post.embed) ||
|
||||
AppBskyEmbedGallery.isView(post.embed)
|
||||
bsky.isType(app.bsky.embed.images.view, post.embed) ||
|
||||
bsky.isType(app.bsky.embed.gallery.view, post.embed)
|
||||
) {
|
||||
const totalImages = AppBskyEmbedGallery.isView(post.embed)
|
||||
? post.embed.items.filter(AppBskyEmbedGallery.isViewImage).length
|
||||
const totalImages = bsky.isType(
|
||||
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
|
||||
const useExpandedLayout = AppBskyEmbedGallery.isView(post.embed)
|
||||
const useExpandedLayout = bsky.isType(
|
||||
app.bsky.embed.gallery.view,
|
||||
post.embed,
|
||||
)
|
||||
? totalImages > 4
|
||||
: ax.features.enabled(ax.features.PostGalleryEmbedEnable)
|
||||
const layout =
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
AppBskyFeedGetAuthorFeed,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {msg as msgLingui} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -13,11 +9,13 @@ import {useNavigation} from '@react-navigation/native'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {type NavigationProp} from '#/lib/routes/types'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {getErrorName, getErrorStatus} from '#/lib/xrpc-error'
|
||||
import {logger} from '#/logger'
|
||||
import {type FeedDescriptor} from '#/state/queries/post-feed'
|
||||
import {useRemoveFeedMutation} from '#/state/queries/preferences'
|
||||
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {type app} from '#/lexicons'
|
||||
import {EmptyState} from '../util/EmptyState'
|
||||
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||
import {Button} from '../util/forms/Button'
|
||||
@@ -45,7 +43,7 @@ export function PostFeedErrorMessage({
|
||||
feedDesc: FeedDescriptor
|
||||
error?: Error
|
||||
onPressTryAgain: () => void
|
||||
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
||||
savedFeedConfig?: app.bsky.actor.defs.SavedFeed
|
||||
}) {
|
||||
const {_: _l} = useLingui()
|
||||
const knownError = useMemo(
|
||||
@@ -96,7 +94,7 @@ function FeedgenErrorMessage({
|
||||
feedDesc: FeedDescriptor
|
||||
knownError: KnownError
|
||||
rawError?: Error
|
||||
savedFeedConfig?: AppBskyActorDefs.SavedFeed
|
||||
savedFeedConfig?: app.bsky.actor.defs.SavedFeed
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {_: _l} = useLingui()
|
||||
@@ -241,15 +239,21 @@ function detectKnownError(
|
||||
if (!error) {
|
||||
return undefined
|
||||
}
|
||||
/*
|
||||
* Both names are declared by `app.bsky.feed.getAuthorFeed` AND
|
||||
* `.getActorLikes`, and this helper takes an error from an arbitrary feed
|
||||
* descriptor, so the source method is ambiguous - hence the untyped
|
||||
* `getErrorName` check rather than `matchXrpcError`.
|
||||
*/
|
||||
if (
|
||||
error instanceof AppBskyFeedGetAuthorFeed.BlockedActorError ||
|
||||
error instanceof AppBskyFeedGetAuthorFeed.BlockedByActorError
|
||||
getErrorName(error) === 'BlockedActor' ||
|
||||
getErrorName(error) === 'BlockedByActor'
|
||||
) {
|
||||
return KnownError.Block
|
||||
}
|
||||
|
||||
// check status codes
|
||||
if (error?.status === 429) {
|
||||
if (getErrorStatus(error) === 429) {
|
||||
return KnownError.FeedTooManyRequests
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import {memo, useCallback, useMemo, useState} from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {
|
||||
type AppBskyActorDefs,
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -17,7 +11,6 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -54,19 +47,20 @@ import {RichText} from '#/components/RichText'
|
||||
import {SubtleHover} from '#/components/SubtleHover'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {PostFeedReason} from './PostFeedReason'
|
||||
|
||||
interface FeedItemProps {
|
||||
record: AppBskyFeedPost.Record
|
||||
record: app.bsky.feed.post.Main
|
||||
reason:
|
||||
| AppBskyFeedDefs.ReasonRepost
|
||||
| AppBskyFeedDefs.ReasonPin
|
||||
| app.bsky.feed.defs.ReasonRepost
|
||||
| app.bsky.feed.defs.ReasonPin
|
||||
| ReasonFeedSource
|
||||
| {[k: string]: unknown; $type: string}
|
||||
| undefined
|
||||
moderation: ModerationDecision
|
||||
parentAuthor: AppBskyActorDefs.ProfileViewBasic | undefined
|
||||
parentAuthor: app.bsky.actor.defs.ProfileViewBasic | undefined
|
||||
showReplyTo: boolean
|
||||
isThreadChild?: boolean
|
||||
isThreadLastChild?: boolean
|
||||
@@ -96,16 +90,16 @@ export function PostFeedItem({
|
||||
rootPost,
|
||||
onShowLess,
|
||||
}: FeedItemProps & {
|
||||
post: AppBskyFeedDefs.PostView
|
||||
rootPost: AppBskyFeedDefs.PostView
|
||||
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void
|
||||
post: app.bsky.feed.defs.PostView
|
||||
rootPost: app.bsky.feed.defs.PostView
|
||||
onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
|
||||
}): React.ReactNode {
|
||||
const postShadowed = usePostShadow(post)
|
||||
const richText = useMemo(
|
||||
() =>
|
||||
new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: asSdkFacets(record.facets),
|
||||
facets: record.facets,
|
||||
}),
|
||||
[record],
|
||||
)
|
||||
@@ -160,9 +154,9 @@ let FeedItemInner = ({
|
||||
onShowLess,
|
||||
}: FeedItemProps & {
|
||||
richText: RichTextAPI
|
||||
post: Shadow<AppBskyFeedDefs.PostView>
|
||||
rootPost: AppBskyFeedDefs.PostView
|
||||
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void
|
||||
post: Shadow<app.bsky.feed.defs.PostView>
|
||||
rootPost: app.bsky.feed.defs.PostView
|
||||
onShowLess?: (interaction: app.bsky.feed.defs.Interaction) => void
|
||||
}): React.ReactNode => {
|
||||
const ax = useAnalytics()
|
||||
const queryClient = useQueryClient()
|
||||
@@ -258,7 +252,14 @@ let FeedItemInner = ({
|
||||
feedSourceInfo,
|
||||
post: {
|
||||
post,
|
||||
reason: AppBskyFeedDefs.isReasonRepost(reason) ? reason : undefined,
|
||||
/*
|
||||
* `isType` requires a present `$type` at runtime but narrows to the
|
||||
* schema's input type, whose `$type` is optional, so the `$Typed` arm
|
||||
* of `FeedViewPost['reason']` needs the assertion back.
|
||||
*/
|
||||
reason: bsky.isType(app.bsky.feed.defs.reasonRepost, reason)
|
||||
? (reason as app.bsky.feed.defs.FeedViewPost['reason'])
|
||||
: undefined,
|
||||
feedContext,
|
||||
reqId,
|
||||
},
|
||||
@@ -282,9 +283,9 @@ let FeedItemInner = ({
|
||||
* If `post[0]` in this slice is the actual root post (not an orphan thread),
|
||||
* 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,
|
||||
AppBskyFeedThreadgate.isRecord,
|
||||
)
|
||||
? rootPost.threadgate.record
|
||||
: undefined
|
||||
@@ -292,7 +293,11 @@ let FeedItemInner = ({
|
||||
const {isActive: live} = useActorStatus(post.author)
|
||||
|
||||
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 {
|
||||
uri: reason.uri,
|
||||
cid: reason.cid,
|
||||
@@ -305,10 +310,7 @@ let FeedItemInner = ({
|
||||
})
|
||||
const additionalPostAlerts: AppModerationCause[] = useMemo(() => {
|
||||
const isPostHiddenByThreadgate = threadgateHiddenReplies.has(post.uri)
|
||||
const rootPostUri = bsky.dangerousIsType<AppBskyFeedPost.Record>(
|
||||
post.record,
|
||||
AppBskyFeedPost.isRecord,
|
||||
)
|
||||
const rootPostUri = bsky.isType(app.bsky.feed.post, post.record)
|
||||
? post.record?.reply?.root?.uri || post.uri
|
||||
: undefined
|
||||
const isControlledByViewer =
|
||||
@@ -465,10 +467,10 @@ let PostContent = ({
|
||||
}: {
|
||||
moderation: ModerationDecision
|
||||
richText: RichTextAPI
|
||||
postEmbed: AppBskyFeedDefs.PostView['embed']
|
||||
postAuthor: AppBskyFeedDefs.PostView['author']
|
||||
postEmbed: app.bsky.feed.defs.PostView['embed']
|
||||
postAuthor: app.bsky.feed.defs.PostView['author']
|
||||
onOpenEmbed: () => void
|
||||
post: AppBskyFeedDefs.PostView
|
||||
post: app.bsky.feed.defs.PostView
|
||||
additionalPostAlerts?: AppModerationCause[]
|
||||
feedDescriptor?: string
|
||||
}): React.ReactNode => {
|
||||
@@ -476,11 +478,9 @@ let PostContent = ({
|
||||
() => 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)
|
||||
? post.record
|
||||
: undefined,
|
||||
bsky.matches(app.bsky.feed.post, post.record) ? post.record : undefined,
|
||||
[post],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -15,6 +14,8 @@ import {Repost_Stroke2_Corner3_Rounded as RepostIcon} from '#/components/icons/R
|
||||
import {Link} from '#/components/Link'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {FeedNameText} from '../util/FeedInfoText'
|
||||
|
||||
export function PostFeedReason({
|
||||
@@ -24,8 +25,8 @@ export function PostFeedReason({
|
||||
}: {
|
||||
reason:
|
||||
| ReasonFeedSource
|
||||
| AppBskyFeedDefs.ReasonRepost
|
||||
| AppBskyFeedDefs.ReasonPin
|
||||
| app.bsky.feed.defs.ReasonRepost
|
||||
| app.bsky.feed.defs.ReasonPin
|
||||
| {[k: string]: unknown; $type: string}
|
||||
moderation?: ModerationDecision
|
||||
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 reposter = createSanitizedDisplayName(
|
||||
reason.by,
|
||||
@@ -103,7 +104,7 @@ export function PostFeedReason({
|
||||
)
|
||||
}
|
||||
|
||||
if (AppBskyFeedDefs.isReasonPin(reason)) {
|
||||
if (bsky.isType(app.bsky.feed.defs.reasonPin, reason)) {
|
||||
return (
|
||||
<View style={styles.includeReason}>
|
||||
<PinIcon
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import Svg, {Circle, Line} from 'react-native-svg'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/syntax'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -19,6 +18,7 @@ import {
|
||||
FollowersPromoBanner,
|
||||
useFollowersPromoDismissed,
|
||||
} from '#/features/inviteFriends'
|
||||
import {type app} from '#/lexicons'
|
||||
import {List} from '../util/List'
|
||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
|
||||
@@ -27,7 +27,7 @@ function renderItem({
|
||||
index,
|
||||
contextProfileDid,
|
||||
}: {
|
||||
item: ActorDefs.ProfileView
|
||||
item: app.bsky.actor.defs.ProfileView
|
||||
index: number
|
||||
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
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||
|
||||
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}),
|
||||
[resolvedDid],
|
||||
)
|
||||
@@ -160,7 +160,7 @@ export function ProfileFollowers({name}: {name: string}) {
|
||||
seenItemsRef.current.clear()
|
||||
}, [resolvedDid])
|
||||
const onItemSeen = useCallback(
|
||||
(item: ActorDefs.ProfileView) => {
|
||||
(item: app.bsky.actor.defs.ProfileView) => {
|
||||
if (seenItemsRef.current.has(item.did)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {type AppBskyActorDefs as ActorDefs} from '@atproto/api'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -15,6 +14,7 @@ import {PeopleRemove2_Stroke1_Corner0_Rounded as PeopleRemoveIcon} from '#/compo
|
||||
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_WEB} from '#/env'
|
||||
import {type app} from '#/lexicons'
|
||||
import {List} from '../util/List'
|
||||
import {ProfileCardWithFollowBtn} from './ProfileCard'
|
||||
|
||||
@@ -23,7 +23,7 @@ function renderItem({
|
||||
index,
|
||||
contextProfileDid,
|
||||
}: {
|
||||
item: ActorDefs.ProfileView
|
||||
item: app.bsky.actor.defs.ProfileView
|
||||
index: number
|
||||
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
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ export function ProfileFollows({name}: {name: string}) {
|
||||
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
|
||||
|
||||
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}),
|
||||
[resolvedDid],
|
||||
)
|
||||
@@ -165,7 +165,7 @@ export function ProfileFollows({name}: {name: string}) {
|
||||
seenItemsRef.current.clear()
|
||||
}, [resolvedDid])
|
||||
const onItemSeen = useCallback(
|
||||
(item: ActorDefs.ProfileView) => {
|
||||
(item: app.bsky.actor.defs.ProfileView) => {
|
||||
if (seenItemsRef.current.has(item.did)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {memo, useCallback, useMemo} from 'react'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -66,12 +65,13 @@ import {GoLiveDialog} from '#/features/liveNow/components/GoLiveDialog'
|
||||
import {GoLiveDisabledDialog} from '#/features/liveNow/components/GoLiveDisabledDialog'
|
||||
import {Dot} from '#/features/nuxs/components/Dot'
|
||||
import {Gradient} from '#/features/nuxs/components/Gradient'
|
||||
import {type app} from '#/lexicons'
|
||||
import {useDevMode} from '#/storage/hooks/dev-mode'
|
||||
|
||||
let ProfileMenu = ({
|
||||
profile,
|
||||
}: {
|
||||
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed>
|
||||
profile: Shadow<app.bsky.actor.defs.ProfileViewDetailed>
|
||||
}): React.ReactNode => {
|
||||
const t = useTheme()
|
||||
const ax = useAnalytics()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {useCallback} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import Animated, {useAnimatedRef} from 'react-native-reanimated'
|
||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -20,6 +19,7 @@ import {UserAvatar, type UserAvatarType} from '#/view/com/util/UserAvatar'
|
||||
import {StarterPackMultiPathLarge as StarterPackIcon} from '#/components/icons/StarterPack'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {useLightboxControls} from '#/components/Lightbox/state'
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
export function ProfileSubpageHeader({
|
||||
isLoading,
|
||||
@@ -37,7 +37,7 @@ export function ProfileSubpageHeader({
|
||||
title: string | undefined
|
||||
avatar: string | undefined
|
||||
isOwner: boolean | undefined
|
||||
purpose: AppBskyGraphDefs.ListPurpose | undefined
|
||||
purpose: app.bsky.graph.defs.ListPurpose | undefined
|
||||
creator:
|
||||
| {
|
||||
did: string
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {memo, useCallback} from 'react'
|
||||
import {type StyleProp, View, type ViewStyle} from 'react-native'
|
||||
import {type AppBskyActorDefs} from '@atproto/api'
|
||||
import {type ModerationDecision} from '@bsky.app/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
@@ -21,11 +20,12 @@ import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {IS_ANDROID} from '#/env'
|
||||
import {useActorStatus} from '#/features/liveNow'
|
||||
import {type app} from '#/lexicons'
|
||||
import {TimeElapsed} from './TimeElapsed'
|
||||
import {PreviewableUserAvatar} from './UserAvatar'
|
||||
|
||||
interface PostMetaOpts {
|
||||
author: AppBskyActorDefs.ProfileViewBasic
|
||||
author: app.bsky.actor.defs.ProfileViewBasic
|
||||
moderation: ModerationDecision | undefined
|
||||
postHref: string
|
||||
timestamp: string
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {type StyleProp, type TextStyle} from 'react-native'
|
||||
import {type AppBskyActorGetProfile} from '@atproto/api'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
@@ -9,6 +8,7 @@ import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {type app} from '#/lexicons'
|
||||
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
||||
|
||||
export function UserInfoText({
|
||||
@@ -19,7 +19,7 @@ export function UserInfoText({
|
||||
style,
|
||||
}: {
|
||||
did: string
|
||||
attr?: keyof AppBskyActorGetProfile.OutputSchema
|
||||
attr?: keyof app.bsky.actor.getProfile.$OutputBody
|
||||
loading?: string
|
||||
failed?: string
|
||||
prefix?: string
|
||||
|
||||
Reference in New Issue
Block a user