flip the legacy view type imports to the generated lexicons
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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,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'
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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,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'
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -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,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,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 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,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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user