flip the state layer type imports to the generated lexicons

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-08-04 04:20:35 +03:00
parent 541a82c2dd
commit dd957dd123
83 changed files with 819 additions and 900 deletions
+17 -17
View File
@@ -1,12 +1,9 @@
import {useEffect, useMemo, useState} from 'react' import {useEffect, useMemo, useState} from 'react'
import {
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
} from '@atproto/api'
import {type QueryClient} from '@tanstack/react-query' import {type QueryClient} from '@tanstack/react-query'
import {EventEmitter} from 'eventemitter3' import {EventEmitter} from 'eventemitter3'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {batchedUpdates} from '#/lib/batchedUpdates' import {batchedUpdates} from '#/lib/batchedUpdates'
import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery' import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews' import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews'
@@ -22,7 +19,10 @@ export interface PostShadow {
likeUri: string | undefined likeUri: string | undefined
repostUri: string | undefined repostUri: string | undefined
isDeleted: boolean isDeleted: boolean
embed: AppBskyEmbedRecord.View | AppBskyEmbedRecordWithMedia.View | undefined embed:
| app.bsky.embed.record.View
| app.bsky.embed.recordWithMedia.View
| undefined
pinned: boolean pinned: boolean
optimisticReplyCount: number | undefined optimisticReplyCount: number | undefined
bookmarked: boolean | undefined bookmarked: boolean | undefined
@@ -32,7 +32,7 @@ export const POST_TOMBSTONE = Symbol('PostTombstone')
const emitter = new EventEmitter() const emitter = new EventEmitter()
const shadows: WeakMap< const shadows: WeakMap<
AppBskyFeedDefs.PostView, app.bsky.feed.defs.PostView,
Partial<PostShadow> Partial<PostShadow>
> = new WeakMap() > = new WeakMap()
@@ -40,13 +40,13 @@ const shadows: WeakMap<
* Use with caution! This function returns the raw shadow data for a post. * Use with caution! This function returns the raw shadow data for a post.
* Prefer using `usePostShadow`. * Prefer using `usePostShadow`.
*/ */
export function dangerousGetPostShadow(post: AppBskyFeedDefs.PostView) { export function dangerousGetPostShadow(post: app.bsky.feed.defs.PostView) {
return shadows.get(post) return shadows.get(post)
} }
export function usePostShadow( export function usePostShadow(
post: AppBskyFeedDefs.PostView, post: app.bsky.feed.defs.PostView,
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE { ): Shadow<app.bsky.feed.defs.PostView> | typeof POST_TOMBSTONE {
const [shadow, setShadow] = useState(() => shadows.get(post)) const [shadow, setShadow] = useState(() => shadows.get(post))
const [prevPost, setPrevPost] = useState(post) const [prevPost, setPrevPost] = useState(post)
if (post !== prevPost) { if (post !== prevPost) {
@@ -74,9 +74,9 @@ export function usePostShadow(
} }
function mergeShadow( function mergeShadow(
post: AppBskyFeedDefs.PostView, post: app.bsky.feed.defs.PostView,
shadow: Partial<PostShadow>, shadow: Partial<PostShadow>,
): Shadow<AppBskyFeedDefs.PostView> | typeof POST_TOMBSTONE { ): Shadow<app.bsky.feed.defs.PostView> | typeof POST_TOMBSTONE {
if (shadow.isDeleted) { if (shadow.isDeleted) {
return POST_TOMBSTONE return POST_TOMBSTONE
} }
@@ -125,10 +125,10 @@ function mergeShadow(
let embed: typeof post.embed let embed: typeof post.embed
if ('embed' in shadow) { if ('embed' in shadow) {
if ( if (
(AppBskyEmbedRecord.isView(post.embed) && (bsky.isType(app.bsky.embed.record.view, post.embed) &&
AppBskyEmbedRecord.isView(shadow.embed)) || bsky.isType(app.bsky.embed.record.view, shadow.embed)) ||
(AppBskyEmbedRecordWithMedia.isView(post.embed) && (bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed) &&
AppBskyEmbedRecordWithMedia.isView(shadow.embed)) bsky.isType(app.bsky.embed.recordWithMedia.view, shadow.embed))
) { ) {
embed = shadow.embed embed = shadow.embed
} }
@@ -169,7 +169,7 @@ export function updatePostShadow(
function* findPostsInCache( function* findPostsInCache(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, void> { ): Generator<app.bsky.feed.defs.PostView, void> {
for (let post of findAllPostsInFeedQueryData(queryClient, uri)) { for (let post of findAllPostsInFeedQueryData(queryClient, uri)) {
yield post yield post
} }
+6 -4
View File
@@ -1,8 +1,8 @@
import {useEffect, useMemo, useState} from 'react' import {useEffect, useMemo, useState} from 'react'
import {type AppBskyActorDefs, type AppBskyNotificationDefs} from '@atproto/api'
import {type QueryClient} from '@tanstack/react-query' import {type QueryClient} from '@tanstack/react-query'
import {EventEmitter} from 'eventemitter3' import {EventEmitter} from 'eventemitter3'
import {app} from '#/lexicons'
import {batchedUpdates} from '#/lib/batchedUpdates' import {batchedUpdates} from '#/lib/batchedUpdates'
import {findAllProfilesInQueryData as findAllProfilesInActivitySubscriptionsQueryData} from '#/state/queries/activity-subscriptions' import {findAllProfilesInQueryData as findAllProfilesInActivitySubscriptionsQueryData} from '#/state/queries/activity-subscriptions'
import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '#/state/queries/actor-search' import {findAllProfilesInQueryData as findAllProfilesInActorSearchQueryData} from '#/state/queries/actor-search'
@@ -43,9 +43,11 @@ export interface ProfileShadow {
muted: boolean | undefined muted: boolean | undefined
mutedOnlyReposts: boolean | undefined mutedOnlyReposts: boolean | undefined
blockingUri: string | undefined blockingUri: string | undefined
verification: AppBskyActorDefs.VerificationState verification: app.bsky.actor.defs.VerificationState
status: AppBskyActorDefs.StatusView | undefined status: app.bsky.actor.defs.StatusView | undefined
activitySubscription: AppBskyNotificationDefs.ActivitySubscription | undefined activitySubscription:
| app.bsky.notification.defs.ActivitySubscription
| undefined
} }
const shadows: WeakMap< const shadows: WeakMap<
+8 -9
View File
@@ -7,7 +7,6 @@ import {
useRef, useRef,
} from 'react' } from 'react'
import {AppState, type AppStateStatus} from 'react-native' import {AppState, type AppStateStatus} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api'
import {type AtUriString, type DidString} from '@atproto/syntax' import {type AtUriString, type DidString} from '@atproto/syntax'
import throttle from 'lodash.throttle' import throttle from 'lodash.throttle'
@@ -29,7 +28,7 @@ import {useAppviewClient} from './session'
export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS] export const FEEDBACK_FEEDS = [...PROD_FEEDS, ...STAGING_FEEDS]
export const THIRD_PARTY_ALLOWED_INTERACTIONS = new Set< export const THIRD_PARTY_ALLOWED_INTERACTIONS = new Set<
AppBskyFeedDefs.Interaction['event'] app.bsky.feed.defs.Interaction['event']
>([ >([
// These are explicit actions and are therefore fine to send. // These are explicit actions and are therefore fine to send.
'app.bsky.feed.defs#requestLess', 'app.bsky.feed.defs#requestLess',
@@ -47,7 +46,7 @@ export const THIRD_PARTY_ALLOWED_INTERACTIONS = new Set<
export type StateContext = { export type StateContext = {
enabled: boolean enabled: boolean
onItemSeen: (item: any) => void onItemSeen: (item: any) => void
sendInteraction: (interaction: AppBskyFeedDefs.Interaction) => void sendInteraction: (interaction: app.bsky.feed.defs.Interaction) => void
feedDescriptor: FeedDescriptor | undefined feedDescriptor: FeedDescriptor | undefined
feedSourceInfo: FeedSourceInfo | undefined feedSourceInfo: FeedSourceInfo | undefined
} }
@@ -55,7 +54,7 @@ export type StateContext = {
const stateContext = createContext<StateContext>({ const stateContext = createContext<StateContext>({
enabled: false, enabled: false,
onItemSeen: (_item: any) => {}, onItemSeen: (_item: any) => {},
sendInteraction: (_interaction: AppBskyFeedDefs.Interaction) => {}, sendInteraction: (_interaction: app.bsky.feed.defs.Interaction) => {},
feedDescriptor: undefined, feedDescriptor: undefined,
feedSourceInfo: undefined, feedSourceInfo: undefined,
}) })
@@ -84,7 +83,7 @@ export function useFeedFeedback(
const history = useRef< const history = useRef<
// Use a WeakSet so that we don't need to clear it. // Use a WeakSet so that we don't need to clear it.
// This assumes that referential identity of slice items maps 1:1 to feed (re)fetches. // This assumes that referential identity of slice items maps 1:1 to feed (re)fetches.
WeakSet<FeedPostSliceItem | AppBskyFeedDefs.Interaction> WeakSet<FeedPostSliceItem | app.bsky.feed.defs.Interaction>
>(new WeakSet()) >(new WeakSet())
const flushEvents = useCallback( const flushEvents = useCallback(
@@ -225,7 +224,7 @@ export function useFeedFeedback(
) )
const sendInteraction = useCallback( const sendInteraction = useCallback(
(interaction: AppBskyFeedDefs.Interaction) => { (interaction: app.bsky.feed.defs.Interaction) => {
if (!enabled) { if (!enabled) {
return return
} }
@@ -273,7 +272,7 @@ export function isDiscoverFeed(feed?: FeedDescriptor) {
function isInteractionAllowed( function isInteractionAllowed(
enabled: boolean, enabled: boolean,
feed: FeedSourceFeedInfo | undefined, feed: FeedSourceFeedInfo | undefined,
interaction: AppBskyFeedDefs.Interaction['event'], interaction: app.bsky.feed.defs.Interaction['event'],
) { ) {
if (!enabled || !feed) { if (!enabled || !feed) {
return false return false
@@ -282,7 +281,7 @@ function isInteractionAllowed(
return isDiscover ? true : THIRD_PARTY_ALLOWED_INTERACTIONS.has(interaction) return isDiscover ? true : THIRD_PARTY_ALLOWED_INTERACTIONS.has(interaction)
} }
function toString(interaction: AppBskyFeedDefs.Interaction): string { function toString(interaction: app.bsky.feed.defs.Interaction): string {
return `${interaction.item}|${interaction.event}|${ return `${interaction.item}|${interaction.event}|${
interaction.feedContext || '' interaction.feedContext || ''
}|${interaction.reqId || ''}` }|${interaction.reqId || ''}`
@@ -313,7 +312,7 @@ function createAggregatedStats(): AggregatedStats {
function sendOrAggregateInteractionsForStats( function sendOrAggregateInteractionsForStats(
stats: AggregatedStats, stats: AggregatedStats,
interactions: AppBskyFeedDefs.Interaction[], interactions: app.bsky.feed.defs.Interaction[],
) { ) {
for (let interaction of interactions) { for (let interaction of interactions) {
switch (interaction.event) { switch (interaction.event) {
+64 -72
View File
@@ -1,16 +1,8 @@
import { import {type Client, XrpcResponseError, type $Typed} from '@atproto/lex'
type $Typed,
type AppBskyEmbedRecord,
type ChatBskyActorDefs,
ChatBskyConvoDefs,
type ChatBskyConvoGetLog,
type ChatBskyEmbedJoinLink,
type ChatBskyGroupDefs,
} from '@atproto/api'
import {type Client, XrpcResponseError} from '@atproto/lex'
import {EventEmitter} from 'eventemitter3' import {EventEmitter} from 'eventemitter3'
import {nanoid} from 'nanoid/non-secure' import {nanoid} from 'nanoid/non-secure'
import * as bsky from '#/types/bsky'
import {networkRetry} from '#/lib/async/retry' import {networkRetry} from '#/lib/async/retry'
import { import {
isErrorMaybeAppPasswordPermissions, isErrorMaybeAppPasswordPermissions,
@@ -49,7 +41,7 @@ import {
parseConvoView, parseConvoView,
} from '#/components/dms/util' } from '#/components/dms/util'
import {IS_NATIVE} from '#/env' import {IS_NATIVE} from '#/env'
import {chat} from '#/lexicons' import {app, chat} from '#/lexicons'
const logger = Logger.create(Logger.Context.ConversationAgent) const logger = Logger.create(Logger.Context.ConversationAgent)
@@ -65,21 +57,21 @@ export function isConvoItemMessage(
} }
function toSystemMessageView( function toSystemMessageView(
ev: ChatBskyConvoGetLog.OutputSchema['logs'][number], ev: chat.bsky.convo.getLog.$OutputBody['logs'][number],
): ChatBskyConvoDefs.SystemMessageView | null { ): chat.bsky.convo.defs.SystemMessageView | null {
const isSystem = const isSystem =
ChatBskyConvoDefs.isLogAddMember(ev) || bsky.isType(chat.bsky.convo.defs.logAddMember, ev) ||
ChatBskyConvoDefs.isLogRemoveMember(ev) || bsky.isType(chat.bsky.convo.defs.logRemoveMember, ev) ||
ChatBskyConvoDefs.isLogMemberJoin(ev) || bsky.isType(chat.bsky.convo.defs.logMemberJoin, ev) ||
ChatBskyConvoDefs.isLogMemberLeave(ev) || bsky.isType(chat.bsky.convo.defs.logMemberLeave, ev) ||
ChatBskyConvoDefs.isLogLockConvo(ev) || bsky.isType(chat.bsky.convo.defs.logLockConvo, ev) ||
ChatBskyConvoDefs.isLogUnlockConvo(ev) || bsky.isType(chat.bsky.convo.defs.logUnlockConvo, ev) ||
ChatBskyConvoDefs.isLogLockConvoPermanently(ev) || bsky.isType(chat.bsky.convo.defs.logLockConvoPermanently, ev) ||
ChatBskyConvoDefs.isLogEditGroup(ev) || bsky.isType(chat.bsky.convo.defs.logEditGroup, ev) ||
ChatBskyConvoDefs.isLogCreateJoinLink(ev) || bsky.isType(chat.bsky.convo.defs.logCreateJoinLink, ev) ||
ChatBskyConvoDefs.isLogEditJoinLink(ev) || bsky.isType(chat.bsky.convo.defs.logEditJoinLink, ev) ||
ChatBskyConvoDefs.isLogEnableJoinLink(ev) || bsky.isType(chat.bsky.convo.defs.logEnableJoinLink, ev) ||
ChatBskyConvoDefs.isLogDisableJoinLink(ev) bsky.isType(chat.bsky.convo.defs.logDisableJoinLink, ev)
if (!isSystem) return null if (!isSystem) return null
return ev.message return ev.message
} }
@@ -89,8 +81,8 @@ function toSystemMessageView(
* the fields the deleted view carries so a reply can render it as deleted. * the fields the deleted view carries so a reply can render it as deleted.
*/ */
function toDeletedMessageView( function toDeletedMessageView(
m: ChatBskyConvoDefs.MessageView, m: chat.bsky.convo.defs.MessageView,
): $Typed<ChatBskyConvoDefs.DeletedMessageView> { ): $Typed<chat.bsky.convo.defs.DeletedMessageView> {
return { return {
$type: 'chat.bsky.convo.defs#deletedMessageView', $type: 'chat.bsky.convo.defs#deletedMessageView',
id: m.id, id: m.id,
@@ -115,15 +107,15 @@ export class Convo {
private pastMessages: Map< private pastMessages: Map<
string, string,
| ChatBskyConvoDefs.MessageView | chat.bsky.convo.defs.MessageView
| ChatBskyConvoDefs.DeletedMessageView | chat.bsky.convo.defs.DeletedMessageView
| ChatBskyConvoDefs.SystemMessageView | chat.bsky.convo.defs.SystemMessageView
> = new Map() > = new Map()
private newMessages: Map< private newMessages: Map<
string, string,
| ChatBskyConvoDefs.MessageView | chat.bsky.convo.defs.MessageView
| ChatBskyConvoDefs.DeletedMessageView | chat.bsky.convo.defs.DeletedMessageView
| ChatBskyConvoDefs.SystemMessageView | chat.bsky.convo.defs.SystemMessageView
> = new Map() > = new Map()
private pendingMessages: Map< private pendingMessages: Map<
string, string,
@@ -131,13 +123,13 @@ export class Convo {
id: string id: string
message: chat.bsky.convo.defs.MessageInput message: chat.bsky.convo.defs.MessageInput
optimisticEmbedView?: optimisticEmbedView?:
| $Typed<AppBskyEmbedRecord.View> | $Typed<app.bsky.embed.record.View>
| $Typed<ChatBskyEmbedJoinLink.View> | $Typed<chat.bsky.embed.joinLink.View>
optimisticReplyTo?: $Typed<ChatBskyConvoDefs.MessageView> optimisticReplyTo?: $Typed<chat.bsky.convo.defs.MessageView>
} }
> = new Map() > = new Map()
private deletedMessages: Set<string> = new Set() private deletedMessages: Set<string> = new Set()
private relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> = private relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic> =
new Map() new Map()
/** /**
* Accumulated profile shadow state, keyed by did. The profiles this agent * Accumulated profile shadow state, keyed by did. The profiles this agent
@@ -157,8 +149,8 @@ export class Convo {
convoId: string convoId: string
convo: ConvoWithDetails | undefined convo: ConvoWithDetails | undefined
sender: ChatBskyActorDefs.ProfileViewBasic | undefined sender: chat.bsky.actor.defs.ProfileViewBasic | undefined
recipients: ChatBskyActorDefs.ProfileViewBasic[] | undefined recipients: chat.bsky.actor.defs.ProfileViewBasic[] | undefined
snapshot: ConvoState | undefined snapshot: ConvoState | undefined
constructor(params: ConvoParams) { constructor(params: ConvoParams) {
@@ -596,7 +588,7 @@ export class Convo {
} }
} }
private setConvo(convo: ChatBskyConvoDefs.ConvoView) { private setConvo(convo: chat.bsky.convo.defs.ConvoView) {
this.convo = parseConvoView(convo, this.senderUserDid) ?? this.convo this.convo = parseConvoView(convo, this.senderUserDid) ?? this.convo
if (this.convo) { if (this.convo) {
for (const member of this.convo.members) { for (const member of this.convo.members) {
@@ -606,7 +598,7 @@ export class Convo {
this.applyProfileShadows() this.applyProfileShadows()
} }
private updateConvo(convo: Partial<ChatBskyConvoDefs.ConvoView>) { private updateConvo(convo: Partial<chat.bsky.convo.defs.ConvoView>) {
if (this.convo) { if (this.convo) {
this.convo = this.convo =
parseConvoView({...this.convo.view, ...convo}, this.senderUserDid) ?? parseConvoView({...this.convo.view, ...convo}, this.senderUserDid) ??
@@ -716,7 +708,7 @@ export class Convo {
} }
private pendingFetchConvo: private pendingFetchConvo:
| Promise<{convo: ChatBskyConvoDefs.ConvoView}> | Promise<{convo: chat.bsky.convo.defs.ConvoView}>
| undefined | undefined
async fetchConvo() { async fetchConvo() {
if (this.pendingFetchConvo) return this.pendingFetchConvo if (this.pendingFetchConvo) return this.pendingFetchConvo
@@ -836,9 +828,9 @@ export class Convo {
for (const message of messages) { for (const message of messages) {
if ( if (
ChatBskyConvoDefs.isMessageView(message) || bsky.isType(chat.bsky.convo.defs.messageView, message) ||
ChatBskyConvoDefs.isDeletedMessageView(message) || bsky.isType(chat.bsky.convo.defs.deletedMessageView, message) ||
ChatBskyConvoDefs.isSystemMessageView(message) bsky.isType(chat.bsky.convo.defs.systemMessageView, message)
) { ) {
/* /*
* If this message is already in new messages, it was added by the * If this message is already in new messages, it was added by the
@@ -913,7 +905,7 @@ export class Convo {
this.commit() this.commit()
} }
ingestFirehose(events: ChatBskyConvoGetLog.OutputSchema['logs']) { ingestFirehose(events: chat.bsky.convo.getLog.$OutputBody['logs']) {
let needsCommit = false let needsCommit = false
for (const ev of events) { for (const ev of events) {
@@ -950,8 +942,8 @@ export class Convo {
} }
if ( if (
ChatBskyConvoDefs.isLogCreateMessage(ev) && bsky.isType(chat.bsky.convo.defs.logCreateMessage, ev) &&
ChatBskyConvoDefs.isMessageView(ev.message) bsky.isType(chat.bsky.convo.defs.messageView, ev.message)
) { ) {
/* /*
* If this message is already in past messages, the initial * If this message is already in past messages, the initial
@@ -976,8 +968,8 @@ export class Convo {
} }
needsCommit = true needsCommit = true
} else if ( } else if (
ChatBskyConvoDefs.isLogDeleteMessage(ev) && bsky.isType(chat.bsky.convo.defs.logDeleteMessage, ev) &&
ChatBskyConvoDefs.isDeletedMessageView(ev.message) bsky.isType(chat.bsky.convo.defs.deletedMessageView, ev.message)
) { ) {
/* /*
* Remove the message itself, and keep its id in `deletedMessages` * Remove the message itself, and keep its id in `deletedMessages`
@@ -992,9 +984,9 @@ export class Convo {
this.deletedMessages.add(ev.message.id) this.deletedMessages.add(ev.message.id)
needsCommit = true needsCommit = true
} else if ( } else if (
(ChatBskyConvoDefs.isLogAddReaction(ev) || (bsky.isType(chat.bsky.convo.defs.logAddReaction, ev) ||
ChatBskyConvoDefs.isLogRemoveReaction(ev)) && bsky.isType(chat.bsky.convo.defs.logRemoveReaction, ev)) &&
ChatBskyConvoDefs.isMessageView(ev.message) bsky.isType(chat.bsky.convo.defs.messageView, ev.message)
) { ) {
/* /*
* Update if we have this in state - replace message wholesale. If we don't, don't worry about it. * Update if we have this in state - replace message wholesale. If we don't, don't worry about it.
@@ -1033,9 +1025,9 @@ export class Convo {
sendMessage( sendMessage(
message: chat.bsky.convo.defs.MessageInput, message: chat.bsky.convo.defs.MessageInput,
optimisticEmbedView?: optimisticEmbedView?:
| $Typed<AppBskyEmbedRecord.View> | $Typed<app.bsky.embed.record.View>
| $Typed<ChatBskyEmbedJoinLink.View>, | $Typed<chat.bsky.embed.joinLink.View>,
optimisticReplyTo?: $Typed<ChatBskyConvoDefs.MessageView>, optimisticReplyTo?: $Typed<chat.bsky.convo.defs.MessageView>,
) { ) {
// Ignore empty messages for now since they have no other purpose atm // Ignore empty messages for now since they have no other purpose atm
if (!message.text.trim() && !message.embed) return if (!message.text.trim() && !message.embed) return
@@ -1110,7 +1102,7 @@ export class Convo {
this.commit() this.commit()
} }
updateJoinLink(joinLink: ChatBskyGroupDefs.JoinLinkView | undefined) { updateJoinLink(joinLink: chat.bsky.group.defs.JoinLinkView | undefined) {
if (this.convo?.kind !== 'group') { if (this.convo?.kind !== 'group') {
throw new Error('updateJoinLink can only be called on group convo') throw new Error('updateJoinLink can only be called on group convo')
} }
@@ -1126,7 +1118,7 @@ export class Convo {
} }
updateLockStatus( updateLockStatus(
lockStatus: ChatBskyConvoDefs.ConvoLockStatus, lockStatus: chat.bsky.convo.defs.ConvoLockStatus,
lockStatusModerationOverride: boolean, lockStatusModerationOverride: boolean,
) { ) {
if (this.convo?.kind !== 'group') { if (this.convo?.kind !== 'group') {
@@ -1340,11 +1332,11 @@ export class Convo {
* matching what the server returns on refresh. * matching what the server returns on refresh.
*/ */
private tombstoneDeletedReplyTo( private tombstoneDeletedReplyTo(
m: ChatBskyConvoDefs.MessageView, m: chat.bsky.convo.defs.MessageView,
): ChatBskyConvoDefs.MessageView { ): chat.bsky.convo.defs.MessageView {
const {replyTo} = m const {replyTo} = m
if ( if (
!ChatBskyConvoDefs.isMessageView(replyTo) || !bsky.isType(chat.bsky.convo.defs.messageView, replyTo) ||
!this.deletedMessages.has(replyTo.id) !this.deletedMessages.has(replyTo.id)
) { ) {
return m return m
@@ -1359,19 +1351,19 @@ export class Convo {
const items: ConvoItem[] = [] const items: ConvoItem[] = []
this.pastMessages.forEach(m => { this.pastMessages.forEach(m => {
if (ChatBskyConvoDefs.isMessageView(m)) { if (bsky.isType(chat.bsky.convo.defs.messageView, m)) {
items.unshift({ items.unshift({
type: 'message', type: 'message',
key: m.id, key: m.id,
message: this.tombstoneDeletedReplyTo(m), message: this.tombstoneDeletedReplyTo(m),
}) })
} else if (ChatBskyConvoDefs.isDeletedMessageView(m)) { } else if (bsky.isType(chat.bsky.convo.defs.deletedMessageView, m)) {
items.unshift({ items.unshift({
type: 'deleted-message', type: 'deleted-message',
key: m.id, key: m.id,
message: m, message: m,
}) })
} else if (ChatBskyConvoDefs.isSystemMessageView(m)) { } else if (bsky.isType(chat.bsky.convo.defs.systemMessageView, m)) {
items.unshift({ items.unshift({
type: 'system-message', type: 'system-message',
key: m.id, key: m.id,
@@ -1392,19 +1384,19 @@ export class Convo {
} }
this.newMessages.forEach(m => { this.newMessages.forEach(m => {
if (ChatBskyConvoDefs.isMessageView(m)) { if (bsky.isType(chat.bsky.convo.defs.messageView, m)) {
items.push({ items.push({
type: 'message', type: 'message',
key: m.id, key: m.id,
message: this.tombstoneDeletedReplyTo(m), message: this.tombstoneDeletedReplyTo(m),
}) })
} else if (ChatBskyConvoDefs.isDeletedMessageView(m)) { } else if (bsky.isType(chat.bsky.convo.defs.deletedMessageView, m)) {
items.push({ items.push({
type: 'deleted-message', type: 'deleted-message',
key: m.id, key: m.id,
message: m, message: m,
}) })
} else if (ChatBskyConvoDefs.isSystemMessageView(m)) { } else if (bsky.isType(chat.bsky.convo.defs.systemMessageView, m)) {
items.push({ items.push({
type: 'system-message', type: 'system-message',
key: m.id, key: m.id,
@@ -1479,7 +1471,7 @@ export class Convo {
if (this.pastMessages.has(messageId)) { if (this.pastMessages.has(messageId)) {
const prevMessage = this.pastMessages.get(messageId) const prevMessage = this.pastMessages.get(messageId)
if ( if (
ChatBskyConvoDefs.isMessageView(prevMessage) && bsky.isType(chat.bsky.convo.defs.messageView, prevMessage) &&
// skip optimistic update if reaction already exists // skip optimistic update if reaction already exists
!prevMessage.reactions?.find( !prevMessage.reactions?.find(
reaction => reaction =>
@@ -1509,7 +1501,7 @@ export class Convo {
} else if (this.newMessages.has(messageId)) { } else if (this.newMessages.has(messageId)) {
const prevMessage = this.newMessages.get(messageId) const prevMessage = this.newMessages.get(messageId)
if ( if (
ChatBskyConvoDefs.isMessageView(prevMessage) && bsky.isType(chat.bsky.convo.defs.messageView, prevMessage) &&
!prevMessage.reactions?.find(reaction => reaction.value === emoji) !prevMessage.reactions?.find(reaction => reaction.value === emoji)
) { ) {
if (prevMessage.reactions && prevMessage.reactions.length >= 5) if (prevMessage.reactions && prevMessage.reactions.length >= 5)
@@ -1533,7 +1525,7 @@ export class Convo {
value: emoji, value: emoji,
convoId: this.convoId, convoId: this.convoId,
}) })
if (ChatBskyConvoDefs.isMessageView(data.message)) { if (bsky.isType(chat.bsky.convo.defs.messageView, data.message)) {
if (this.pastMessages.has(messageId)) { if (this.pastMessages.has(messageId)) {
this.pastMessages.set(messageId, data.message) this.pastMessages.set(messageId, data.message)
this.commit() this.commit()
@@ -1558,7 +1550,7 @@ export class Convo {
let restore: null | (() => void) = null let restore: null | (() => void) = null
if (this.pastMessages.has(messageId)) { if (this.pastMessages.has(messageId)) {
const prevMessage = this.pastMessages.get(messageId) const prevMessage = this.pastMessages.get(messageId)
if (ChatBskyConvoDefs.isMessageView(prevMessage)) { if (bsky.isType(chat.bsky.convo.defs.messageView, prevMessage)) {
this.pastMessages.set(messageId, { this.pastMessages.set(messageId, {
...prevMessage, ...prevMessage,
reactions: prevMessage.reactions?.filter( reactions: prevMessage.reactions?.filter(
@@ -1575,7 +1567,7 @@ export class Convo {
} }
} else if (this.newMessages.has(messageId)) { } else if (this.newMessages.has(messageId)) {
const prevMessage = this.newMessages.get(messageId) const prevMessage = this.newMessages.get(messageId)
if (ChatBskyConvoDefs.isMessageView(prevMessage)) { if (bsky.isType(chat.bsky.convo.defs.messageView, prevMessage)) {
this.newMessages.set(messageId, { this.newMessages.set(messageId, {
...prevMessage, ...prevMessage,
reactions: prevMessage.reactions?.filter( reactions: prevMessage.reactions?.filter(
+11 -9
View File
@@ -6,10 +6,11 @@ import {
useState, useState,
useSyncExternalStore, useSyncExternalStore,
} from 'react' } from 'react'
import {ChatBskyConvoDefs} from '@atproto/api'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query' import {useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {chat} from '#/lexicons'
import {useAppState} from '#/lib/appState' import {useAppState} from '#/lib/appState'
import {Convo} from '#/state/messages/convo/agent' import {Convo} from '#/state/messages/convo/agent'
import { import {
@@ -34,8 +35,8 @@ import {type GroupConvoMember} from '#/components/dms/util'
export * from '#/state/messages/convo/util' export * from '#/state/messages/convo/util'
function membersChanged( function membersChanged(
a: ChatBskyConvoDefs.ConvoView['members'], a: chat.bsky.convo.defs.ConvoView['members'],
b: ChatBskyConvoDefs.ConvoView['members'], b: chat.bsky.convo.defs.ConvoView['members'],
) { ) {
if (a.length !== b.length) return true if (a.length !== b.length) return true
const aDids = new Set(a.map(m => m.did)) const aDids = new Set(a.map(m => m.did))
@@ -83,9 +84,10 @@ export function ConvoProvider({
const chatClient = useChatClient() const chatClient = useChatClient()
const events = useMessagesEventBus() const events = useMessagesEventBus()
const [convo] = useState(() => { const [convo] = useState(() => {
const placeholder = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>( const placeholder =
getConvoKey(convoId), queryClient.getQueryData<chat.bsky.convo.defs.ConvoView>(
) getConvoKey(convoId),
)
return new Convo({ return new Convo({
convoId, convoId,
chatClient, chatClient,
@@ -151,14 +153,14 @@ export function ConvoProvider({
const queryKey = event.query.queryKey as string[] const queryKey = event.query.queryKey as string[]
if (queryKey[0] === root && queryKey[1] === id) { if (queryKey[0] === root && queryKey[1] === id) {
const data = event.query.state.data as const data = event.query.state.data as
| ChatBskyConvoDefs.ConvoView | chat.bsky.convo.defs.ConvoView
| undefined | undefined
if (data && convo.convo && data.muted !== convo.convo.view.muted) { if (data && convo.convo && data.muted !== convo.convo.view.muted) {
convo.updateMuted(data.muted) convo.updateMuted(data.muted)
} }
if ( if (
data && data &&
ChatBskyConvoDefs.isGroupConvo(data.kind) && bsky.isType(chat.bsky.convo.defs.groupConvo, data.kind) &&
convo.convo?.kind === 'group' convo.convo?.kind === 'group'
) { ) {
if (data.kind.name !== convo.convo.details.name) { if (data.kind.name !== convo.convo.details.name) {
@@ -180,7 +182,7 @@ export function ConvoProvider({
} }
if ( if (
data && data &&
ChatBskyConvoDefs.isGroupConvo(data.kind) && bsky.isType(chat.bsky.convo.defs.groupConvo, data.kind) &&
convo.convo?.kind === 'group' && convo.convo?.kind === 'group' &&
(membersChanged(data.members, convo.convo.members) || (membersChanged(data.members, convo.convo.members) ||
data.kind.memberCount !== convo.convo.details.memberCount) data.kind.memberCount !== convo.convo.details.memberCount)
+14 -21
View File
@@ -1,15 +1,8 @@
import { import {type Client, type $Typed} from '@atproto/lex'
type $Typed,
type AppBskyEmbedRecord,
type ChatBskyActorDefs,
type ChatBskyConvoDefs,
type ChatBskyEmbedJoinLink,
} from '@atproto/api'
import {type Client} from '@atproto/lex'
import {type MessagesEventBus} from '#/state/messages/events/agent' import {type MessagesEventBus} from '#/state/messages/events/agent'
import {type ConvoWithDetails} from '#/components/dms/util' import {type ConvoWithDetails} from '#/components/dms/util'
import {type chat} from '#/lexicons' import {app, chat} from '#/lexicons'
export type ConvoParams = { export type ConvoParams = {
convoId: string convoId: string
@@ -17,7 +10,7 @@ export type ConvoParams = {
chatClient: Client chatClient: Client
events: MessagesEventBus events: MessagesEventBus
placeholderData?: { placeholderData?: {
convo: ChatBskyConvoDefs.ConvoView convo: chat.bsky.convo.defs.ConvoView
} }
} }
@@ -75,12 +68,12 @@ export type ConvoItem =
| { | {
type: 'message' type: 'message'
key: string key: string
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
} }
| { | {
type: 'pending-message' type: 'pending-message'
key: string key: string
message: ChatBskyConvoDefs.MessageView message: chat.bsky.convo.defs.MessageView
failed: boolean failed: boolean
/** /**
* Retry sending the message. If present, the message is in a failed state. * Retry sending the message. If present, the message is in a failed state.
@@ -90,12 +83,12 @@ export type ConvoItem =
| { | {
type: 'deleted-message' type: 'deleted-message'
key: string key: string
message: ChatBskyConvoDefs.DeletedMessageView message: chat.bsky.convo.defs.DeletedMessageView
} }
| { | {
type: 'system-message' type: 'system-message'
key: string key: string
message: ChatBskyConvoDefs.SystemMessageView message: chat.bsky.convo.defs.SystemMessageView
} }
| { | {
type: 'error' type: 'error'
@@ -111,10 +104,10 @@ type DeleteMessage = (messageId: string) => Promise<void>
type SendMessage = ( type SendMessage = (
message: chat.bsky.convo.defs.MessageInput, message: chat.bsky.convo.defs.MessageInput,
optimisticEmbedView: optimisticEmbedView:
| $Typed<AppBskyEmbedRecord.View> | $Typed<app.bsky.embed.record.View>
| $Typed<ChatBskyEmbedJoinLink.View> | $Typed<chat.bsky.embed.joinLink.View>
| undefined, | undefined,
optimisticReplyTo?: $Typed<ChatBskyConvoDefs.MessageView>, optimisticReplyTo?: $Typed<chat.bsky.convo.defs.MessageView>,
) => void ) => void
type FetchMessageHistory = () => Promise<void> type FetchMessageHistory = () => Promise<void>
type MarkConvoAccepted = () => void type MarkConvoAccepted = () => void
@@ -153,7 +146,7 @@ export type ConvoStateReady = {
status: ConvoStatus.Ready status: ConvoStatus.Ready
items: ConvoItem[] items: ConvoItem[]
convo: ConvoWithDetails convo: ConvoWithDetails
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
error: undefined error: undefined
isFetchingHistory: boolean isFetchingHistory: boolean
hasAllHistory: boolean hasAllHistory: boolean
@@ -168,7 +161,7 @@ export type ConvoStateBackgrounded = {
status: ConvoStatus.Backgrounded status: ConvoStatus.Backgrounded
items: ConvoItem[] items: ConvoItem[]
convo: ConvoWithDetails convo: ConvoWithDetails
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
error: undefined error: undefined
isFetchingHistory: boolean isFetchingHistory: boolean
hasAllHistory: boolean hasAllHistory: boolean
@@ -183,7 +176,7 @@ export type ConvoStateSuspended = {
status: ConvoStatus.Suspended status: ConvoStatus.Suspended
items: ConvoItem[] items: ConvoItem[]
convo: ConvoWithDetails convo: ConvoWithDetails
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
error: undefined error: undefined
isFetchingHistory: boolean isFetchingHistory: boolean
hasAllHistory: boolean hasAllHistory: boolean
@@ -212,7 +205,7 @@ export type ConvoStateDisabled = {
status: ConvoStatus.Disabled status: ConvoStatus.Disabled
items: ConvoItem[] items: ConvoItem[]
convo: ConvoWithDetails convo: ConvoWithDetails
relatedProfiles: Map<string, ChatBskyActorDefs.ProfileViewBasic> relatedProfiles: Map<string, chat.bsky.actor.defs.ProfileViewBasic>
error: undefined error: undefined
isFetchingHistory: boolean isFetchingHistory: boolean
hasAllHistory: boolean hasAllHistory: boolean
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type AtIdentifierString} from '@atproto/syntax' import {type AtIdentifierString} from '@atproto/syntax'
import {t} from '@lingui/core/macro' import {t} from '@lingui/core/macro'
import { import {
@@ -106,7 +105,7 @@ export function useNotificationDeclarationMutation() {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.notification.listActivitySubscriptions.$OutputBody> InfiniteData<app.bsky.notification.listActivitySubscriptions.$OutputBody>
>({ >({
+4 -5
View File
@@ -1,5 +1,4 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {keepPreviousData, useQuery, useQueryClient} from '@tanstack/react-query' import {keepPreviousData, useQuery, useQueryClient} from '@tanstack/react-query'
@@ -34,7 +33,7 @@ export function useActorAutocompleteQuery(
prefix = prefix.slice(0, -1) prefix = prefix.slice(0, -1)
} }
return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({ return useQuery<app.bsky.actor.defs.ProfileViewBasic[]>({
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(prefix || ''), queryKey: RQKEY(prefix || ''),
async queryFn() { async queryFn() {
@@ -47,7 +46,7 @@ export function useActorAutocompleteQuery(
return data?.actors || [] return data?.actors || []
}, },
select: useCallback( select: useCallback(
(data: AppBskyActorDefs.ProfileViewBasic[]) => { (data: app.bsky.actor.defs.ProfileViewBasic[]) => {
return computeSuggestions({ return computeSuggestions({
q: prefix, q: prefix,
searched: data, searched: data,
@@ -104,10 +103,10 @@ function computeSuggestions({
moderationOpts, moderationOpts,
}: { }: {
q?: string q?: string
searched?: AppBskyActorDefs.ProfileViewBasic[] searched?: app.bsky.actor.defs.ProfileViewBasic[]
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
}) { }) {
let items: AppBskyActorDefs.ProfileViewBasic[] = [] let items: app.bsky.actor.defs.ProfileViewBasic[] = []
for (const item of searched) { for (const item of searched) {
if (!items.find(item2 => item2.handle === item.handle)) { if (!items.find(item2 => item2.handle === item.handle)) {
items.push(item) items.push(item)
@@ -1,4 +1,3 @@
import {type AppBskyFeedDefs} from '@atproto/api'
import {type AtUriString} from '@atproto/syntax' import {type AtUriString} from '@atproto/syntax'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
@@ -13,7 +12,7 @@ import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons' import {app} from '#/lexicons'
type MutationArgs = type MutationArgs =
| {action: 'create'; post: AppBskyFeedDefs.PostView} | {action: 'create'; post: app.bsky.feed.defs.PostView}
| { | {
action: 'delete' action: 'delete'
/** /**
@@ -1,4 +1,3 @@
import {type $Typed, AppBskyFeedDefs, AtUri} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -11,6 +10,8 @@ import {
embedViewRecordToPostView, embedViewRecordToPostView,
getEmbeddedPost, getEmbeddedPost,
} from '#/state/queries/util' } from '#/state/queries/util'
import {type $Typed} from '@atproto/lex'
import {AtUri} from '@atproto/syntax'
import {useAppviewClient} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons' import {app} from '#/lexicons'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
@@ -57,7 +58,7 @@ export async function truncateAndInvalidate(qc: QueryClient) {
export async function optimisticallySaveBookmark( export async function optimisticallySaveBookmark(
qc: QueryClient, qc: QueryClient,
post: AppBskyFeedDefs.PostView, post: app.bsky.feed.defs.PostView,
) { ) {
qc.setQueriesData<InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>>( qc.setQueriesData<InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>>(
{ {
@@ -81,7 +82,7 @@ export async function optimisticallySaveBookmark(
uri: post.uri, uri: post.uri,
cid: post.cid, cid: post.cid,
}, },
item: post as $Typed<AppBskyFeedDefs.PostView>, item: post as $Typed<app.bsky.feed.defs.PostView>,
} as unknown as app.bsky.bookmark.defs.BookmarkView } as unknown as app.bsky.bookmark.defs.BookmarkView
return { return {
...page, ...page,
@@ -121,7 +122,7 @@ export async function optimisticallyDeleteBookmark(
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, undefined> { ): Generator<app.bsky.feed.defs.PostView, undefined> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody> InfiniteData<app.bsky.bookmark.getBookmarks.$OutputBody>
>({ >({
@@ -135,13 +136,7 @@ export function* findAllPostsInQueryData(
} }
for (const page of queryData?.pages) { for (const page of queryData?.pages) {
for (const bookmark of page.bookmarks) { for (const bookmark of page.bookmarks) {
if ( if (!bsky.isType(app.bsky.feed.defs.postView, bookmark.item)) continue
!bsky.dangerousIsType<AppBskyFeedDefs.PostView>(
bookmark.item,
AppBskyFeedDefs.isPostView,
)
)
continue
if (didOrHandleUriMatches(atUri, bookmark.item)) { if (didOrHandleUriMatches(atUri, bookmark.item)) {
yield bookmark.item yield bookmark.item
+18 -17
View File
@@ -1,6 +1,5 @@
import {useMemo, useRef} from 'react' import {useMemo, useRef} from 'react'
import {type AppBskyActorDefs, AppBskyFeedDefs, AtUri} from '@atproto/api' import {type AtUriString, AtUri} from '@atproto/syntax'
import {type AtUriString} from '@atproto/syntax'
import {msg} from '@lingui/core/macro' import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import { import {
@@ -9,6 +8,8 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {CustomFeedAPI} from '#/lib/api/feed/custom' import {CustomFeedAPI} from '#/lib/api/feed/custom'
import {aggregateUserInterests} from '#/lib/api/feed/utils' import {aggregateUserInterests} from '#/lib/api/feed/utils'
import {FeedTuner} from '#/lib/api/feed-manip' import {FeedTuner} from '#/lib/api/feed-manip'
@@ -82,7 +83,7 @@ export type FeedPreviewItem =
| { | {
type: 'preview:header' type: 'preview:header'
key: string key: string
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
} }
| { | {
type: 'preview:footer' type: 'preview:footer'
@@ -94,7 +95,7 @@ export type FeedPreviewItem =
key: string key: string
slice: FeedPostSlice slice: FeedPostSlice
indexInSlice: number indexInSlice: number
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
showReplyTo: boolean showReplyTo: boolean
hideTopBorder: boolean hideTopBorder: boolean
} }
@@ -105,7 +106,7 @@ export type FeedPreviewItem =
} }
export function useFeedPreviews( export function useFeedPreviews(
feedsMaybeWithDuplicates: AppBskyFeedDefs.GeneratorView[], feedsMaybeWithDuplicates: app.bsky.feed.defs.GeneratorView[],
isEnabled: boolean = true, isEnabled: boolean = true,
) { ) {
const feeds = useMemo( const feeds = useMemo(
@@ -127,8 +128,8 @@ export function useFeedPreviews(
const processedPageCache = useRef( const processedPageCache = useRef(
new Map< new Map<
{ {
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
posts: AppBskyFeedDefs.FeedViewPost[] posts: app.bsky.feed.defs.FeedViewPost[]
}, },
FeedPreviewItem[] FeedPreviewItem[]
>(), >(),
@@ -346,13 +347,13 @@ export function useFeedPreviews(
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, undefined> { ): Generator<app.bsky.feed.defs.PostView, undefined> {
const atUri = new AtUri(uri) const atUri = new AtUri(uri)
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<{ InfiniteData<{
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
posts: AppBskyFeedDefs.FeedViewPost[] posts: app.bsky.feed.defs.FeedViewPost[]
}> }>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
@@ -372,7 +373,7 @@ export function* findAllPostsInQueryData(
yield embedViewRecordToPostView(quotedPost) yield embedViewRecordToPostView(quotedPost)
} }
if (AppBskyFeedDefs.isPostView(item.reply?.parent)) { if (bsky.isType(app.bsky.feed.defs.postView, item.reply?.parent)) {
if (didOrHandleUriMatches(atUri, item.reply.parent)) { if (didOrHandleUriMatches(atUri, item.reply.parent)) {
yield item.reply.parent yield item.reply.parent
} }
@@ -386,7 +387,7 @@ export function* findAllPostsInQueryData(
} }
} }
if (AppBskyFeedDefs.isPostView(item.reply?.root)) { if (bsky.isType(app.bsky.feed.defs.postView, item.reply?.root)) {
if (didOrHandleUriMatches(atUri, item.reply.root)) { if (didOrHandleUriMatches(atUri, item.reply.root)) {
yield item.reply.root yield item.reply.root
} }
@@ -404,11 +405,11 @@ export function* findAllPostsInQueryData(
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileViewBasic, undefined> { ): Generator<app.bsky.actor.defs.ProfileViewBasic, undefined> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<{ InfiniteData<{
feed: AppBskyFeedDefs.GeneratorView feed: app.bsky.feed.defs.GeneratorView
posts: AppBskyFeedDefs.FeedViewPost[] posts: app.bsky.feed.defs.FeedViewPost[]
}> }>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
@@ -427,13 +428,13 @@ export function* findAllProfilesInQueryData(
yield quotedPost.author yield quotedPost.author
} }
if ( if (
AppBskyFeedDefs.isPostView(item.reply?.parent) && bsky.isType(app.bsky.feed.defs.postView, item.reply?.parent) &&
item.reply?.parent?.author.did === did item.reply?.parent?.author.did === did
) { ) {
yield item.reply.parent.author yield item.reply.parent.author
} }
if ( if (
AppBskyFeedDefs.isPostView(item.reply?.root) && bsky.isType(app.bsky.feed.defs.postView, item.reply?.root) &&
item.reply?.root?.author.did === did item.reply?.root?.author.did === did
) { ) {
yield item.reply.root.author yield item.reply.root.author
+19 -23
View File
@@ -1,11 +1,5 @@
import {useCallback, useEffect, useMemo, useRef} from 'react' import {useCallback, useEffect, useMemo, useRef} from 'react'
import { import {type AtUriString, AtUri} from '@atproto/syntax'
type AppBskyActorDefs,
type AppBskyFeedDefs,
type AppBskyGraphDefs,
AtUri,
} from '@atproto/api'
import {type AtUriString} from '@atproto/syntax'
import {RichText} from '@bsky.app/sdk/richtext' import {RichText} from '@bsky.app/sdk/richtext'
import {t} from '@lingui/core/macro' import {t} from '@lingui/core/macro'
import { import {
@@ -36,7 +30,7 @@ import {precacheResolvedUri} from './resolve-uri'
export type FeedSourceFeedInfo = { export type FeedSourceFeedInfo = {
type: 'feed' type: 'feed'
view?: AppBskyFeedDefs.GeneratorView view?: app.bsky.feed.defs.GeneratorView
uri: string uri: string
feedDescriptor: FeedDescriptor feedDescriptor: FeedDescriptor
route: { route: {
@@ -53,12 +47,12 @@ export type FeedSourceFeedInfo = {
likeCount: number | undefined likeCount: number | undefined
acceptsInteractions?: boolean acceptsInteractions?: boolean
likeUri: string | undefined likeUri: string | undefined
contentMode: AppBskyFeedDefs.GeneratorView['contentMode'] contentMode: app.bsky.feed.defs.GeneratorView['contentMode']
} }
export type FeedSourceListInfo = { export type FeedSourceListInfo = {
type: 'list' type: 'list'
view?: AppBskyGraphDefs.ListView view?: app.bsky.graph.defs.ListView
uri: string uri: string
feedDescriptor: FeedDescriptor feedDescriptor: FeedDescriptor
route: { route: {
@@ -95,7 +89,7 @@ const feedSourceNSIDs = {
} }
export function hydrateFeedGenerator( export function hydrateFeedGenerator(
view: AppBskyFeedDefs.GeneratorView, view: app.bsky.feed.defs.GeneratorView,
): FeedSourceInfo { ): FeedSourceInfo {
const urip = new AtUri(view.uri) const urip = new AtUri(view.uri)
const collection = const collection =
@@ -137,7 +131,9 @@ export function hydrateFeedGenerator(
} }
} }
export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo { export function hydrateList(
view: app.bsky.graph.defs.ListView,
): FeedSourceInfo {
const urip = new AtUri(view.uri) const urip = new AtUri(view.uri)
const collection = const collection =
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists' urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
@@ -410,7 +406,7 @@ export function usePopularFeedsSearch({
} }
export type SavedFeedSourceInfo = FeedSourceInfo & { export type SavedFeedSourceInfo = FeedSourceInfo & {
savedFeed: AppBskyActorDefs.SavedFeed savedFeed: app.bsky.actor.defs.SavedFeed
} }
const PWI_DISCOVER_FEED_STUB: SavedFeedSourceInfo = { const PWI_DISCOVER_FEED_STUB: SavedFeedSourceInfo = {
@@ -549,17 +545,17 @@ export function usePinnedFeedsInfos() {
export type SavedFeedItem = export type SavedFeedItem =
| { | {
type: 'feed' type: 'feed'
config: AppBskyActorDefs.SavedFeed config: app.bsky.actor.defs.SavedFeed
view: AppBskyFeedDefs.GeneratorView view: app.bsky.feed.defs.GeneratorView
} }
| { | {
type: 'list' type: 'list'
config: AppBskyActorDefs.SavedFeed config: app.bsky.actor.defs.SavedFeed
view: AppBskyGraphDefs.ListView view: app.bsky.graph.defs.ListView
} }
| { | {
type: 'timeline' type: 'timeline'
config: AppBskyActorDefs.SavedFeed config: app.bsky.actor.defs.SavedFeed
view: undefined view: undefined
} }
@@ -587,8 +583,8 @@ export function useSavedFeeds() {
) )
}, },
queryFn: async () => { queryFn: async () => {
const resolvedFeeds = new Map<string, AppBskyFeedDefs.GeneratorView>() const resolvedFeeds = new Map<string, app.bsky.feed.defs.GeneratorView>()
const resolvedLists = new Map<string, AppBskyGraphDefs.ListView>() const resolvedLists = new Map<string, app.bsky.graph.defs.ListView>()
const savedFeeds = savedItems.filter(feed => feed.type === 'feed') const savedFeeds = savedItems.filter(feed => feed.type === 'feed')
const savedLists = savedItems.filter(feed => feed.type === 'list') const savedLists = savedItems.filter(feed => feed.type === 'list')
@@ -703,10 +699,10 @@ function precacheFeed(queryClient: QueryClient, hydratedFeed: FeedSourceInfo) {
export function precacheList( export function precacheList(
queryClient: QueryClient, queryClient: QueryClient,
list: AppBskyGraphDefs.ListView, list: app.bsky.graph.defs.ListView,
) { ) {
precacheResolvedUri(queryClient, list.creator.handle, list.creator.did) precacheResolvedUri(queryClient, list.creator.handle, list.creator.did)
queryClient.setQueryData<AppBskyGraphDefs.ListView>( queryClient.setQueryData<app.bsky.graph.defs.ListView>(
listQueryKey(list.uri), listQueryKey(list.uri),
list, list,
) )
@@ -714,7 +710,7 @@ export function precacheList(
export function precacheFeedFromGeneratorView( export function precacheFeedFromGeneratorView(
queryClient: QueryClient, queryClient: QueryClient,
view: AppBskyFeedDefs.GeneratorView, view: app.bsky.feed.defs.GeneratorView,
) { ) {
const hydratedFeed = hydrateFeedGenerator(view) const hydratedFeed = hydrateFeedGenerator(view)
precacheFeed(queryClient, hydratedFeed) precacheFeed(queryClient, hydratedFeed)
+13 -17
View File
@@ -1,12 +1,8 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import { import {type Client, type $Typed} from '@atproto/lex'
type $Typed,
ChatBskyGroupDefs,
type ChatBskyGroupGetJoinLinkPreviews,
} from '@atproto/api'
import {type Client} from '@atproto/lex'
import {type QueryClient, useQuery, useQueryClient} from '@tanstack/react-query' import {type QueryClient, useQuery, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {CHAT_SERVICE} from '#/lib/constants' import {CHAT_SERVICE} from '#/lib/constants'
import {createLexClient} from '#/lib/lexClient' import {createLexClient} from '#/lib/lexClient'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -21,9 +17,9 @@ import {chat} from '#/lexicons'
* `ChatInvitePreview` for that. * `ChatInvitePreview` for that.
*/ */
export type KnownChatInvitePreview = export type KnownChatInvitePreview =
| $Typed<ChatBskyGroupDefs.JoinLinkPreviewView> | $Typed<chat.bsky.group.defs.JoinLinkPreviewView>
| $Typed<ChatBskyGroupDefs.DisabledJoinLinkPreviewView> | $Typed<chat.bsky.group.defs.DisabledJoinLinkPreviewView>
| $Typed<ChatBskyGroupDefs.InvalidJoinLinkPreviewView> | $Typed<chat.bsky.group.defs.InvalidJoinLinkPreviewView>
/** /**
* The full open-union shape, including the `{$type: string}` fallback for * The full open-union shape, including the `{$type: string}` fallback for
@@ -39,9 +35,9 @@ export function isKnownJoinLinkPreview(
preview: unknown, preview: unknown,
): preview is KnownChatInvitePreview { ): preview is KnownChatInvitePreview {
return ( return (
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) || bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, preview) ||
ChatBskyGroupDefs.isDisabledJoinLinkPreviewView(preview) || bsky.isType(chat.bsky.group.defs.disabledJoinLinkPreviewView, preview) ||
ChatBskyGroupDefs.isInvalidJoinLinkPreviewView(preview) bsky.isType(chat.bsky.group.defs.invalidJoinLinkPreviewView, preview)
) )
} }
@@ -90,7 +86,7 @@ export function setJoinLinkPreviewRequestedForCode(
code: string, code: string,
requested: boolean, requested: boolean,
) { ) {
queryClient.setQueriesData<ChatBskyGroupGetJoinLinkPreviews.OutputSchema>( queryClient.setQueriesData<chat.bsky.group.getJoinLinkPreviews.$OutputBody>(
{ {
predicate: query => { predicate: query => {
const [root, args] = query.queryKey as Partial< const [root, args] = query.queryKey as Partial<
@@ -109,7 +105,7 @@ export function setJoinLinkPreviewRequestedForCode(
...old, ...old,
joinLinkPreviews: old.joinLinkPreviews.map(preview => { joinLinkPreviews: old.joinLinkPreviews.map(preview => {
if ( if (
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) && bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, preview) &&
preview.code === code preview.code === code
) { ) {
return { return {
@@ -143,12 +139,12 @@ export function invalidateJoinLinkPreviewsForConvo(
const [root] = query.queryKey const [root] = query.queryKey
if (root !== joinLinkPreviewQueryKeyRoot) return false if (root !== joinLinkPreviewQueryKeyRoot) return false
const data = query.state.data as const data = query.state.data as
| ChatBskyGroupGetJoinLinkPreviews.OutputSchema | chat.bsky.group.getJoinLinkPreviews.$OutputBody
| undefined | undefined
return ( return (
data?.joinLinkPreviews.some( data?.joinLinkPreviews.some(
preview => preview =>
ChatBskyGroupDefs.isJoinLinkPreviewView(preview) && bsky.isType(chat.bsky.group.defs.joinLinkPreviewView, preview) &&
preview.convoId === convoId, preview.convoId === convoId,
) ?? false ) ?? false
) )
@@ -204,7 +200,7 @@ export function useJoinLinkPreviewsQuery({
* Seed the query with an already-known preview (e.g. a DM message embed * Seed the query with an already-known preview (e.g. a DM message embed
* already carries the resolved view), avoiding a duplicate fetch. * already carries the resolved view), avoiding a duplicate fetch.
*/ */
initialData?: ChatBskyGroupGetJoinLinkPreviews.OutputSchema initialData?: chat.bsky.group.getJoinLinkPreviews.$OutputBody
}) { }) {
const client = useChatClient() const client = useChatClient()
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -43,7 +42,7 @@ export function useProfileKnownFollowersQuery(did: string | undefined) {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.graph.getKnownFollowers.$OutputBody> InfiniteData<app.bsky.graph.getKnownFollowers.$OutputBody>
>({ >({
+2 -3
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs, type AppBskyGraphDefs} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type AtUriString} from '@atproto/syntax' import {type AtUriString} from '@atproto/syntax'
import { import {
@@ -91,7 +90,7 @@ export async function invalidateListMembersQuery({
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.graph.getList.$OutputBody> InfiniteData<app.bsky.graph.getList.$OutputBody>
>({ >({
@@ -114,7 +113,7 @@ export function* findAllProfilesInQueryData(
} }
const allQueryData = queryClient.getQueriesData< const allQueryData = queryClient.getQueriesData<
AppBskyGraphDefs.ListItemView[] app.bsky.graph.defs.ListItemView[]
>({ >({
queryKey: [RQKEY_ROOT_ALL], queryKey: [RQKEY_ROOT_ALL],
}) })
+4 -8
View File
@@ -1,7 +1,3 @@
import {
type AppBskyActorDefs,
type AppBskyGraphGetStarterPacksWithMembership,
} from '@atproto/api'
import { import {
AtUri, AtUri,
type AtUriString, type AtUriString,
@@ -73,7 +69,7 @@ export function useListMembershipAddMutation({
// update WITH_MEMBERSHIPS query for starter packs // update WITH_MEMBERSHIPS query for starter packs
if (subject) { if (subject) {
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<AppBskyGraphGetStarterPacksWithMembership.OutputSchema> InfiniteData<app.bsky.graph.getStarterPacksWithMembership.$OutputBody>
>(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => { >(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => {
if (!old) return old if (!old) return old
@@ -94,7 +90,7 @@ export function useListMembershipAddMutation({
listItemsSample: [ listItemsSample: [
{ {
uri: data.uri, uri: data.uri,
subject: subject as AppBskyActorDefs.ProfileView, subject: subject as app.bsky.actor.defs.ProfileView,
}, },
...(spWithMembership.starterPack.listItemsSample?.filter( ...(spWithMembership.starterPack.listItemsSample?.filter(
item => item.subject.did !== variables.actorDid, item => item.subject.did !== variables.actorDid,
@@ -109,7 +105,7 @@ export function useListMembershipAddMutation({
}, },
listItem: { listItem: {
uri: data.uri, uri: data.uri,
subject: subject as AppBskyActorDefs.ProfileView, subject: subject as app.bsky.actor.defs.ProfileView,
}, },
} }
} }
@@ -167,7 +163,7 @@ export function useListMembershipRemoveMutation({
// update WITH_MEMBERSHIPS query for starter packs // update WITH_MEMBERSHIPS query for starter packs
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<AppBskyGraphGetStarterPacksWithMembership.OutputSchema> InfiniteData<app.bsky.graph.getStarterPacksWithMembership.$OutputBody>
>(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => { >(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => {
if (!old) return old if (!old) return old
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyGraphDefs} from '@atproto/api'
import {type $Typed, type Client} from '@atproto/lex' import {type $Typed, type Client} from '@atproto/lex'
import { import {
type AtIdentifierString, type AtIdentifierString,
@@ -30,7 +29,7 @@ export const RQKEY = (uri: string) => [RQKEY_ROOT, uri]
export function useListQuery(uri?: string) { export function useListQuery(uri?: string) {
const client = useAppviewClient() const client = useAppviewClient()
return useQuery<AppBskyGraphDefs.ListView, Error>({ return useQuery<app.bsky.graph.defs.ListView, Error>({
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(uri || ''), queryKey: RQKEY(uri || ''),
async queryFn() { async queryFn() {
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type AtIdentifierString} from '@atproto/syntax' import {type AtIdentifierString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -60,7 +59,7 @@ export function updateListMembershipOptimistically({
actor: string actor: string
listUri: string listUri: string
membershipUri: string membershipUri: string
subject: AppBskyActorDefs.ProfileView subject: app.bsky.actor.defs.ProfileView
}) { }) {
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody> InfiniteData<app.bsky.graph.getListsWithMembership.$OutputBody>
@@ -1,7 +1,3 @@
import {
type ChatBskyConvoAcceptConvo,
type ChatBskyConvoDefs,
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -29,7 +25,7 @@ export function useAcceptConversation(
onError, onError,
}: { }: {
onMutate?: () => void onMutate?: () => void
onSuccess?: (data: ChatBskyConvoAcceptConvo.OutputSchema) => void onSuccess?: (data: chat.bsky.convo.acceptConvo.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -47,7 +43,7 @@ export function useAcceptConversation(
queryClient.getQueriesData<ConvoListQueryData>({ queryClient.getQueriesData<ConvoListQueryData>({
queryKey: [CONVO_LIST_ROOT_KEY], queryKey: [CONVO_LIST_ROOT_KEY],
}) })
let convoBeingAccepted: ChatBskyConvoDefs.ConvoView | null = null let convoBeingAccepted: chat.bsky.convo.defs.ConvoView | null = null
for (const [_key, data] of queryClient.getQueriesData<ConvoListQueryData>( for (const [_key, data] of queryClient.getQueriesData<ConvoListQueryData>(
{queryKey: CONVO_LIST_PARTIAL_KEY('request')}, {queryKey: CONVO_LIST_PARTIAL_KEY('request')},
)) { )) {
@@ -60,7 +56,7 @@ export function useAcceptConversation(
(old?: ConvoListQueryData) => optimisticDelete(convoId, old), (old?: ConvoListQueryData) => optimisticDelete(convoId, old),
) )
if (convoBeingAccepted) { if (convoBeingAccepted) {
const acceptedConvo: ChatBskyConvoDefs.ConvoView = { const acceptedConvo: chat.bsky.convo.defs.ConvoView = {
...convoBeingAccepted, ...convoBeingAccepted,
status: 'accepted', status: 'accepted',
} }
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
@@ -6,7 +5,7 @@ import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
import {usePdsClient, useSession} from '#/state/session' import {usePdsClient, useSession} from '#/state/session'
import {resolveAllowGroupInvites} from '#/components/dms/util' import {resolveAllowGroupInvites} from '#/components/dms/util'
import {chat, com} from '#/lexicons' import {app, chat, com} from '#/lexicons'
import {RQKEY as PROFILE_RKEY} from '../profile' import {RQKEY as PROFILE_RKEY} from '../profile'
export function useUpdateActorDeclaration({ export function useUpdateActorDeclaration({
@@ -27,7 +26,7 @@ export function useUpdateActorDeclaration({
}) => { }) => {
if (!currentAccount) throw new Error('Not signed in') if (!currentAccount) throw new Error('Not signed in')
const current = const current =
queryClient.getQueryData<AppBskyActorDefs.ProfileViewDetailed>( queryClient.getQueryData<app.bsky.actor.defs.ProfileViewDetailed>(
PROFILE_RKEY(currentAccount.did), PROFILE_RKEY(currentAccount.did),
) )
const allowIncoming = const allowIncoming =
@@ -57,7 +56,7 @@ export function useUpdateActorDeclaration({
if (!currentAccount) return if (!currentAccount) return
queryClient.setQueryData( queryClient.setQueryData(
PROFILE_RKEY(currentAccount?.did), PROFILE_RKEY(currentAccount?.did),
(old?: AppBskyActorDefs.ProfileViewDetailed) => { (old?: app.bsky.actor.defs.ProfileViewDetailed) => {
if (!old) return old if (!old) return old
const allowIncoming = const allowIncoming =
update.allowIncoming ?? update.allowIncoming ??
@@ -81,7 +80,7 @@ export function useUpdateActorDeclaration({
allowGroupInvites, allowGroupInvites,
}, },
}, },
} satisfies AppBskyActorDefs.ProfileViewDetailed } satisfies app.bsky.actor.defs.ProfileViewDetailed
}, },
) )
}, },
+25 -27
View File
@@ -1,9 +1,3 @@
import {
type ChatBskyActorDefs,
ChatBskyConvoDefs,
type ChatBskyConvoListConvos,
type ChatBskyGroupAddMembers,
} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -15,7 +9,7 @@ import {logger} from '#/logger'
import {useProfileQuery} from '#/state/queries/profile' import {useProfileQuery} from '#/state/queries/profile'
import {useChatClient, useSession} from '#/state/session' import {useChatClient, useSession} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
import type * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
import {RQKEY as CONVO_KEY} from './conversation' import {RQKEY as CONVO_KEY} from './conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations' import {RQKEY_ROOT as CONVO_LIST_KEY} from './list-conversations'
import {listConvoMembersQueryKey} from './list-convo-members' import {listConvoMembersQueryKey} from './list-convo-members'
@@ -26,7 +20,7 @@ export function useAddGroupMembers(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupAddMembers.OutputSchema) => void onSuccess?: (data: chat.bsky.group.addMembers.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -52,24 +46,26 @@ export function useAddGroupMembers(
onMutate: ({profiles}) => { onMutate: ({profiles}) => {
if (!convoId) return if (!convoId) return
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>( const prevConvo =
CONVO_KEY(convoId), queryClient.getQueryData<chat.bsky.convo.defs.ConvoView>(
) CONVO_KEY(convoId),
)
const prevListEntries = queryClient.getQueriesData< const prevListEntries = queryClient.getQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}) >({queryKey: [CONVO_LIST_KEY]})
const prevMemberList = queryClient.getQueryData< const prevMemberList = queryClient.getQueryData<
ChatBskyActorDefs.ProfileViewBasic[] chat.bsky.actor.defs.ProfileViewBasic[]
>(listConvoMembersQueryKey(convoId)) >(listConvoMembersQueryKey(convoId))
const addedBy: ChatBskyActorDefs.ProfileViewBasic | undefined = myProfile const addedBy: chat.bsky.actor.defs.ProfileViewBasic | undefined =
? { myProfile
...myProfile, ? {
$type: 'chat.bsky.actor.defs#profileViewBasic', ...myProfile,
} $type: 'chat.bsky.actor.defs#profileViewBasic',
: undefined }
: undefined
const optimisticMembers: ChatBskyActorDefs.ProfileViewBasic[] = const optimisticMembers: chat.bsky.actor.defs.ProfileViewBasic[] =
profiles.map(profile => ({ profiles.map(profile => ({
...profile, ...profile,
$type: 'chat.bsky.actor.defs#profileViewBasic', $type: 'chat.bsky.actor.defs#profileViewBasic',
@@ -80,11 +76,12 @@ export function useAddGroupMembers(
}, },
})) }))
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>( queryClient.setQueryData<chat.bsky.convo.defs.ConvoView>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
prev => { prev => {
if (!prev) return if (!prev) return
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return prev if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return prev
return { return {
...prev, ...prev,
members: [...prev.members, ...optimisticMembers], members: [...prev.members, ...optimisticMembers],
@@ -97,7 +94,7 @@ export function useAddGroupMembers(
) )
queryClient.setQueriesData< queryClient.setQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}, prev => { >({queryKey: [CONVO_LIST_KEY]}, prev => {
if (!prev?.pages) return if (!prev?.pages) return
return { return {
@@ -106,7 +103,8 @@ export function useAddGroupMembers(
...page, ...page,
convos: page.convos.map(convo => { convos: page.convos.map(convo => {
if (convo.id !== convoId) return convo if (convo.id !== convoId) return convo
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) return convo if (!bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind))
return convo
return { return {
...convo, ...convo,
members: [...convo.members, ...optimisticMembers], members: [...convo.members, ...optimisticMembers],
@@ -121,7 +119,7 @@ export function useAddGroupMembers(
} }
}) })
queryClient.setQueryData<ChatBskyActorDefs.ProfileViewBasic[]>( queryClient.setQueryData<chat.bsky.actor.defs.ProfileViewBasic[]>(
listConvoMembersQueryKey(convoId), listConvoMembersQueryKey(convoId),
prev => { prev => {
if (!prev) return if (!prev) return
@@ -133,13 +131,13 @@ export function useAddGroupMembers(
}, },
onSuccess: data => { onSuccess: data => {
if (convoId) { if (convoId) {
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>( queryClient.setQueryData<chat.bsky.convo.defs.ConvoView>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
data.convo, data.convo,
) )
queryClient.setQueriesData< queryClient.setQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}, prev => { >({queryKey: [CONVO_LIST_KEY]}, prev => {
if (!prev?.pages) return if (!prev?.pages) return
return { return {
+10 -14
View File
@@ -1,9 +1,3 @@
import {
type ChatBskyActorDefs,
type ChatBskyConvoDefs,
type ChatBskyConvoGetConvo,
type ChatBskyConvoGetUnreadCounts,
} from '@atproto/api'
import { import {
type QueryClient, type QueryClient,
useMutation, useMutation,
@@ -44,7 +38,7 @@ export function useConvoQuery({convoId}: {convoId: string}) {
export function precacheConvoQuery( export function precacheConvoQuery(
queryClient: QueryClient, queryClient: QueryClient,
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
) { ) {
queryClient.setQueryData(RQKEY(convo.id), convo) queryClient.setQueryData(RQKEY(convo.id), convo)
} }
@@ -81,7 +75,7 @@ export function useMarkAsReadMutation() {
// find the convo so we know which badge counter (if any) to decrement. // find the convo so we know which badge counter (if any) to decrement.
// keep scanning past a stale unreadCount === 0 cache so another cache // keep scanning past a stale unreadCount === 0 cache so another cache
// holding the true unread state still drives the decrement // holding the true unread state still drives the decrement
let unreadStatus: ChatBskyConvoDefs.ConvoView['status'] | undefined let unreadStatus: chat.bsky.convo.defs.ConvoView['status'] | undefined
for (const [, data] of prevListQueries) { for (const [, data] of prevListQueries) {
if (!data) continue if (!data) continue
const convo = getConvoFromQueryData(convoId, data) const convo = getConvoFromQueryData(convoId, data)
@@ -96,11 +90,13 @@ export function useMarkAsReadMutation() {
// the badge count query is a separate server query that the list caches // the badge count query is a separate server query that the list caches
// don't feed, so decrement it here to keep the badge in sync // don't feed, so decrement it here to keep the badge in sync
const prevUnreadCountsQueries = const prevUnreadCountsQueries =
queryClient.getQueriesData<ChatBskyConvoGetUnreadCounts.OutputSchema>({ queryClient.getQueriesData<chat.bsky.convo.getUnreadCounts.$OutputBody>(
queryKey: UNREAD_COUNTS_PARTIAL_KEY, {
}) queryKey: UNREAD_COUNTS_PARTIAL_KEY,
},
)
if (unreadStatus) { if (unreadStatus) {
queryClient.setQueriesData<ChatBskyConvoGetUnreadCounts.OutputSchema>( queryClient.setQueriesData<chat.bsky.convo.getUnreadCounts.$OutputBody>(
{queryKey: UNREAD_COUNTS_PARTIAL_KEY}, {queryKey: UNREAD_COUNTS_PARTIAL_KEY},
old => { old => {
if (!old) return old if (!old) return old
@@ -184,9 +180,9 @@ export function useMarkAsReadMutation() {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<ChatBskyActorDefs.ProfileViewBasic, void> { ): Generator<chat.bsky.actor.defs.ProfileViewBasic, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
ChatBskyConvoGetConvo.OutputSchema['convo'] chat.bsky.convo.getConvo.$OutputBody['convo']
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
@@ -1,4 +1,3 @@
import {type ChatBskyGroupCreateGroup} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
@@ -11,7 +10,7 @@ export function useCreateGroupChat({
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupCreateGroup.OutputSchema) => void onSuccess?: (data: chat.bsky.group.createGroup.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}) { }) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
@@ -1,10 +1,6 @@
import {
ChatBskyConvoDefs,
type ChatBskyGroupCreateJoinLink,
type ChatBskyGroupDefs,
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
@@ -19,7 +15,7 @@ export function useCreateJoinLink(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupCreateJoinLink.OutputSchema) => void onSuccess?: (data: chat.bsky.group.createJoinLink.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -31,7 +27,7 @@ export function useCreateJoinLink(
joinRule, joinRule,
requireApproval, requireApproval,
}: { }: {
joinRule: ChatBskyGroupDefs.JoinRule joinRule: chat.bsky.group.defs.JoinRule
requireApproval: boolean requireApproval: boolean
}) => { }) => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
@@ -44,7 +40,8 @@ export function useCreateJoinLink(
onMutate: ({joinRule, requireApproval}) => { onMutate: ({joinRule, requireApproval}) => {
if (!convoId) return if (!convoId) return
return updateConvoOptimistic(queryClient, convoId, prev => { return updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: { kind: {
@@ -64,7 +61,8 @@ export function useCreateJoinLink(
onSuccess: data => { onSuccess: data => {
if (convoId) { if (convoId) {
updateConvoOptimistic(queryClient, convoId, prev => { updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: {...prev.kind, joinLink: data.joinLink}, kind: {...prev.kind, joinLink: data.joinLink},
@@ -1,9 +1,6 @@
import {
ChatBskyConvoDefs,
type ChatBskyGroupDisableJoinLink,
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {invalidateJoinLinkPreviewsForCode} from '#/state/queries/join-links' import {invalidateJoinLinkPreviewsForCode} from '#/state/queries/join-links'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
@@ -19,7 +16,7 @@ export function useDisableJoinLink(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupDisableJoinLink.OutputSchema) => void onSuccess?: (data: chat.bsky.group.disableJoinLink.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -34,7 +31,10 @@ export function useDisableJoinLink(
onMutate: () => { onMutate: () => {
if (!convoId) return if (!convoId) return
return updateConvoOptimistic(queryClient, convoId, prev => { return updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind) || !prev.kind.joinLink) { if (
!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind) ||
!prev.kind.joinLink
) {
return undefined return undefined
} }
return { return {
@@ -49,7 +49,8 @@ export function useDisableJoinLink(
onSuccess: data => { onSuccess: data => {
if (convoId) { if (convoId) {
updateConvoOptimistic(queryClient, convoId, prev => { updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: {...prev.kind, joinLink: data.joinLink}, kind: {...prev.kind, joinLink: data.joinLink},
@@ -1,6 +1,6 @@
import {ChatBskyConvoDefs, type ChatBskyGroupEditGroup} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
@@ -15,7 +15,7 @@ export function useEditGroupChatName(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupEditGroup.OutputSchema) => void onSuccess?: (data: chat.bsky.group.editGroup.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -33,7 +33,8 @@ export function useEditGroupChatName(
onMutate: ({name: groupName}) => { onMutate: ({name: groupName}) => {
if (!convoId) return if (!convoId) return
return updateConvoOptimistic(queryClient, convoId, prev => { return updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: {...prev.kind, name: groupName}, kind: {...prev.kind, name: groupName},
+9 -9
View File
@@ -1,10 +1,6 @@
import {
ChatBskyConvoDefs,
type ChatBskyGroupDefs,
type ChatBskyGroupEditJoinLink,
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
@@ -19,7 +15,7 @@ export function useEditJoinLink(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupEditJoinLink.OutputSchema) => void onSuccess?: (data: chat.bsky.group.editJoinLink.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -31,7 +27,7 @@ export function useEditJoinLink(
joinRule, joinRule,
requireApproval, requireApproval,
}: { }: {
joinRule: ChatBskyGroupDefs.JoinRule joinRule: chat.bsky.group.defs.JoinRule
requireApproval: boolean requireApproval: boolean
}) => { }) => {
if (!convoId) throw new Error('No convoId provided') if (!convoId) throw new Error('No convoId provided')
@@ -44,7 +40,10 @@ export function useEditJoinLink(
onMutate: ({joinRule, requireApproval}) => { onMutate: ({joinRule, requireApproval}) => {
if (!convoId) return if (!convoId) return
return updateConvoOptimistic(queryClient, convoId, prev => { return updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind) || !prev.kind.joinLink) { if (
!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind) ||
!prev.kind.joinLink
) {
return undefined return undefined
} }
return { return {
@@ -59,7 +58,8 @@ export function useEditJoinLink(
onSuccess: data => { onSuccess: data => {
if (convoId) { if (convoId) {
updateConvoOptimistic(queryClient, convoId, prev => { updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: {...prev.kind, joinLink: data.joinLink}, kind: {...prev.kind, joinLink: data.joinLink},
@@ -1,6 +1,6 @@
import {ChatBskyConvoDefs, type ChatBskyGroupEnableJoinLink} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {invalidateJoinLinkPreviewsForCode} from '#/state/queries/join-links' import {invalidateJoinLinkPreviewsForCode} from '#/state/queries/join-links'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
@@ -16,7 +16,7 @@ export function useEnableJoinLink(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupEnableJoinLink.OutputSchema) => void onSuccess?: (data: chat.bsky.group.enableJoinLink.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -31,7 +31,10 @@ export function useEnableJoinLink(
onMutate: () => { onMutate: () => {
if (!convoId) return if (!convoId) return
return updateConvoOptimistic(queryClient, convoId, prev => { return updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind) || !prev.kind.joinLink) { if (
!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind) ||
!prev.kind.joinLink
) {
return undefined return undefined
} }
return { return {
@@ -46,7 +49,8 @@ export function useEnableJoinLink(
onSuccess: data => { onSuccess: data => {
if (convoId) { if (convoId) {
updateConvoOptimistic(queryClient, convoId, prev => { updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: {...prev.kind, joinLink: data.joinLink}, kind: {...prev.kind, joinLink: data.joinLink},
@@ -1,4 +1,3 @@
import {type ChatBskyConvoGetConvoForMembers} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
@@ -11,7 +10,7 @@ export function useGetConvoForMembers({
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyConvoGetConvoForMembers.OutputSchema) => void onSuccess?: (data: chat.bsky.convo.getConvoForMembers.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}) { }) {
const queryClient = useQueryClient() const queryClient = useQueryClient()
+7 -13
View File
@@ -1,9 +1,3 @@
import {
type ChatBskyActorDefs,
type ChatBskyGroupApproveJoinRequest,
type ChatBskyGroupListJoinRequests,
type ChatBskyGroupRejectJoinRequest,
} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -20,8 +14,8 @@ import {createListJoinRequestsQueryKey} from './list-join-requests'
type JoinRequestAction = 'approve' | 'reject' type JoinRequestAction = 'approve' | 'reject'
type JoinRequestOutput<A extends JoinRequestAction> = A extends 'approve' type JoinRequestOutput<A extends JoinRequestAction> = A extends 'approve'
? ChatBskyGroupApproveJoinRequest.OutputSchema ? chat.bsky.group.approveJoinRequest.$OutputBody
: ChatBskyGroupRejectJoinRequest.OutputSchema : chat.bsky.group.rejectJoinRequest.$OutputBody
export function useJoinRequestMutation<A extends JoinRequestAction>( export function useJoinRequestMutation<A extends JoinRequestAction>(
action: A, action: A,
@@ -65,7 +59,7 @@ export function useJoinRequestMutation<A extends JoinRequestAction>(
const requestsKey = createListJoinRequestsQueryKey({convoId}) const requestsKey = createListJoinRequestsQueryKey({convoId})
const prevRequests = const prevRequests =
queryClient.getQueryData< queryClient.getQueryData<
InfiniteData<ChatBskyGroupListJoinRequests.OutputSchema> InfiniteData<chat.bsky.group.listJoinRequests.$OutputBody>
>(requestsKey) >(requestsKey)
const requestedByProfile = prevRequests?.pages const requestedByProfile = prevRequests?.pages
@@ -73,7 +67,7 @@ export function useJoinRequestMutation<A extends JoinRequestAction>(
.find(request => request.requestedBy.did === member)?.requestedBy .find(request => request.requestedBy.did === member)?.requestedBy
queryClient.setQueryData< queryClient.setQueryData<
InfiniteData<ChatBskyGroupListJoinRequests.OutputSchema> InfiniteData<chat.bsky.group.listJoinRequests.$OutputBody>
>(requestsKey, prev => { >(requestsKey, prev => {
if (!prev?.pages) return prev if (!prev?.pages) return prev
return { return {
@@ -87,14 +81,14 @@ export function useJoinRequestMutation<A extends JoinRequestAction>(
} }
}) })
let prevMembers: ChatBskyActorDefs.ProfileViewBasic[] | undefined let prevMembers: chat.bsky.actor.defs.ProfileViewBasic[] | undefined
if (action === 'approve' && requestedByProfile) { if (action === 'approve' && requestedByProfile) {
const membersKey = listConvoMembersQueryKey(convoId) const membersKey = listConvoMembersQueryKey(convoId)
prevMembers = prevMembers =
queryClient.getQueryData<ChatBskyActorDefs.ProfileViewBasic[]>( queryClient.getQueryData<chat.bsky.actor.defs.ProfileViewBasic[]>(
membersKey, membersKey,
) )
queryClient.setQueryData<ChatBskyActorDefs.ProfileViewBasic[]>( queryClient.setQueryData<chat.bsky.actor.defs.ProfileViewBasic[]>(
membersKey, membersKey,
prev => { prev => {
if (!prev) return prev if (!prev) return prev
@@ -1,7 +1,3 @@
import {
type ChatBskyConvoLeaveConvo,
type ChatBskyConvoListConvos,
} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -22,7 +18,7 @@ export function RQKEY(convoId: string | undefined) {
type ConvoListQueryData = { type ConvoListQueryData = {
pageParams: Array<string | undefined> pageParams: Array<string | undefined>
pages: Array<ChatBskyConvoListConvos.OutputSchema> pages: Array<chat.bsky.convo.listConvos.$OutputBody>
} }
export function useLeaveConvo( export function useLeaveConvo(
@@ -33,7 +29,7 @@ export function useLeaveConvo(
onError, onError,
}: { }: {
onMutate?: () => void onMutate?: () => void
onSuccess?: (data: ChatBskyConvoLeaveConvo.OutputSchema) => void onSuccess?: (data: chat.bsky.convo.leaveConvo.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -1,14 +1,10 @@
import {
ChatBskyConvoDefs,
type ChatBskyConvoListConvoRequests,
ChatBskyGroupDefs,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
@@ -44,16 +40,18 @@ export function useListConvoRequests({
export type ConvoRequestListQueryData = { export type ConvoRequestListQueryData = {
pageParams: Array<string | undefined> pageParams: Array<string | undefined>
pages: Array<ChatBskyConvoListConvoRequests.OutputSchema> pages: Array<chat.bsky.convo.listConvoRequests.$OutputBody>
} }
export type ConvoRequestItem = export type ConvoRequestItem =
ChatBskyConvoListConvoRequests.OutputSchema['requests'][number] chat.bsky.convo.listConvoRequests.$OutputBody['requests'][number]
export function optimisticUpdate( export function optimisticUpdate(
chatId: string, chatId: string,
old: ConvoRequestListQueryData | undefined, old: ConvoRequestListQueryData | undefined,
updateFn: (convo: ChatBskyConvoDefs.ConvoView) => ChatBskyConvoDefs.ConvoView, updateFn: (
convo: chat.bsky.convo.defs.ConvoView,
) => chat.bsky.convo.defs.ConvoView,
): ConvoRequestListQueryData | undefined { ): ConvoRequestListQueryData | undefined {
if (!old) return old if (!old) return old
@@ -62,7 +60,10 @@ export function optimisticUpdate(
pages: old.pages.map(page => ({ pages: old.pages.map(page => ({
...page, ...page,
requests: page.requests.map((item): ConvoRequestItem => { requests: page.requests.map((item): ConvoRequestItem => {
if (ChatBskyConvoDefs.isConvoView(item) && item.id === chatId) { if (
bsky.isType(chat.bsky.convo.defs.convoView, item) &&
item.id === chatId
) {
return { return {
...updateFn(item), ...updateFn(item),
$type: 'chat.bsky.convo.defs#convoView', $type: 'chat.bsky.convo.defs#convoView',
@@ -85,7 +86,9 @@ export function optimisticDelete(
pages: old.pages.map(page => ({ pages: old.pages.map(page => ({
...page, ...page,
requests: page.requests.filter( requests: page.requests.filter(
item => !ChatBskyConvoDefs.isConvoView(item) || item.id !== chatId, item =>
!bsky.isType(chat.bsky.convo.defs.convoView, item) ||
item.id !== chatId,
), ),
})), })),
} }
@@ -101,7 +104,7 @@ export function markAllRead(
pages: old.pages.map(page => ({ pages: old.pages.map(page => ({
...page, ...page,
requests: page.requests.map((item): ConvoRequestItem => { requests: page.requests.map((item): ConvoRequestItem => {
if (ChatBskyConvoDefs.isConvoView(item)) { if (bsky.isType(chat.bsky.convo.defs.convoView, item)) {
return { return {
...item, ...item,
$type: 'chat.bsky.convo.defs#convoView', $type: 'chat.bsky.convo.defs#convoView',
@@ -126,7 +129,7 @@ export function optimisticDeleteJoinRequest(
...page, ...page,
requests: page.requests.filter( requests: page.requests.filter(
item => item =>
!ChatBskyGroupDefs.isJoinRequestConvoView(item) || !bsky.isType(chat.bsky.group.defs.joinRequestConvoView, item) ||
item.convoId !== convoId, item.convoId !== convoId,
), ),
})), })),
@@ -138,7 +141,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
) { ) {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<ChatBskyConvoListConvoRequests.OutputSchema> InfiniteData<chat.bsky.convo.listConvoRequests.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
@@ -147,13 +150,15 @@ export function* findAllProfilesInQueryData(
for (const page of queryData.pages) { for (const page of queryData.pages) {
for (const item of page.requests) { for (const item of page.requests) {
if (ChatBskyConvoDefs.isConvoView(item)) { if (bsky.isType(chat.bsky.convo.defs.convoView, item)) {
for (const member of item.members) { for (const member of item.members) {
if (member.did === did) { if (member.did === did) {
yield member yield member
} }
} }
} else if (ChatBskyGroupDefs.isJoinRequestConvoView(item)) { } else if (
bsky.isType(chat.bsky.group.defs.joinRequestConvoView, item)
) {
if (item.owner.did === did) { if (item.owner.did === did) {
yield item.owner yield item.owner
} }
+129 -94
View File
@@ -1,9 +1,4 @@
import {useCallback, useEffect, useMemo} from 'react' import {useCallback, useEffect, useMemo} from 'react'
import {
type ChatBskyActorDefs,
ChatBskyConvoDefs,
type ChatBskyConvoListConvos,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type Query, type Query,
@@ -68,7 +63,7 @@ export const RQKEY_PARTIAL = (
* filters client-side or convos leak into lists that should exclude them. * filters client-side or convos leak into lists that should exclude them.
*/ */
export function convoMatchesQueryKey( export function convoMatchesQueryKey(
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
queryKey: QueryKey, queryKey: QueryKey,
): boolean { ): boolean {
const [, status, readState, kind, lockStatus] = queryKey as ReturnType< const [, status, readState, kind, lockStatus] = queryKey as ReturnType<
@@ -76,7 +71,7 @@ export function convoMatchesQueryKey(
> >
if (status !== 'all' && status !== convo.status) return false if (status !== 'all' && status !== convo.status) return false
if (readState === 'unread' && convo.unreadCount === 0) return false if (readState === 'unread' && convo.unreadCount === 0) return false
if (ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
if (kind === 'direct') return false if (kind === 'direct') return false
if (lockStatus && convo.kind.lockStatus !== lockStatus) return false if (lockStatus && convo.kind.lockStatus !== lockStatus) return false
} else { } else {
@@ -94,7 +89,7 @@ export function convoMatchesQueryKey(
* longer matches (e.g. unreadCount dropped to 0), mirroring how read/mute * longer matches (e.g. unreadCount dropped to 0), mirroring how read/mute
* log events update convos in place everywhere. * log events update convos in place everywhere.
*/ */
export function convoListQueryPredicate(convo: ChatBskyConvoDefs.ConvoView) { export function convoListQueryPredicate(convo: chat.bsky.convo.defs.ConvoView) {
return (query: Query): boolean => { return (query: Query): boolean => {
const data = query.state.data as ConvoListQueryData | undefined const data = query.state.data as ConvoListQueryData | undefined
if (data && getConvoFromQueryData(convo.id, data)) return true if (data && getConvoFromQueryData(convo.id, data)) return true
@@ -196,10 +191,10 @@ export function ListConvosProviderInner({
function mutateMembers( function mutateMembers(
convoId: string, convoId: string,
fn: ( fn: (
members: ChatBskyActorDefs.ProfileViewBasic[], members: chat.bsky.actor.defs.ProfileViewBasic[],
) => ChatBskyActorDefs.ProfileViewBasic[], ) => chat.bsky.actor.defs.ProfileViewBasic[],
) { ) {
queryClient.setQueryData<ChatBskyActorDefs.ProfileViewBasic[]>( queryClient.setQueryData<chat.bsky.actor.defs.ProfileViewBasic[]>(
listConvoMembersQueryKey(convoId), listConvoMembersQueryKey(convoId),
old => { old => {
if (!old) return // query doesn't exist yet, skip if (!old) return // query doesn't exist yet, skip
@@ -211,8 +206,8 @@ export function ListConvosProviderInner({
function updateConvoInAllLists( function updateConvoInAllLists(
convoId: string, convoId: string,
fn: ( fn: (
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
) => ChatBskyConvoDefs.ConvoView, ) => chat.bsky.convo.defs.ConvoView,
) { ) {
queryClient.setQueriesData<ConvoListQueryData>( queryClient.setQueriesData<ConvoListQueryData>(
{queryKey: [RQKEY_ROOT]}, {queryKey: [RQKEY_ROOT]},
@@ -227,10 +222,10 @@ export function ListConvosProviderInner({
function mutateConvoView( function mutateConvoView(
convoId: string, convoId: string,
fn: ( fn: (
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
) => ChatBskyConvoDefs.ConvoView, ) => chat.bsky.convo.defs.ConvoView,
) { ) {
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>( queryClient.setQueryData<chat.bsky.convo.defs.ConvoView>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
old => (old ? fn(old) : old), old => (old ? fn(old) : old),
) )
@@ -251,7 +246,7 @@ export function ListConvosProviderInner({
function handleMemberAdded( function handleMemberAdded(
convoId: string, convoId: string,
did: string, did: string,
relatedProfiles: ChatBskyActorDefs.ProfileViewBasic[], relatedProfiles: chat.bsky.actor.defs.ProfileViewBasic[],
rev: string, rev: string,
) { ) {
const newMember = relatedProfiles.find(r => r.did === did) const newMember = relatedProfiles.find(r => r.did === did)
@@ -261,7 +256,7 @@ export function ListConvosProviderInner({
const alreadyKnownMember = const alreadyKnownMember =
queryClient queryClient
.getQueryData< .getQueryData<
ChatBskyActorDefs.ProfileViewBasic[] chat.bsky.actor.defs.ProfileViewBasic[]
>(listConvoMembersQueryKey(convoId)) >(listConvoMembersQueryKey(convoId))
?.some(m => m.did === did) ?? false ?.some(m => m.did === did) ?? false
mutateMembers(convoId, list => mutateMembers(convoId, list =>
@@ -285,7 +280,7 @@ export function ListConvosProviderInner({
const alreadyRemovedMember = const alreadyRemovedMember =
queryClient queryClient
.getQueryData< .getQueryData<
ChatBskyActorDefs.ProfileViewBasic[] chat.bsky.actor.defs.ProfileViewBasic[]
>(listConvoMembersQueryKey(convoId)) >(listConvoMembersQueryKey(convoId))
?.some(m => m.did === did) === false ?.some(m => m.did === did) === false
mutateMembers(convoId, list => list.filter(m => m.did !== did)) mutateMembers(convoId, list => list.filter(m => m.did !== did))
@@ -298,24 +293,36 @@ export function ListConvosProviderInner({
} }
for (const log of events.logs) { for (const log of events.logs) {
if (ChatBskyConvoDefs.isLogBeginConvo(log)) { if (bsky.isType(chat.bsky.convo.defs.logBeginConvo, log)) {
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogLeaveConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logLeaveConvo, log)) {
deleteConvoFromAllLists(log.convoId) deleteConvoFromAllLists(log.convoId)
// The viewer is no longer in this convo (they left on another // The viewer is no longer in this convo (they left on another
// device, or were removed - removed members receive a // device, or were removed - removed members receive a
// logLeaveConvo, not a logRemoveMember). Refetch any cached join // logLeaveConvo, not a logRemoveMember). Refetch any cached join
// link preview so its viewer state reflects the lost membership. // link preview so its viewer state reflects the lost membership.
void invalidateJoinLinkPreviewsForConvo(queryClient, log.convoId) void invalidateJoinLinkPreviewsForConvo(queryClient, log.convoId)
} else if (ChatBskyConvoDefs.isLogDeleteMessage(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logDeleteMessage, log)) {
updateConvoInAllLists( updateConvoInAllLists(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => { withRevGuard(log.rev, convo => {
if ( if (
(ChatBskyConvoDefs.isDeletedMessageView(log.message) || (bsky.isType(
ChatBskyConvoDefs.isMessageView(log.message)) && chat.bsky.convo.defs.deletedMessageView,
(ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage) || log.message,
ChatBskyConvoDefs.isMessageView(convo.lastMessage)) ) ||
bsky.isType(
chat.bsky.convo.defs.messageView,
log.message,
)) &&
(bsky.isType(
chat.bsky.convo.defs.deletedMessageView,
convo.lastMessage,
) ||
bsky.isType(
chat.bsky.convo.defs.messageView,
convo.lastMessage,
))
) { ) {
return log.message.id === convo.lastMessage.id return log.message.id === convo.lastMessage.id
? { ? {
@@ -329,9 +336,9 @@ export function ListConvosProviderInner({
} }
}), }),
) )
} else if (ChatBskyConvoDefs.isLogCreateMessage(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logCreateMessage, log)) {
// Store in a new var to avoid TS errors due to closures. // Store in a new var to avoid TS errors due to closures.
const logRef: ChatBskyConvoDefs.LogCreateMessage = log const logRef: chat.bsky.convo.defs.LogCreateMessage = log
// Get all matching queries // Get all matching queries
const queries = queryClient.getQueriesData<ConvoListQueryData>({ const queries = queryClient.getQueriesData<ConvoListQueryData>({
@@ -339,7 +346,7 @@ export function ListConvosProviderInner({
}) })
// Check if convo exists in any query // Check if convo exists in any query
let foundConvo: ChatBskyConvoDefs.ConvoView | null = null let foundConvo: chat.bsky.convo.defs.ConvoView | null = null
for (const [_key, query] of queries) { for (const [_key, query] of queries) {
if (!query) continue if (!query) continue
const convo = getConvoFromQueryData(logRef.convoId, query) const convo = getConvoFromQueryData(logRef.convoId, query)
@@ -382,15 +389,23 @@ export function ListConvosProviderInner({
lastMessage: logRef.message, lastMessage: logRef.message,
unreadCount: unreadCount:
foundConvo.id !== currentConvoId foundConvo.id !== currentConvoId
? (ChatBskyConvoDefs.isMessageView(logRef.message) || ? (bsky.isType(
ChatBskyConvoDefs.isDeletedMessageView(logRef.message)) && chat.bsky.convo.defs.messageView,
logRef.message,
) ||
bsky.isType(
chat.bsky.convo.defs.deletedMessageView,
logRef.message,
)) &&
logRef.message.sender.did !== currentAccount?.did logRef.message.sender.did !== currentAccount?.did
? foundConvo.unreadCount + 1 ? foundConvo.unreadCount + 1
: foundConvo.unreadCount : foundConvo.unreadCount
: 0, : 0,
} }
function filterConvoFromPage(convo: ChatBskyConvoDefs.ConvoView[]) { function filterConvoFromPage(
convo: chat.bsky.convo.defs.ConvoView[],
) {
return convo.filter(c => c.id !== logRef.convoId) return convo.filter(c => c.id !== logRef.convoId)
} }
@@ -453,7 +468,7 @@ export function ListConvosProviderInner({
old => moveConvoToTopInRequests(updatedConvo, old), old => moveConvoToTopInRequests(updatedConvo, old),
) )
} }
} else if (ChatBskyConvoDefs.isLogReadMessage(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logReadMessage, log)) {
updateConvoInAllLists( updateConvoInAllLists(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => ({ withRevGuard(log.rev, convo => ({
@@ -462,7 +477,7 @@ export function ListConvosProviderInner({
rev: log.rev, rev: log.rev,
})), })),
) )
} else if (ChatBskyConvoDefs.isLogReadConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logReadConvo, log)) {
updateConvoInAllLists( updateConvoInAllLists(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => ({ withRevGuard(log.rev, convo => ({
@@ -471,12 +486,12 @@ export function ListConvosProviderInner({
rev: log.rev, rev: log.rev,
})), })),
) )
} else if (ChatBskyConvoDefs.isLogAcceptConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logAcceptConvo, log)) {
const requestQueries = const requestQueries =
queryClient.getQueriesData<ConvoListQueryData>({ queryClient.getQueriesData<ConvoListQueryData>({
queryKey: RQKEY_PARTIAL('request'), queryKey: RQKEY_PARTIAL('request'),
}) })
let foundConvo: ChatBskyConvoDefs.ConvoView | null = null let foundConvo: chat.bsky.convo.defs.ConvoView | null = null
for (const [_key, data] of requestQueries) { for (const [_key, data] of requestQueries) {
if (!data) continue if (!data) continue
foundConvo = getConvoFromQueryData(log.convoId, data) foundConvo = getConvoFromQueryData(log.convoId, data)
@@ -492,7 +507,7 @@ export function ListConvosProviderInner({
if (log.rev <= foundConvo.rev) { if (log.rev <= foundConvo.rev) {
continue continue
} }
const acceptedConvo: ChatBskyConvoDefs.ConvoView = { const acceptedConvo: chat.bsky.convo.defs.ConvoView = {
...foundConvo, ...foundConvo,
status: 'accepted', status: 'accepted',
rev: log.rev, rev: log.rev,
@@ -553,7 +568,7 @@ export function ListConvosProviderInner({
} }
}, },
) )
} else if (ChatBskyConvoDefs.isLogMuteConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logMuteConvo, log)) {
mutateConvoView( mutateConvoView(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => ({ withRevGuard(log.rev, convo => ({
@@ -562,7 +577,7 @@ export function ListConvosProviderInner({
rev: log.rev, rev: log.rev,
})), })),
) )
} else if (ChatBskyConvoDefs.isLogUnmuteConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logUnmuteConvo, log)) {
mutateConvoView( mutateConvoView(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => ({ withRevGuard(log.rev, convo => ({
@@ -571,11 +586,11 @@ export function ListConvosProviderInner({
rev: log.rev, rev: log.rev,
})), })),
) )
} else if (ChatBskyConvoDefs.isLogLockConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logLockConvo, log)) {
mutateConvoView( mutateConvoView(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => { withRevGuard(log.rev, convo => {
if (ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
return { return {
...convo, ...convo,
kind: {...convo.kind, lockStatus: 'locked'}, kind: {...convo.kind, lockStatus: 'locked'},
@@ -590,11 +605,11 @@ export function ListConvosProviderInner({
void queryClient.invalidateQueries({ void queryClient.invalidateQueries({
queryKey: CONVO_KEY(log.convoId), queryKey: CONVO_KEY(log.convoId),
}) })
} else if (ChatBskyConvoDefs.isLogUnlockConvo(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logUnlockConvo, log)) {
mutateConvoView( mutateConvoView(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => { withRevGuard(log.rev, convo => {
if (ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
return { return {
...convo, ...convo,
kind: { kind: {
@@ -609,11 +624,13 @@ export function ListConvosProviderInner({
return {...convo, rev: log.rev} return {...convo, rev: log.rev}
}), }),
) )
} else if (ChatBskyConvoDefs.isLogLockConvoPermanently(log)) { } else if (
bsky.isType(chat.bsky.convo.defs.logLockConvoPermanently, log)
) {
mutateConvoView( mutateConvoView(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => { withRevGuard(log.rev, convo => {
if (ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
return { return {
...convo, ...convo,
kind: {...convo.kind, lockStatus: 'locked-permanently'}, kind: {...convo.kind, lockStatus: 'locked-permanently'},
@@ -624,20 +641,20 @@ export function ListConvosProviderInner({
}), }),
) )
} else if ( } else if (
ChatBskyConvoDefs.isLogCreateJoinLink(log) || bsky.isType(chat.bsky.convo.defs.logCreateJoinLink, log) ||
ChatBskyConvoDefs.isLogEditJoinLink(log) || bsky.isType(chat.bsky.convo.defs.logEditJoinLink, log) ||
ChatBskyConvoDefs.isLogEnableJoinLink(log) || bsky.isType(chat.bsky.convo.defs.logEnableJoinLink, log) ||
ChatBskyConvoDefs.isLogDisableJoinLink(log) bsky.isType(chat.bsky.convo.defs.logDisableJoinLink, log)
) { ) {
// Join link data not included in the log event, trigger refetch to get it // Join link data not included in the log event, trigger refetch to get it
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogEditGroup(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logEditGroup, log)) {
// Updated group details (name etc.) aren't included in the log // Updated group details (name etc.) aren't included in the log
// event, so refetch to pick them up. // event, so refetch to pick them up.
debouncedRefetch() debouncedRefetch()
} else if ( } else if (
ChatBskyConvoDefs.isLogApproveJoinRequest(log) || bsky.isType(chat.bsky.convo.defs.logApproveJoinRequest, log) ||
ChatBskyConvoDefs.isLogRejectJoinRequest(log) bsky.isType(chat.bsky.convo.defs.logRejectJoinRequest, log)
) { ) {
// Route through mutateConvoView (not updateConvoInAllLists) so the // Route through mutateConvoView (not updateConvoInAllLists) so the
// single-convo cache updates too, keeping the in-convo requests // single-convo cache updates too, keeping the in-convo requests
@@ -648,7 +665,9 @@ export function ListConvosProviderInner({
applyJoinRequestCountDelta(convo, log.rev, -1), applyJoinRequestCountDelta(convo, log.rev, -1),
), ),
) )
} else if (ChatBskyConvoDefs.isLogIncomingJoinRequest(log)) { } else if (
bsky.isType(chat.bsky.convo.defs.logIncomingJoinRequest, log)
) {
// Route through mutateConvoView (not updateConvoInAllLists) so the // Route through mutateConvoView (not updateConvoInAllLists) so the
// single-convo cache updates too, letting the in-convo requests // single-convo cache updates too, letting the in-convo requests
// banner appear live. // banner appear live.
@@ -658,14 +677,16 @@ export function ListConvosProviderInner({
applyJoinRequestCountDelta(convo, log.rev, 1), applyJoinRequestCountDelta(convo, log.rev, 1),
), ),
) )
} else if (ChatBskyConvoDefs.isLogReadJoinRequests(log)) { } else if (
bsky.isType(chat.bsky.convo.defs.logReadJoinRequests, log)
) {
// The owner marked join requests as read (possibly on another // The owner marked join requests as read (possibly on another
// device). Zero the unread count but keep the total, mirroring the // device). Zero the unread count but keep the total, mirroring the
// useMarkJoinRequestsRead mutation. // useMarkJoinRequestsRead mutation.
mutateConvoView( mutateConvoView(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => { withRevGuard(log.rev, convo => {
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (!bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
return {...convo, rev: log.rev} return {...convo, rev: log.rev}
} }
return { return {
@@ -675,11 +696,18 @@ export function ListConvosProviderInner({
} }
}), }),
) )
} else if (ChatBskyConvoDefs.isLogOutgoingJoinRequest(log)) { } else if (
bsky.isType(chat.bsky.convo.defs.logOutgoingJoinRequest, log)
) {
// Viewer isn't in the chat yet, but the inbox surfaces outgoing // Viewer isn't in the chat yet, but the inbox surfaces outgoing
// requests, so refetch to pick up the new entry. // requests, so refetch to pick up the new entry.
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogWithdrawIncomingJoinRequest(log)) { } else if (
bsky.isType(
chat.bsky.convo.defs.logWithdrawIncomingJoinRequest,
log,
)
) {
// A requester rescinded their request to a group the viewer owns. // A requester rescinded their request to a group the viewer owns.
// Mirror of isLogIncomingJoinRequest: decrement the counts. // Mirror of isLogIncomingJoinRequest: decrement the counts.
mutateConvoView( mutateConvoView(
@@ -688,14 +716,19 @@ export function ListConvosProviderInner({
applyJoinRequestCountDelta(convo, log.rev, -1), applyJoinRequestCountDelta(convo, log.rev, -1),
), ),
) )
} else if (ChatBskyConvoDefs.isLogWithdrawOutgoingJoinRequest(log)) { } else if (
bsky.isType(
chat.bsky.convo.defs.logWithdrawOutgoingJoinRequest,
log,
)
) {
// The viewer rescinded their own outgoing join request (possibly on // The viewer rescinded their own outgoing join request (possibly on
// another device). Remove it from the requests inbox cache. // another device). Remove it from the requests inbox cache.
queryClient.setQueriesData<ConvoRequestListQueryData>( queryClient.setQueriesData<ConvoRequestListQueryData>(
{queryKey: [REQUESTS_RQKEY_ROOT]}, {queryKey: [REQUESTS_RQKEY_ROOT]},
old => optimisticDeleteJoinRequest(log.convoId, old), old => optimisticDeleteJoinRequest(log.convoId, old),
) )
} else if (ChatBskyConvoDefs.isLogAddReaction(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logAddReaction, log)) {
updateConvoInAllLists( updateConvoInAllLists(
log.convoId, log.convoId,
withRevGuard(log.rev, convo => { withRevGuard(log.rev, convo => {
@@ -718,13 +751,10 @@ export function ListConvosProviderInner({
} }
}), }),
) )
} else if (ChatBskyConvoDefs.isLogAddMember(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logAddMember, log)) {
const data = log.message.data const data = log.message.data
if ( if (
bsky.dangerousIsType<ChatBskyConvoDefs.SystemMessageDataAddMember>( bsky.isType(chat.bsky.convo.defs.systemMessageDataAddMember, data)
data,
ChatBskyConvoDefs.isSystemMessageDataAddMember,
)
) { ) {
handleMemberAdded( handleMemberAdded(
log.convoId, log.convoId,
@@ -738,12 +768,12 @@ export function ListConvosProviderInner({
queryKey: CONVO_KEY(log.convoId), queryKey: CONVO_KEY(log.convoId),
}) })
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogRemoveMember(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logRemoveMember, log)) {
const data = log.message.data const data = log.message.data
if ( if (
bsky.dangerousIsType<ChatBskyConvoDefs.SystemMessageDataRemoveMember>( bsky.isType(
chat.bsky.convo.defs.systemMessageDataRemoveMember,
data, data,
ChatBskyConvoDefs.isSystemMessageDataRemoveMember,
) )
) { ) {
handleMemberRemoved(log.convoId, data.member.did, log.rev) handleMemberRemoved(log.convoId, data.member.did, log.rev)
@@ -753,12 +783,12 @@ export function ListConvosProviderInner({
queryKey: CONVO_KEY(log.convoId), queryKey: CONVO_KEY(log.convoId),
}) })
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogMemberJoin(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logMemberJoin, log)) {
const data = log.message.data const data = log.message.data
if ( if (
bsky.dangerousIsType<ChatBskyConvoDefs.SystemMessageDataMemberJoin>( bsky.isType(
chat.bsky.convo.defs.systemMessageDataMemberJoin,
data, data,
ChatBskyConvoDefs.isSystemMessageDataMemberJoin,
) )
) { ) {
handleMemberAdded( handleMemberAdded(
@@ -772,12 +802,12 @@ export function ListConvosProviderInner({
queryKey: CONVO_KEY(log.convoId), queryKey: CONVO_KEY(log.convoId),
}) })
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogMemberLeave(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logMemberLeave, log)) {
const data = log.message.data const data = log.message.data
if ( if (
bsky.dangerousIsType<ChatBskyConvoDefs.SystemMessageDataMemberLeave>( bsky.isType(
chat.bsky.convo.defs.systemMessageDataMemberLeave,
data, data,
ChatBskyConvoDefs.isSystemMessageDataMemberLeave,
) )
) { ) {
handleMemberRemoved(log.convoId, data.member.did, log.rev) handleMemberRemoved(log.convoId, data.member.did, log.rev)
@@ -786,7 +816,7 @@ export function ListConvosProviderInner({
queryKey: CONVO_KEY(log.convoId), queryKey: CONVO_KEY(log.convoId),
}) })
debouncedRefetch() debouncedRefetch()
} else if (ChatBskyConvoDefs.isLogRemoveReaction(log)) { } else if (bsky.isType(chat.bsky.convo.defs.logRemoveReaction, log)) {
queryClient.setQueriesData( queryClient.setQueriesData(
{queryKey: [RQKEY_ROOT]}, {queryKey: [RQKEY_ROOT]},
(old?: ConvoListQueryData) => (old?: ConvoListQueryData) =>
@@ -797,10 +827,14 @@ export function ListConvosProviderInner({
if ( if (
// if the convo is the same // if the convo is the same
log.convoId === convo.id && log.convoId === convo.id &&
ChatBskyConvoDefs.isMessageAndReactionView( bsky.isType(
chat.bsky.convo.defs.messageAndReactionView,
convo.lastReaction, convo.lastReaction,
) && ) &&
ChatBskyConvoDefs.isMessageView(log.message) && bsky.isType(
chat.bsky.convo.defs.messageView,
log.message,
) &&
// ...and the message is the same // ...and the message is the same
convo.lastReaction.message.id === log.message.id && convo.lastReaction.message.id === log.message.id &&
// ...and the reaction is the same // ...and the reaction is the same
@@ -882,7 +916,7 @@ export function useUnreadMessageCount(): {
export type ConvoListQueryData = { export type ConvoListQueryData = {
pageParams: Array<string | undefined> pageParams: Array<string | undefined>
pages: Array<ChatBskyConvoListConvos.OutputSchema> pages: Array<chat.bsky.convo.listConvos.$OutputBody>
} }
export function useOnMarkAsRead() { export function useOnMarkAsRead() {
@@ -919,8 +953,8 @@ export function useOnMarkAsRead() {
*/ */
function withRevGuard( function withRevGuard(
rev: string, rev: string,
fn: (convo: ChatBskyConvoDefs.ConvoView) => ChatBskyConvoDefs.ConvoView, fn: (convo: chat.bsky.convo.defs.ConvoView) => chat.bsky.convo.defs.ConvoView,
): (convo: ChatBskyConvoDefs.ConvoView) => ChatBskyConvoDefs.ConvoView { ): (convo: chat.bsky.convo.defs.ConvoView) => chat.bsky.convo.defs.ConvoView {
return convo => (rev <= convo.rev ? convo : fn(convo)) return convo => (rev <= convo.rev ? convo : fn(convo))
} }
@@ -928,8 +962,8 @@ function optimisticUpdate(
chatId: string, chatId: string,
old?: ConvoListQueryData, old?: ConvoListQueryData,
updateFn?: ( updateFn?: (
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
) => ChatBskyConvoDefs.ConvoView, ) => chat.bsky.convo.defs.ConvoView,
) { ) {
if (!old || !updateFn) return old if (!old || !updateFn) return old
@@ -945,12 +979,12 @@ function optimisticUpdate(
} }
function applyJoinRequestCountDelta( function applyJoinRequestCountDelta(
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
rev: string, rev: string,
delta: 1 | -1, delta: 1 | -1,
): ChatBskyConvoDefs.ConvoView { ): chat.bsky.convo.defs.ConvoView {
// Join requests are only meaningful for group convos. // Join requests are only meaningful for group convos.
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (!bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
return {...convo, rev} return {...convo, rev}
} }
// Bump the total and unread counts together. Both are clamped at 0 and // Bump the total and unread counts together. Both are clamped at 0 and
@@ -971,7 +1005,7 @@ function applyJoinRequestCountDelta(
} }
function moveConvoToTopInRequests( function moveConvoToTopInRequests(
updatedConvo: ChatBskyConvoDefs.ConvoView, updatedConvo: chat.bsky.convo.defs.ConvoView,
old: ConvoRequestListQueryData | undefined, old: ConvoRequestListQueryData | undefined,
): ConvoRequestListQueryData | undefined { ): ConvoRequestListQueryData | undefined {
if (!old) return old if (!old) return old
@@ -985,7 +1019,8 @@ function moveConvoToTopInRequests(
pages: old.pages.map((page, i) => { pages: old.pages.map((page, i) => {
const filtered = page.requests.filter( const filtered = page.requests.filter(
item => item =>
!ChatBskyConvoDefs.isConvoView(item) || item.id !== updatedConvo.id, !bsky.isType(chat.bsky.convo.defs.convoView, item) ||
item.id !== updatedConvo.id,
) )
if (i === 0) { if (i === 0) {
return { return {
@@ -999,13 +1034,13 @@ function moveConvoToTopInRequests(
} }
function removeMemberFromConvoView( function removeMemberFromConvoView(
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
did: string, did: string,
rev: string, rev: string,
alreadyRemovedMember: boolean, alreadyRemovedMember: boolean,
): ChatBskyConvoDefs.ConvoView { ): chat.bsky.convo.defs.ConvoView {
// Member add/remove/join/leave events are only meaningful for group convos. // Member add/remove/join/leave events are only meaningful for group convos.
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) return convo if (!bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) return convo
const nextMembers = convo.members.filter(m => m.did !== did) const nextMembers = convo.members.filter(m => m.did !== did)
return { return {
...convo, ...convo,
@@ -1021,13 +1056,13 @@ function removeMemberFromConvoView(
} }
function addMemberToConvoView( function addMemberToConvoView(
convo: ChatBskyConvoDefs.ConvoView, convo: chat.bsky.convo.defs.ConvoView,
member: ChatBskyActorDefs.ProfileViewBasic, member: chat.bsky.actor.defs.ProfileViewBasic,
rev: string, rev: string,
alreadyKnownMember: boolean, alreadyKnownMember: boolean,
): ChatBskyConvoDefs.ConvoView { ): chat.bsky.convo.defs.ConvoView {
// Member add/remove/join/leave events are only meaningful for group convos. // Member add/remove/join/leave events are only meaningful for group convos.
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) return convo if (!bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) return convo
const alreadyInCuratedList = convo.members.some(m => m.did === member.did) const alreadyInCuratedList = convo.members.some(m => m.did === member.did)
const nextMembers = alreadyInCuratedList const nextMembers = alreadyInCuratedList
? convo.members ? convo.members
@@ -1073,7 +1108,7 @@ export function* findAllProfilesInQueryData(
did: string, did: string,
) { ) {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
@@ -1,4 +1,3 @@
import {type ChatBskyActorDefs} from '@atproto/api'
import {type QueryClient, useQuery} from '@tanstack/react-query' import {type QueryClient, useQuery} from '@tanstack/react-query'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
@@ -18,7 +17,7 @@ export function useListConvoMembersQuery({
placeholderData, placeholderData,
}: { }: {
convoId: string convoId: string
placeholderData?: ChatBskyActorDefs.ProfileViewBasic[] placeholderData?: chat.bsky.actor.defs.ProfileViewBasic[]
}) { }) {
const client = useChatClient() const client = useChatClient()
@@ -32,7 +31,7 @@ export function useListConvoMembersQuery({
* `members` with the exported profile type also keeps the hook's result * `members` with the exported profile type also keeps the hook's result
* type unchanged for consumers. * type unchanged for consumers.
*/ */
const members: ChatBskyActorDefs.ProfileViewBasic[] = [] const members: chat.bsky.actor.defs.ProfileViewBasic[] = []
let cursor: string | undefined let cursor: string | undefined
do { do {
@@ -55,9 +54,9 @@ export function useListConvoMembersQuery({
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<ChatBskyActorDefs.ProfileViewBasic, void> { ): Generator<chat.bsky.actor.defs.ProfileViewBasic, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
ChatBskyActorDefs.ProfileViewBasic[] chat.bsky.actor.defs.ProfileViewBasic[]
>({ >({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
@@ -1,7 +1,7 @@
import {useEffect} from 'react' import {useEffect} from 'react'
import {ChatBskyConvoDefs} from '@atproto/api'
import {useInfiniteQuery, useQueryClient} from '@tanstack/react-query' import {useInfiniteQuery, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {useMessagesEventBus} from '#/state/messages/events' import {useMessagesEventBus} from '#/state/messages/events'
import {createQueryKey} from '#/state/queries/util' import {createQueryKey} from '#/state/queries/util'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
@@ -35,9 +35,9 @@ export function useListJoinRequestsQuery({
if (event.type !== 'logs') return if (event.type !== 'logs') return
for (const log of event.logs) { for (const log of event.logs) {
if ( if (
ChatBskyConvoDefs.isLogIncomingJoinRequest(log) || bsky.isType(chat.bsky.convo.defs.logIncomingJoinRequest, log) ||
ChatBskyConvoDefs.isLogApproveJoinRequest(log) || bsky.isType(chat.bsky.convo.defs.logApproveJoinRequest, log) ||
ChatBskyConvoDefs.isLogRejectJoinRequest(log) bsky.isType(chat.bsky.convo.defs.logRejectJoinRequest, log)
) { ) {
void queryClient.invalidateQueries({ void queryClient.invalidateQueries({
queryKey: createListJoinRequestsQueryKey({convoId}), queryKey: createListJoinRequestsQueryKey({convoId}),
@@ -1,6 +1,6 @@
import {ChatBskyConvoDefs, type ChatBskyConvoLockConvo} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
import { import {
@@ -15,7 +15,7 @@ export function useLockConvo(
onError, onError,
}: { }: {
onSuccess?: ( onSuccess?: (
data: ChatBskyConvoLockConvo.OutputSchema, data: chat.bsky.convo.lockConvo.$OutputBody,
variables: {lock: boolean; silent?: boolean}, variables: {lock: boolean; silent?: boolean},
) => void ) => void
onError?: ( onError?: (
@@ -39,7 +39,8 @@ export function useLockConvo(
onMutate: ({lock}) => { onMutate: ({lock}) => {
if (!convoId) return if (!convoId) return
return updateConvoOptimistic(queryClient, convoId, prev => { return updateConvoOptimistic(queryClient, convoId, prev => {
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) return undefined if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind))
return undefined
return { return {
...prev, ...prev,
kind: { kind: {
@@ -1,6 +1,6 @@
import {ChatBskyConvoDefs} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
@@ -22,13 +22,15 @@ export function useMarkJoinRequestsRead(convoId: string | undefined) {
onMutate: () => { onMutate: () => {
if (!convoId) return if (!convoId) return
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>( const prevConvo =
CONVO_KEY(convoId), queryClient.getQueryData<chat.bsky.convo.defs.ConvoView>(
) CONVO_KEY(convoId),
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView | undefined>( )
queryClient.setQueryData<chat.bsky.convo.defs.ConvoView | undefined>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
old => { old => {
if (!old || !ChatBskyConvoDefs.isGroupConvo(old.kind)) return old if (!old || !bsky.isType(chat.bsky.convo.defs.groupConvo, old.kind))
return old
return { return {
...old, ...old,
kind: {...old.kind, unreadJoinRequestCount: 0}, kind: {...old.kind, unreadJoinRequestCount: 0},
@@ -50,7 +52,7 @@ export function useMarkJoinRequestsRead(convoId: string | undefined) {
convos: page.convos.map(convo => { convos: page.convos.map(convo => {
if ( if (
convo.id !== convoId || convo.id !== convoId ||
!ChatBskyConvoDefs.isGroupConvo(convo.kind) !bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)
) { ) {
return convo return convo
} }
@@ -1,4 +1,3 @@
import {type ChatBskyConvoMuteConvo} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
@@ -14,7 +13,7 @@ export function useMuteConvo(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyConvoMuteConvo.OutputSchema) => void onSuccess?: (data: chat.bsky.convo.muteConvo.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
+13 -17
View File
@@ -1,9 +1,3 @@
import {
type ChatBskyActorDefs,
ChatBskyConvoDefs,
type ChatBskyConvoListConvos,
type ChatBskyGroupRemoveMembers,
} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -11,6 +5,7 @@ import {
useQueryClient, useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useChatClient} from '#/state/session' import {useChatClient} from '#/state/session'
import {chat} from '#/lexicons' import {chat} from '#/lexicons'
@@ -24,7 +19,7 @@ export function useRemoveFromGroupChat(
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupRemoveMembers.OutputSchema) => void onSuccess?: (data: chat.bsky.group.removeMembers.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
}, },
) { ) {
@@ -43,23 +38,24 @@ export function useRemoveFromGroupChat(
onMutate: ({members}) => { onMutate: ({members}) => {
if (!convoId) return if (!convoId) return
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>( const prevConvo =
CONVO_KEY(convoId), queryClient.getQueryData<chat.bsky.convo.defs.ConvoView>(
) CONVO_KEY(convoId),
)
const prevListEntries = queryClient.getQueriesData< const prevListEntries = queryClient.getQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}) >({queryKey: [CONVO_LIST_KEY]})
const prevMemberList = queryClient.getQueryData< const prevMemberList = queryClient.getQueryData<
ChatBskyActorDefs.ProfileViewBasic[] chat.bsky.actor.defs.ProfileViewBasic[]
>(listConvoMembersQueryKey(convoId)) >(listConvoMembersQueryKey(convoId))
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>( queryClient.setQueryData<chat.bsky.convo.defs.ConvoView>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
prev => { prev => {
if (!prev) return if (!prev) return
const nextMembers = prev.members.filter(m => !members.includes(m.did)) const nextMembers = prev.members.filter(m => !members.includes(m.did))
const removed = prev.members.length - nextMembers.length const removed = prev.members.length - nextMembers.length
if (!ChatBskyConvoDefs.isGroupConvo(prev.kind)) { if (!bsky.isType(chat.bsky.convo.defs.groupConvo, prev.kind)) {
return {...prev, members: nextMembers} return {...prev, members: nextMembers}
} }
return { return {
@@ -74,7 +70,7 @@ export function useRemoveFromGroupChat(
) )
queryClient.setQueriesData< queryClient.setQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}, prev => { >({queryKey: [CONVO_LIST_KEY]}, prev => {
if (!prev?.pages) return if (!prev?.pages) return
return { return {
@@ -87,7 +83,7 @@ export function useRemoveFromGroupChat(
m => !members.includes(m.did), m => !members.includes(m.did),
) )
const removed = convo.members.length - nextMembers.length const removed = convo.members.length - nextMembers.length
if (!ChatBskyConvoDefs.isGroupConvo(convo.kind)) { if (!bsky.isType(chat.bsky.convo.defs.groupConvo, convo.kind)) {
return {...convo, members: nextMembers} return {...convo, members: nextMembers}
} }
return { return {
@@ -103,7 +99,7 @@ export function useRemoveFromGroupChat(
} }
}) })
queryClient.setQueryData<ChatBskyActorDefs.ProfileViewBasic[]>( queryClient.setQueryData<chat.bsky.actor.defs.ProfileViewBasic[]>(
listConvoMembersQueryKey(convoId), listConvoMembersQueryKey(convoId),
prev => { prev => {
if (!prev) return if (!prev) return
@@ -1,4 +1,3 @@
import {type ChatBskyGroupRequestJoin} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -10,7 +9,7 @@ export function useRequestJoinGroupChat({
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupRequestJoin.OutputSchema) => void onSuccess?: (data: chat.bsky.group.requestJoin.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
} = {}) { } = {}) {
const client = useChatClient() const client = useChatClient()
@@ -1,4 +1,3 @@
import {type ChatBskyConvoGetUnreadCounts} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -94,10 +93,12 @@ export function useUpdateAllRead(
// zero out the badge count query that actually drives the unread badge, // zero out the badge count query that actually drives the unread badge,
// since it's a separate server query that the list caches don't feed // since it's a separate server query that the list caches don't feed
const prevUnreadCountsQueries = const prevUnreadCountsQueries =
queryClient.getQueriesData<ChatBskyConvoGetUnreadCounts.OutputSchema>({ queryClient.getQueriesData<chat.bsky.convo.getUnreadCounts.$OutputBody>(
queryKey: UNREAD_COUNTS_PARTIAL_KEY, {
}) queryKey: UNREAD_COUNTS_PARTIAL_KEY,
queryClient.setQueriesData<ChatBskyConvoGetUnreadCounts.OutputSchema>( },
)
queryClient.setQueriesData<chat.bsky.convo.getUnreadCounts.$OutputBody>(
{queryKey: UNREAD_COUNTS_PARTIAL_KEY}, {queryKey: UNREAD_COUNTS_PARTIAL_KEY},
old => { old => {
if (!old) return old if (!old) return old
@@ -1,7 +1,3 @@
import {
type ChatBskyConvoDefs,
type ChatBskyConvoListConvos,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -10,15 +6,16 @@ import {
import {RQKEY as CONVO_KEY} from '../conversation' import {RQKEY as CONVO_KEY} from '../conversation'
import {RQKEY_ROOT as CONVO_LIST_KEY} from '../list-conversations' import {RQKEY_ROOT as CONVO_LIST_KEY} from '../list-conversations'
import {chat} from '#/lexicons'
type ConvoUpdater = ( type ConvoUpdater = (
prev: ChatBskyConvoDefs.ConvoView, prev: chat.bsky.convo.defs.ConvoView,
) => ChatBskyConvoDefs.ConvoView | undefined ) => chat.bsky.convo.defs.ConvoView | undefined
export type ConvoCacheSnapshot = { export type ConvoCacheSnapshot = {
prevConvo: ChatBskyConvoDefs.ConvoView | undefined prevConvo: chat.bsky.convo.defs.ConvoView | undefined
prevListEntries: Array< prevListEntries: Array<
[QueryKey, InfiniteData<ChatBskyConvoListConvos.OutputSchema> | undefined] [QueryKey, InfiniteData<chat.bsky.convo.listConvos.$OutputBody> | undefined]
> >
} }
@@ -34,14 +31,14 @@ export function updateConvoOptimistic(
convoId: string, convoId: string,
updater: ConvoUpdater, updater: ConvoUpdater,
): ConvoCacheSnapshot { ): ConvoCacheSnapshot {
const prevConvo = queryClient.getQueryData<ChatBskyConvoDefs.ConvoView>( const prevConvo = queryClient.getQueryData<chat.bsky.convo.defs.ConvoView>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
) )
const prevListEntries = queryClient.getQueriesData< const prevListEntries = queryClient.getQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}) >({queryKey: [CONVO_LIST_KEY]})
queryClient.setQueryData<ChatBskyConvoDefs.ConvoView>( queryClient.setQueryData<chat.bsky.convo.defs.ConvoView>(
CONVO_KEY(convoId), CONVO_KEY(convoId),
prev => { prev => {
if (!prev) return if (!prev) return
@@ -51,7 +48,7 @@ export function updateConvoOptimistic(
) )
queryClient.setQueriesData< queryClient.setQueriesData<
InfiniteData<ChatBskyConvoListConvos.OutputSchema> InfiniteData<chat.bsky.convo.listConvos.$OutputBody>
>({queryKey: [CONVO_LIST_KEY]}, prev => { >({queryKey: [CONVO_LIST_KEY]}, prev => {
if (!prev?.pages) return if (!prev?.pages) return
return { return {
@@ -1,4 +1,3 @@
import {type ChatBskyGroupWithdrawJoinRequest} from '@atproto/api'
import {useMutation, useQueryClient} from '@tanstack/react-query' import {useMutation, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger' import {logger} from '#/logger'
@@ -14,7 +13,7 @@ export function useWithdrawJoinGroupChatRequest({
onSuccess, onSuccess,
onError, onError,
}: { }: {
onSuccess?: (data: ChatBskyGroupWithdrawJoinRequest.OutputSchema) => void onSuccess?: (data: chat.bsky.group.withdrawJoinRequest.$OutputBody) => void
onError?: (error: Error) => void onError?: (error: Error) => void
} = {}) { } = {}) {
const client = useChatClient() const client = useChatClient()
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -37,7 +36,7 @@ export function useMyBlockedAccountsQuery() {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.graph.getBlocks.$OutputBody> InfiniteData<app.bsky.graph.getBlocks.$OutputBody>
>({ >({
+2 -3
View File
@@ -1,4 +1,3 @@
import {type AppBskyGraphDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import {type QueryClient, useQuery} from '@tanstack/react-query' import {type QueryClient, useQuery} from '@tanstack/react-query'
@@ -19,11 +18,11 @@ export const RQKEY = (filter: MyListsFilter) => [RQKEY_ROOT, filter]
export function useMyListsQuery(filter: MyListsFilter) { export function useMyListsQuery(filter: MyListsFilter) {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const client = useAppviewClient() const client = useAppviewClient()
return useQuery<AppBskyGraphDefs.ListView[]>({ return useQuery<app.bsky.graph.defs.ListView[]>({
staleTime: STALE.MINUTES.ONE, staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(filter), queryKey: RQKEY(filter),
async queryFn() { async queryFn() {
let lists: AppBskyGraphDefs.ListView[] = [] let lists: app.bsky.graph.defs.ListView[] = []
const promises = [ const promises = [
accumulate(cursor => accumulate(cursor =>
client client
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -37,7 +36,7 @@ export function useMyMutedAccountsQuery() {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.graph.getMutes.$OutputBody> InfiniteData<app.bsky.graph.getMutes.$OutputBody>
>({ >({
@@ -1,11 +1,11 @@
import {type AppBskyNotificationListNotifications} from '@atproto/api'
import {describe, expect, it, jest} from '@jest/globals' import {describe, expect, it, jest} from '@jest/globals'
import {groupNotifications} from '../util' import {groupNotifications} from '../util'
import {app} from '#/lexicons'
jest.mock('#/state/queries/profile', () => ({precacheProfile: jest.fn()})) jest.mock('#/state/queries/profile', () => ({precacheProfile: jest.fn()}))
type Notification = AppBskyNotificationListNotifications.Notification type Notification = app.bsky.notification.listNotifications.Notification
function makeFollowNotification( function makeFollowNotification(
did: string, did: string,
+9 -6
View File
@@ -17,7 +17,6 @@
*/ */
import {useCallback, useEffect, useMemo, useRef} from 'react' import {useCallback, useEffect, useMemo, useRef} from 'react'
import {AppBskyFeedDefs, AppBskyFeedPost, AtUri} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -26,12 +25,14 @@ import {
useQueryClient, useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {moderatePost} from '#/lib/moderation/subjects' import {moderatePost} from '#/lib/moderation/subjects'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
import {useAppviewClient} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies' import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies'
import type * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
import { import {
didOrHandleUriMatches, didOrHandleUriMatches,
embedViewRecordToPostView, embedViewRecordToPostView,
@@ -195,7 +196,9 @@ export function useNotificationFeedQuery(opts: {
* a `$type` field on the `subject`. But if the nested * a `$type` field on the `subject`. But if the nested
* `record` is a post, we know it's a post view. * `record` is a post, we know it's a post view.
*/ */
if (AppBskyFeedPost.isRecord(item.subject?.record)) { if (
bsky.isType(app.bsky.feed.post, item.subject?.record)
) {
const mod = moderatePost(item.subject, moderationOpts!) const mod = moderatePost(item.subject, moderationOpts!)
if (mod.ui('contentList').filter) { if (mod.ui('contentList').filter) {
return false return false
@@ -272,7 +275,7 @@ export function useNotificationFeedQuery(opts: {
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, void> { ): Generator<app.bsky.feed.defs.PostView, void> {
const atUri = new AtUri(uri) const atUri = new AtUri(uri)
const queryDatas = queryClient.getQueriesData<InfiniteData<FeedPage>>({ const queryDatas = queryClient.getQueriesData<InfiniteData<FeedPage>>({
@@ -291,7 +294,7 @@ export function* findAllPostsInQueryData(
} }
} }
if (AppBskyFeedDefs.isPostView(item.subject)) { if (bsky.isType(app.bsky.feed.defs.postView, item.subject)) {
const quotedPost = getEmbeddedPost(item.subject?.embed) const quotedPost = getEmbeddedPost(item.subject?.embed)
if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) {
yield embedViewRecordToPostView(quotedPost) yield embedViewRecordToPostView(quotedPost)
@@ -329,7 +332,7 @@ export function* findAllProfilesInQueryData(
) { ) {
yield item.subject.author yield item.subject.author
} }
if (AppBskyFeedDefs.isPostView(item.subject)) { if (bsky.isType(app.bsky.feed.defs.postView, item.subject)) {
const quotedPost = getEmbeddedPost(item.subject?.embed) const quotedPost = getEmbeddedPost(item.subject?.embed)
if (quotedPost?.author.did === did) { if (quotedPost?.author.did === did) {
yield quotedPost.author yield quotedPost.author
+12 -16
View File
@@ -1,7 +1,3 @@
import {
type AppBskyNotificationDefs,
type ChatBskyNotificationDefs,
} from '@atproto/api'
import {t} from '@lingui/core/macro' import {t} from '@lingui/core/macro'
import { import {
type QueryClient, type QueryClient,
@@ -24,18 +20,18 @@ const RQKEY_CHAT = [RQKEY_ROOT, 'chat']
// fetched and cached separately. This combined type names every preference for // fetched and cached separately. This combined type names every preference for
// the generic settings dialog, but it is never the shape of a query response. // the generic settings dialog, but it is never the shape of a query response.
export type NotificationSettingsPreferences = Omit< export type NotificationSettingsPreferences = Omit<
AppBskyNotificationDefs.Preferences, app.bsky.notification.defs.Preferences,
'chat' 'chat'
> & > &
Partial<Pick<ChatBskyNotificationDefs.Preferences, 'chat' | 'chatRequest'>> Partial<Pick<chat.bsky.notification.defs.Preferences, 'chat' | 'chatRequest'>>
export type AppNotificationSettingsPreferences = Omit< export type AppNotificationSettingsPreferences = Omit<
AppBskyNotificationDefs.Preferences, app.bsky.notification.defs.Preferences,
'chat' 'chat'
> >
export type ChatNotificationSettingsPreferences = Pick< export type ChatNotificationSettingsPreferences = Pick<
ChatBskyNotificationDefs.Preferences, chat.bsky.notification.defs.Preferences,
'chat' | 'chatRequest' 'chat' | 'chatRequest'
> >
@@ -45,9 +41,9 @@ export type NotificationSettingsPreferenceName = Exclude<
> >
export type NotificationSettingsPreference = export type NotificationSettingsPreference =
| AppBskyNotificationDefs.Preference | app.bsky.notification.defs.Preference
| AppBskyNotificationDefs.FilterablePreference | app.bsky.notification.defs.FilterablePreference
| ChatBskyNotificationDefs.ChatPreference | chat.bsky.notification.defs.ChatPreference
export function isChatPreferenceName( export function isChatPreferenceName(
name: NotificationSettingsPreferenceName, name: NotificationSettingsPreferenceName,
@@ -58,7 +54,7 @@ export function isChatPreferenceName(
type NotificationSettingsUpdate = Partial<NotificationSettingsPreferences> type NotificationSettingsUpdate = Partial<NotificationSettingsPreferences>
type AppNotificationSettingsUpdate = Partial< type AppNotificationSettingsUpdate = Partial<
Omit<AppBskyNotificationDefs.Preferences, '$type' | 'chat'> Omit<app.bsky.notification.defs.Preferences, '$type' | 'chat'>
> >
type ChatNotificationSettingsUpdate = type ChatNotificationSettingsUpdate =
@@ -159,15 +155,15 @@ function optimisticUpdateNotificationSettings(
} }
function appPreferencesWithoutChat( function appPreferencesWithoutChat(
preferences: AppBskyNotificationDefs.Preferences, preferences: app.bsky.notification.defs.Preferences,
): Omit<AppBskyNotificationDefs.Preferences, 'chat'> { ): Omit<app.bsky.notification.defs.Preferences, 'chat'> {
const {chat: _ignoredChat, ...appPreferences} = preferences const {chat: _ignoredChat, ...appPreferences} = preferences
return appPreferences return appPreferences
} }
function chatPreferencesForSettings( function chatPreferencesForSettings(
preferences: ChatBskyNotificationDefs.Preferences, preferences: chat.bsky.notification.defs.Preferences,
): Pick<ChatBskyNotificationDefs.Preferences, 'chat' | 'chatRequest'> { ): Pick<chat.bsky.notification.defs.Preferences, 'chat' | 'chatRequest'> {
return { return {
chat: preferences.chat, chat: preferences.chat,
chatRequest: preferences.chatRequest, chatRequest: preferences.chatRequest,
+8 -10
View File
@@ -1,8 +1,4 @@
import { import {app} from '#/lexicons'
type AppBskyFeedDefs,
type AppBskyGraphDefs,
type AppBskyNotificationListNotifications,
} from '@atproto/api'
export type NotificationType = export type NotificationType =
| StarterPackNotificationType | StarterPackNotificationType
@@ -11,11 +7,11 @@ export type NotificationType =
export type FeedNotification = export type FeedNotification =
| (FeedNotificationBase & { | (FeedNotificationBase & {
type: StarterPackNotificationType type: StarterPackNotificationType
subject?: AppBskyGraphDefs.StarterPackViewBasic subject?: app.bsky.graph.defs.StarterPackViewBasic
}) })
| (FeedNotificationBase & { | (FeedNotificationBase & {
type: OtherNotificationType type: OtherNotificationType
subject?: AppBskyFeedDefs.PostView subject?: app.bsky.feed.defs.PostView
}) })
export interface FeedPage { export interface FeedPage {
@@ -54,8 +50,10 @@ type OtherNotificationType =
type FeedNotificationBase = { type FeedNotificationBase = {
_reactKey: string _reactKey: string
notification: AppBskyNotificationListNotifications.Notification notification: app.bsky.notification.listNotifications.Notification
additional?: AppBskyNotificationListNotifications.Notification[] additional?: app.bsky.notification.listNotifications.Notification[]
subjectUri?: string subjectUri?: string
subject?: AppBskyFeedDefs.PostView | AppBskyGraphDefs.StarterPackViewBasic subject?:
| app.bsky.feed.defs.PostView
| app.bsky.graph.defs.StarterPackViewBasic
} }
+13 -31
View File
@@ -1,12 +1,3 @@
import {
type AppBskyFeedDefs,
AppBskyFeedLike,
AppBskyFeedPost,
AppBskyFeedRepost,
type AppBskyGraphDefs,
AppBskyGraphStarterpack,
type AppBskyNotificationListNotifications,
} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type AtUriString} from '@atproto/syntax' import {type AtUriString} from '@atproto/syntax'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
@@ -117,7 +108,7 @@ export async function fetchPage({
// = // =
export function shouldFilterNotif( export function shouldFilterNotif(
notif: AppBskyNotificationListNotifications.Notification, notif: app.bsky.notification.listNotifications.Notification,
moderationOpts: ModerationOpts | undefined, moderationOpts: ModerationOpts | undefined,
): boolean { ): boolean {
const containsImperative = !!notif.author.labels?.some(labelIsHideableOffense) const containsImperative = !!notif.author.labels?.some(labelIsHideableOffense)
@@ -129,10 +120,7 @@ export function shouldFilterNotif(
} }
if ( if (
notif.reason === 'subscribed-post' && notif.reason === 'subscribed-post' &&
bsky.dangerousIsType<AppBskyFeedPost.Record>( bsky.isType(app.bsky.feed.post, notif.record) &&
notif.record,
AppBskyFeedPost.isRecord,
) &&
hasMutedWord({ hasMutedWord({
mutedWords: moderationOpts.prefs.mutedWords, mutedWords: moderationOpts.prefs.mutedWords,
text: notif.record.text, text: notif.record.text,
@@ -151,7 +139,7 @@ export function shouldFilterNotif(
} }
export function groupNotifications( export function groupNotifications(
notifs: AppBskyNotificationListNotifications.Notification[], notifs: app.bsky.notification.listNotifications.Notification[],
): FeedNotification[] { ): FeedNotification[] {
const groupedNotifs: FeedNotification[] = [] const groupedNotifs: FeedNotification[] = []
for (const notif of notifs) { for (const notif of notifs) {
@@ -211,8 +199,8 @@ async function fetchSubjects(
client: Client, client: Client,
groupedNotifs: FeedNotification[], groupedNotifs: FeedNotification[],
): Promise<{ ): Promise<{
posts: Map<string, AppBskyFeedDefs.PostView> posts: Map<string, app.bsky.feed.defs.PostView>
starterPacks: Map<string, AppBskyGraphDefs.StarterPackViewBasic> starterPacks: Map<string, app.bsky.graph.defs.StarterPackViewBasic>
}> { }> {
const postUris = new Set<string>() const postUris = new Set<string>()
const packUris = new Set<string>() const packUris = new Set<string>()
@@ -243,15 +231,15 @@ async function fetchSubjects(
.then(data => data.starterPacks), .then(data => data.starterPacks),
), ),
) )
const postsMap = new Map<string, AppBskyFeedDefs.PostView>() const postsMap = new Map<string, app.bsky.feed.defs.PostView>()
const packsMap = new Map<string, AppBskyGraphDefs.StarterPackViewBasic>() const packsMap = new Map<string, app.bsky.graph.defs.StarterPackViewBasic>()
for (const post of postsChunks.flat()) { for (const post of postsChunks.flat()) {
if (AppBskyFeedPost.isRecord(post.record)) { if (bsky.isType(app.bsky.feed.post, post.record)) {
postsMap.set(post.uri, post) postsMap.set(post.uri, post)
} }
} }
for (const pack of packsChunks.flat()) { for (const pack of packsChunks.flat()) {
if (AppBskyGraphStarterpack.isRecord(pack.record)) { if (bsky.isType(app.bsky.graph.starterpack, pack.record)) {
packsMap.set(pack.uri, pack) packsMap.set(pack.uri, pack)
} }
} }
@@ -262,7 +250,7 @@ async function fetchSubjects(
} }
function toKnownType( function toKnownType(
notif: AppBskyNotificationListNotifications.Notification, notif: app.bsky.notification.listNotifications.Notification,
): NotificationType { ): NotificationType {
if (notif.reason === 'like') { if (notif.reason === 'like') {
if (notif.reasonSubject?.includes('feed.generator')) { if (notif.reasonSubject?.includes('feed.generator')) {
@@ -291,7 +279,7 @@ function toKnownType(
function getSubjectUri( function getSubjectUri(
type: NotificationType, type: NotificationType,
notif: AppBskyNotificationListNotifications.Notification, notif: app.bsky.notification.listNotifications.Notification,
): string | undefined { ): string | undefined {
if ( if (
type === 'reply' || type === 'reply' ||
@@ -307,14 +295,8 @@ function getSubjectUri(
type === 'repost-via-repost' type === 'repost-via-repost'
) { ) {
if ( if (
bsky.dangerousIsType<AppBskyFeedRepost.Record>( bsky.isType(app.bsky.feed.repost, notif.record) ||
notif.record, bsky.isType(app.bsky.feed.like, notif.record)
AppBskyFeedRepost.isRecord,
) ||
bsky.dangerousIsType<AppBskyFeedLike.Record>(
notif.record,
AppBskyFeedLike.isRecord,
)
) { ) {
return typeof notif.record.subject?.uri === 'string' return typeof notif.record.subject?.uri === 'string'
? notif.record.subject?.uri ? notif.record.subject?.uri
+3 -3
View File
@@ -1,7 +1,7 @@
import {type AppBskyActorDefs} from '@atproto/api' import {app} from '#/lexicons'
export type Data = Record<string, unknown> | undefined export type Data = Record<string, unknown> | undefined
export type BaseNux< export type BaseNux<
T extends Pick<AppBskyActorDefs.Nux, 'id' | 'expiresAt'> & {data: Data}, T extends Pick<app.bsky.actor.defs.Nux, 'id' | 'expiresAt'> & {data: Data},
> = Pick<AppBskyActorDefs.Nux, 'id' | 'completed' | 'expiresAt'> & T > = Pick<app.bsky.actor.defs.Nux, 'id' | 'completed' | 'expiresAt'> & T
+5 -4
View File
@@ -1,4 +1,4 @@
import {type AppBskyActorDefs, nuxSchema} from '@atproto/api' import {nuxSchema} from '@bsky.app/sdk/utils'
import { import {
type AppNux, type AppNux,
@@ -6,8 +6,9 @@ import {
nuxNames, nuxNames,
NuxSchemas, NuxSchemas,
} from '#/state/queries/nuxs/definitions' } from '#/state/queries/nuxs/definitions'
import {type app} from '#/lexicons'
export function parseAppNux(nux: AppBskyActorDefs.Nux): AppNux | undefined { export function parseAppNux(nux: app.bsky.actor.defs.Nux): AppNux | undefined {
if (!nuxNames.has(nux.id as Nux)) return if (!nuxNames.has(nux.id as Nux)) return
if (!nuxSchema.safeParse(nux).success) return if (!nuxSchema.safeParse(nux).success) return
@@ -32,11 +33,11 @@ export function parseAppNux(nux: AppBskyActorDefs.Nux): AppNux | undefined {
} as AppNux } as AppNux
} }
export function serializeAppNux(nux: AppNux): AppBskyActorDefs.Nux { export function serializeAppNux(nux: AppNux): app.bsky.actor.defs.Nux {
const {data, ...rest} = nux const {data, ...rest} = nux
const schema = NuxSchemas[nux.id] const schema = NuxSchemas[nux.id]
const result: AppBskyActorDefs.Nux = { const result: app.bsky.actor.defs.Nux = {
...rest, ...rest,
data: undefined, data: undefined,
} }
+16 -20
View File
@@ -1,13 +1,7 @@
import {useCallback, useEffect, useMemo, useRef} from 'react' import {useCallback, useEffect, useMemo, useRef} from 'react'
import {AppState} from 'react-native' import {AppState} from 'react-native'
import {
type AppBskyActorDefs,
AppBskyFeedDefs,
type AppBskyFeedPost,
AtUri,
} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type AtIdentifierString, type AtUriString} from '@atproto/syntax' import {type AtIdentifierString, type AtUriString, AtUri} from '@atproto/syntax'
import { import {
type ModerationDecision, type ModerationDecision,
type ModerationPrefs, type ModerationPrefs,
@@ -19,6 +13,8 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {AuthorFeedAPI} from '#/lib/api/feed/author' import {AuthorFeedAPI} from '#/lib/api/feed/author'
import {CustomFeedAPI} from '#/lib/api/feed/custom' import {CustomFeedAPI} from '#/lib/api/feed/custom'
import {DemoFeedAPI} from '#/lib/api/feed/demo' import {DemoFeedAPI} from '#/lib/api/feed/demo'
@@ -83,10 +79,10 @@ export function RQKEY(feedDesc: FeedDescriptor, params?: FeedParams) {
export interface FeedPostSliceItem { export interface FeedPostSliceItem {
_reactKey: string _reactKey: string
uri: string uri: string
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
moderation: ModerationDecision moderation: ModerationDecision
parentAuthor?: AppBskyActorDefs.ProfileViewBasic parentAuthor?: app.bsky.actor.defs.ProfileViewBasic
isParentBlocked?: boolean isParentBlocked?: boolean
isParentNotFound?: boolean isParentNotFound?: boolean
} }
@@ -101,8 +97,8 @@ export interface FeedPostSlice {
reqId: string | undefined reqId: string | undefined
feedPostUri: string feedPostUri: string
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}
} }
@@ -110,7 +106,7 @@ export interface FeedPostSlice {
export interface FeedPageUnselected { export interface FeedPageUnselected {
api: FeedAPI api: FeedAPI
cursor: string | undefined cursor: string | undefined
feed: AppBskyFeedDefs.FeedViewPost[] feed: app.bsky.feed.defs.FeedViewPost[]
fetchedAt: number fetchedAt: number
} }
@@ -515,7 +511,7 @@ function createApi({
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, undefined> { ): Generator<app.bsky.feed.defs.PostView, undefined> {
const atUri = new AtUri(uri) const atUri = new AtUri(uri)
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
@@ -538,7 +534,7 @@ export function* findAllPostsInQueryData(
yield embedViewRecordToPostView(quotedPost) yield embedViewRecordToPostView(quotedPost)
} }
if (AppBskyFeedDefs.isPostView(item.reply?.parent)) { if (bsky.isType(app.bsky.feed.defs.postView, item.reply?.parent)) {
if (didOrHandleUriMatches(atUri, item.reply.parent)) { if (didOrHandleUriMatches(atUri, item.reply.parent)) {
yield item.reply.parent yield item.reply.parent
} }
@@ -552,7 +548,7 @@ export function* findAllPostsInQueryData(
} }
} }
if (AppBskyFeedDefs.isPostView(item.reply?.root)) { if (bsky.isType(app.bsky.feed.defs.postView, item.reply?.root)) {
if (didOrHandleUriMatches(atUri, item.reply.root)) { if (didOrHandleUriMatches(atUri, item.reply.root)) {
yield item.reply.root yield item.reply.root
} }
@@ -570,7 +566,7 @@ export function* findAllPostsInQueryData(
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileViewBasic, undefined> { ): Generator<app.bsky.actor.defs.ProfileViewBasic, undefined> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<FeedPageUnselected> InfiniteData<FeedPageUnselected>
>({ >({
@@ -590,13 +586,13 @@ export function* findAllProfilesInQueryData(
yield quotedPost.author yield quotedPost.author
} }
if ( if (
AppBskyFeedDefs.isPostView(item.reply?.parent) && bsky.isType(app.bsky.feed.defs.postView, item.reply?.parent) &&
item.reply?.parent?.author.did === did item.reply?.parent?.author.did === did
) { ) {
yield item.reply.parent.author yield item.reply.parent.author
} }
if ( if (
AppBskyFeedDefs.isPostView(item.reply?.root) && bsky.isType(app.bsky.feed.defs.postView, item.reply?.root) &&
item.reply?.root?.author.did === did item.reply?.root?.author.did === did
) { ) {
yield item.reply.root.author yield item.reply.root.author
@@ -607,7 +603,7 @@ export function* findAllProfilesInQueryData(
} }
function assertSomePostsPassModeration( function assertSomePostsPassModeration(
feed: AppBskyFeedDefs.FeedViewPost[], feed: app.bsky.feed.defs.FeedViewPost[],
moderationPrefs: ModerationPrefs, moderationPrefs: ModerationPrefs,
) { ) {
// no posts in this feed // no posts in this feed
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type AtUriString} from '@atproto/syntax' import {type AtUriString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -83,7 +82,7 @@ export function useLikedBySampleQuery({uri}: {uri: string | undefined}) {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.feed.getLikes.$OutputBody> InfiniteData<app.bsky.feed.getLikes.$OutputBody>
>({ >({
+14 -11
View File
@@ -1,10 +1,4 @@
import { import {type AtUriString, AtUri} from '@atproto/syntax'
type AppBskyActorDefs,
AppBskyEmbedRecord,
type AppBskyFeedDefs,
AtUri,
} from '@atproto/api'
import {type AtUriString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -12,6 +6,7 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import * as bsky from '#/types/bsky'
import {useAppviewClient} from '#/state/session' import {useAppviewClient} from '#/state/session'
import {app} from '#/lexicons' import {app} from '#/lexicons'
import { import {
@@ -54,8 +49,16 @@ export function usePostQuotesQuery(resolvedUri: string | undefined) {
return { return {
...page, ...page,
posts: page.posts.filter(post => { posts: page.posts.filter(post => {
if (post.embed && AppBskyEmbedRecord.isView(post.embed)) { if (
if (AppBskyEmbedRecord.isViewDetached(post.embed.record)) { post.embed &&
bsky.isType(app.bsky.embed.record.view, post.embed)
) {
if (
bsky.isType(
app.bsky.embed.record.viewDetached,
post.embed.record,
)
) {
return false return false
} }
} }
@@ -71,7 +74,7 @@ export function usePostQuotesQuery(resolvedUri: string | undefined) {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileViewBasic, void> { ): Generator<app.bsky.actor.defs.ProfileViewBasic, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.feed.getQuotes.$OutputBody> InfiniteData<app.bsky.feed.getQuotes.$OutputBody>
>({ >({
@@ -98,7 +101,7 @@ export function* findAllProfilesInQueryData(
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, undefined> { ): Generator<app.bsky.feed.defs.PostView, undefined> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.feed.getQuotes.$OutputBody> InfiniteData<app.bsky.feed.getQuotes.$OutputBody>
>({ >({
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type AtUriString} from '@atproto/syntax' import {type AtUriString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -44,7 +43,7 @@ export function usePostRepostedByQuery(resolvedUri: string | undefined) {
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.feed.getRepostedBy.$OutputBody> InfiniteData<app.bsky.feed.getRepostedBy.$OutputBody>
>({ >({
+13 -14
View File
@@ -1,7 +1,6 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {type AppBskyActorDefs, type AppBskyFeedDefs, AtUri} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type AtUriString, type HandleString} from '@atproto/syntax' import {type AtUriString, type HandleString, AtUri} from '@atproto/syntax'
import {deleteLike, deletePost, deleteRepost, like, repost} from '@bsky.app/sdk' import {deleteLike, deletePost, deleteRepost, like, repost} from '@bsky.app/sdk'
import { import {
type QueryClient, type QueryClient,
@@ -26,7 +25,7 @@ export const RQKEY = (postUri: string) => [RQKEY_ROOT, postUri]
export function usePostQuery(uri: string | undefined) { export function usePostQuery(uri: string | undefined) {
const client = useAppviewClient() const client = useAppviewClient()
return useQuery<AppBskyFeedDefs.PostView>({ return useQuery<app.bsky.feed.defs.PostView>({
queryKey: RQKEY(uri || ''), queryKey: RQKEY(uri || ''),
queryFn: async () => { queryFn: async () => {
if (!uri) throw new Error('[unreachable] No URI provided') if (!uri) throw new Error('[unreachable] No URI provided')
@@ -53,7 +52,7 @@ export function usePostQuery(uri: string | undefined) {
async function fetchPost( async function fetchPost(
client: Client, client: Client,
uri: string, uri: string,
): Promise<AppBskyFeedDefs.PostView | undefined> { ): Promise<app.bsky.feed.defs.PostView | undefined> {
const urip = new AtUri(uri) const urip = new AtUri(uri)
if (!urip.host.startsWith('did:')) { if (!urip.host.startsWith('did:')) {
@@ -72,7 +71,7 @@ async function fetchPost(
export function precachePost( export function precachePost(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
post: AppBskyFeedDefs.PostView, post: app.bsky.feed.defs.PostView,
) { ) {
queryClient.setQueryData(RQKEY(uri), post) queryClient.setQueryData(RQKEY(uri), post)
} }
@@ -110,7 +109,7 @@ export function useGetPosts() {
uris: uris as AtUriString[], uris: uris as AtUriString[],
}) })
// See the note on `fetchPost` about the view shapes. // See the note on `fetchPost` about the view shapes.
return data.posts as AppBskyFeedDefs.PostView[] return data.posts as app.bsky.feed.defs.PostView[]
}, },
}) })
}, },
@@ -119,7 +118,7 @@ export function useGetPosts() {
} }
export function usePostLikeMutationQueue( export function usePostLikeMutationQueue(
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
viaRepost: {uri: string; cid: string} | undefined, viaRepost: {uri: string; cid: string} | undefined,
feedDescriptor: string | undefined, feedDescriptor: string | undefined,
logContext: Metrics['post:like']['logContext'], logContext: Metrics['post:like']['logContext'],
@@ -183,7 +182,7 @@ export function usePostLikeMutationQueue(
function usePostLikeMutation( function usePostLikeMutation(
feedDescriptor: string | undefined, feedDescriptor: string | undefined,
logContext: Metrics['post:like']['logContext'], logContext: Metrics['post:like']['logContext'],
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
) { ) {
const {currentAccount} = useSession() const {currentAccount} = useSession()
const queryClient = useQueryClient() const queryClient = useQueryClient()
@@ -196,7 +195,7 @@ function usePostLikeMutation(
{uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present {uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present
>({ >({
mutationFn: ({uri, cid, via}) => { mutationFn: ({uri, cid, via}) => {
let ownProfile: AppBskyActorDefs.ProfileViewDetailed | undefined let ownProfile: app.bsky.actor.defs.ProfileViewDetailed | undefined
if (currentAccount) { if (currentAccount) {
ownProfile = findProfileQueryData(queryClient, currentAccount.did) ownProfile = findProfileQueryData(queryClient, currentAccount.did)
} }
@@ -231,7 +230,7 @@ function usePostLikeMutation(
function usePostUnlikeMutation( function usePostUnlikeMutation(
feedDescriptor: string | undefined, feedDescriptor: string | undefined,
logContext: Metrics['post:unlike']['logContext'], logContext: Metrics['post:unlike']['logContext'],
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
) { ) {
const pdsClient = usePdsClient() const pdsClient = usePdsClient()
const ax = useAnalytics() const ax = useAnalytics()
@@ -249,7 +248,7 @@ function usePostUnlikeMutation(
} }
export function usePostRepostMutationQueue( export function usePostRepostMutationQueue(
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
viaRepost: {uri: string; cid: string} | undefined, viaRepost: {uri: string; cid: string} | undefined,
feedDescriptor: string | undefined, feedDescriptor: string | undefined,
logContext: Metrics['post:repost']['logContext'], logContext: Metrics['post:repost']['logContext'],
@@ -315,7 +314,7 @@ export function usePostRepostMutationQueue(
function usePostRepostMutation( function usePostRepostMutation(
feedDescriptor: string | undefined, feedDescriptor: string | undefined,
logContext: Metrics['post:repost']['logContext'], logContext: Metrics['post:repost']['logContext'],
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
) { ) {
const pdsClient = usePdsClient() const pdsClient = usePdsClient()
const ax = useAnalytics() const ax = useAnalytics()
@@ -343,7 +342,7 @@ function usePostRepostMutation(
function usePostUnrepostMutation( function usePostUnrepostMutation(
feedDescriptor: string | undefined, feedDescriptor: string | undefined,
logContext: Metrics['post:unrepost']['logContext'], logContext: Metrics['post:unrepost']['logContext'],
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
) { ) {
const pdsClient = usePdsClient() const pdsClient = usePdsClient()
const ax = useAnalytics() const ax = useAnalytics()
@@ -374,7 +373,7 @@ export function usePostDeleteMutation() {
} }
export function useThreadMuteMutationQueue( export function useThreadMuteMutationQueue(
post: Shadow<AppBskyFeedDefs.PostView>, post: Shadow<app.bsky.feed.defs.PostView>,
rootUri: string, rootUri: string,
) { ) {
const threadMuteMutation = useThreadMuteMutation() const threadMuteMutation = useThreadMuteMutation()
+4 -9
View File
@@ -1,9 +1,4 @@
import {useRef} from 'react' import {useRef} from 'react'
import {
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {AtUri, type HandleString} from '@atproto/syntax' import {AtUri, type HandleString} from '@atproto/syntax'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
@@ -173,7 +168,7 @@ export function useToggleQuoteDetachmentMutation() {
const pdsClient = usePdsClient() const pdsClient = usePdsClient()
const queryClient = useQueryClient() const queryClient = useQueryClient()
const getPosts = useGetPosts() const getPosts = useGetPosts()
const prevEmbed = useRef<AppBskyFeedDefs.PostView['embed']>(undefined) const prevEmbed = useRef<app.bsky.feed.defs.PostView['embed']>(undefined)
return useMutation({ return useMutation({
mutationFn: async ({ mutationFn: async ({
@@ -181,7 +176,7 @@ export function useToggleQuoteDetachmentMutation() {
quoteUri, quoteUri,
action, action,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
quoteUri: string quoteUri: string
action: 'detach' | 'reattach' action: 'detach' | 'reattach'
}) => { }) => {
@@ -247,8 +242,8 @@ export function useToggleQuoteDetachmentMutation() {
if (action === 'detach' && prevEmbed.current) { if (action === 'detach' && prevEmbed.current) {
// detach failed, add the embed back // detach failed, add the embed back
if ( if (
AppBskyEmbedRecord.isView(prevEmbed.current) || bsky.isType(app.bsky.embed.record.view, prevEmbed.current) ||
AppBskyEmbedRecordWithMedia.isView(prevEmbed.current) bsky.isType(app.bsky.embed.recordWithMedia.view, prevEmbed.current)
) { ) {
updatePostShadow(queryClient, post.uri, { updatePostShadow(queryClient, post.uri, {
embed: prevEmbed.current, embed: prevEmbed.current,
+34 -32
View File
@@ -1,13 +1,8 @@
import { import {type AtUriString, toDatetimeString, AtUri} from '@atproto/syntax'
type $Typed,
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
AtUri,
} from '@atproto/api'
import {type AtUriString, toDatetimeString} from '@atproto/syntax'
import {type app} from '#/lexicons' import {type $Typed} from '@atproto/lex'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
export const POSTGATE_COLLECTION = 'app.bsky.feed.postgate' export const POSTGATE_COLLECTION = 'app.bsky.feed.postgate'
@@ -64,8 +59,8 @@ export function createEmbedViewDetachedRecord({
uri, uri,
}: { }: {
uri: string uri: string
}): $Typed<AppBskyEmbedRecord.View> { }): $Typed<app.bsky.embed.record.View> {
const record: $Typed<AppBskyEmbedRecord.ViewDetached> = { const record: $Typed<app.bsky.embed.record.ViewDetached> = {
$type: 'app.bsky.embed.record#viewDetached', $type: 'app.bsky.embed.record#viewDetached',
uri, uri,
detached: true, detached: true,
@@ -83,24 +78,27 @@ export function createMaybeDetachedQuoteEmbed({
detached, detached,
}: }:
| { | {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
quote: AppBskyFeedDefs.PostView quote: app.bsky.feed.defs.PostView
quoteUri: undefined quoteUri: undefined
detached: false detached: false
} }
| { | {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
quote: undefined quote: undefined
quoteUri: string quoteUri: string
detached: true detached: true
}): AppBskyEmbedRecord.View | AppBskyEmbedRecordWithMedia.View | undefined { }):
if (AppBskyEmbedRecord.isView(post.embed)) { | app.bsky.embed.record.View
| app.bsky.embed.recordWithMedia.View
| undefined {
if (bsky.isType(app.bsky.embed.record.view, post.embed)) {
if (detached) { if (detached) {
return createEmbedViewDetachedRecord({uri: quoteUri}) return createEmbedViewDetachedRecord({uri: quoteUri})
} else { } else {
return createEmbedRecordView({post: quote}) return createEmbedRecordView({post: quote})
} }
} else if (AppBskyEmbedRecordWithMedia.isView(post.embed)) { } else if (bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed)) {
if (detached) { if (detached) {
return { return {
...post.embed, ...post.embed,
@@ -113,8 +111,8 @@ export function createMaybeDetachedQuoteEmbed({
} }
export function createEmbedViewRecordFromPost( export function createEmbedViewRecordFromPost(
post: AppBskyFeedDefs.PostView, post: app.bsky.feed.defs.PostView,
): $Typed<AppBskyEmbedRecord.ViewRecord> { ): $Typed<app.bsky.embed.record.ViewRecord> {
return { return {
$type: 'app.bsky.embed.record#viewRecord', $type: 'app.bsky.embed.record#viewRecord',
uri: post.uri, uri: post.uri,
@@ -134,8 +132,8 @@ export function createEmbedViewRecordFromPost(
export function createEmbedRecordView({ export function createEmbedRecordView({
post, post,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
}): AppBskyEmbedRecord.View { }): app.bsky.embed.record.View {
return { return {
$type: 'app.bsky.embed.record#view', $type: 'app.bsky.embed.record#view',
record: createEmbedViewRecordFromPost(post), record: createEmbedViewRecordFromPost(post),
@@ -146,10 +144,10 @@ export function createEmbedRecordWithMediaView({
post, post,
quote, quote,
}: { }: {
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
quote: AppBskyFeedDefs.PostView quote: app.bsky.feed.defs.PostView
}): AppBskyEmbedRecordWithMedia.View | undefined { }): app.bsky.embed.recordWithMedia.View | undefined {
if (!AppBskyEmbedRecordWithMedia.isView(post.embed)) return if (!bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed)) return
return { return {
...(post.embed || {}), ...(post.embed || {}),
record: { record: {
@@ -163,11 +161,11 @@ export function getMaybeDetachedQuoteEmbed({
post, post,
}: { }: {
viewerDid: string viewerDid: string
post: AppBskyFeedDefs.PostView post: app.bsky.feed.defs.PostView
}) { }) {
if (AppBskyEmbedRecord.isView(post.embed)) { if (bsky.isType(app.bsky.embed.record.view, post.embed)) {
// detached // detached
if (AppBskyEmbedRecord.isViewDetached(post.embed.record)) { if (bsky.isType(app.bsky.embed.record.viewDetached, post.embed.record)) {
const urip = new AtUri(post.embed.record.uri) const urip = new AtUri(post.embed.record.uri)
return { return {
embed: post.embed, embed: post.embed,
@@ -178,7 +176,7 @@ export function getMaybeDetachedQuoteEmbed({
} }
// post // post
if (AppBskyEmbedRecord.isViewRecord(post.embed.record)) { if (bsky.isType(app.bsky.embed.record.viewRecord, post.embed.record)) {
const urip = new AtUri(post.embed.record.uri) const urip = new AtUri(post.embed.record.uri)
return { return {
embed: post.embed, embed: post.embed,
@@ -187,9 +185,11 @@ export function getMaybeDetachedQuoteEmbed({
isDetached: false, isDetached: false,
} }
} }
} else if (AppBskyEmbedRecordWithMedia.isView(post.embed)) { } else if (bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed)) {
// detached // detached
if (AppBskyEmbedRecord.isViewDetached(post.embed.record.record)) { if (
bsky.isType(app.bsky.embed.record.viewDetached, post.embed.record.record)
) {
const urip = new AtUri(post.embed.record.record.uri) const urip = new AtUri(post.embed.record.record.uri)
return { return {
embed: post.embed, embed: post.embed,
@@ -200,7 +200,9 @@ export function getMaybeDetachedQuoteEmbed({
} }
// post // post
if (AppBskyEmbedRecord.isViewRecord(post.embed.record.record)) { if (
bsky.isType(app.bsky.embed.record.viewRecord, post.embed.record.record)
) {
const urip = new AtUri(post.embed.record.record.uri) const urip = new AtUri(post.embed.record.record.uri)
return { return {
embed: post.embed, embed: post.embed,
+8 -9
View File
@@ -1,5 +1,4 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
addSavedFeeds, addSavedFeeds,
@@ -278,7 +277,7 @@ export function useOverwriteSavedFeedsMutation() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const client = usePdsClient() const client = usePdsClient()
return useMutation<void, unknown, AppBskyActorDefs.SavedFeed[]>({ return useMutation<void, unknown, app.bsky.actor.defs.SavedFeed[]>({
mutationFn: async savedFeeds => { mutationFn: async savedFeeds => {
await client.call(overwriteSavedFeeds, savedFeeds) await client.call(overwriteSavedFeeds, savedFeeds)
// triggers a refetch // triggers a refetch
@@ -296,7 +295,7 @@ export function useAddSavedFeedsMutation() {
return useMutation< return useMutation<
void, void,
unknown, unknown,
Pick<AppBskyActorDefs.SavedFeed, 'type' | 'value' | 'pinned'>[] Pick<app.bsky.actor.defs.SavedFeed, 'type' | 'value' | 'pinned'>[]
>({ >({
mutationFn: async savedFeeds => { mutationFn: async savedFeeds => {
await client.call(addSavedFeeds, savedFeeds) await client.call(addSavedFeeds, savedFeeds)
@@ -312,7 +311,7 @@ export function useRemoveFeedMutation() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const client = usePdsClient() const client = usePdsClient()
return useMutation<void, unknown, Pick<AppBskyActorDefs.SavedFeed, 'id'>>({ return useMutation<void, unknown, Pick<app.bsky.actor.defs.SavedFeed, 'id'>>({
mutationFn: async savedFeed => { mutationFn: async savedFeed => {
await client.call(removeSavedFeeds, [savedFeed.id]) await client.call(removeSavedFeeds, [savedFeed.id])
// triggers a refetch // triggers a refetch
@@ -332,8 +331,8 @@ export function useReplaceForYouWithDiscoverFeedMutation() {
forYouFeedConfig, forYouFeedConfig,
discoverFeedConfig, discoverFeedConfig,
}: { }: {
forYouFeedConfig: AppBskyActorDefs.SavedFeed | undefined forYouFeedConfig: app.bsky.actor.defs.SavedFeed | undefined
discoverFeedConfig: AppBskyActorDefs.SavedFeed | undefined discoverFeedConfig: app.bsky.actor.defs.SavedFeed | undefined
}) => { }) => {
if (forYouFeedConfig) { if (forYouFeedConfig) {
await client.call(removeSavedFeeds, [forYouFeedConfig.id]) await client.call(removeSavedFeeds, [forYouFeedConfig.id])
@@ -366,7 +365,7 @@ export function useUpdateSavedFeedsMutation() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const client = usePdsClient() const client = usePdsClient()
return useMutation<void, unknown, AppBskyActorDefs.SavedFeed[]>({ return useMutation<void, unknown, app.bsky.actor.defs.SavedFeed[]>({
mutationFn: async feeds => { mutationFn: async feeds => {
await client.call(updateSavedFeeds, feeds) await client.call(updateSavedFeeds, feeds)
@@ -477,7 +476,7 @@ export function useSetActiveProgressGuideMutation() {
return useMutation({ return useMutation({
mutationFn: async ( mutationFn: async (
guide: AppBskyActorDefs.BskyAppProgressGuide | undefined, guide: app.bsky.actor.defs.BskyAppProgressGuide | undefined,
) => { ) => {
await client.call(setActiveProgressGuide, guide) await client.call(setActiveProgressGuide, guide)
// triggers a refetch // triggers a refetch
@@ -508,7 +507,7 @@ export function useSetVerificationPrefsMutation() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const client = usePdsClient() const client = usePdsClient()
return useMutation<void, unknown, AppBskyActorDefs.VerificationPrefs>({ return useMutation<void, unknown, app.bsky.actor.defs.VerificationPrefs>({
mutationFn: async prefs => { mutationFn: async prefs => {
await client.call(setVerificationPrefs, prefs) await client.call(setVerificationPrefs, prefs)
if (prefs.hideBadges) { if (prefs.hideBadges) {
@@ -1,8 +1,8 @@
import {useCallback, useMemo, useRef, useState} from 'react' import {useCallback, useMemo, useRef, useState} from 'react'
import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api'
import {useFocusEffect} from '@react-navigation/native' import {useFocusEffect} from '@react-navigation/native'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import {app} from '#/lexicons'
import {useCallOnce} from '#/lib/once' import {useCallOnce} from '#/lib/once'
import { import {
usePreferencesQuery, usePreferencesQuery,
@@ -13,7 +13,7 @@ import {useAnalytics} from '#/analytics'
import {type Literal} from '#/types/utils' import {type Literal} from '#/types/utils'
export type ThreadSortOption = Literal< export type ThreadSortOption = Literal<
AppBskyUnspeccedGetPostThreadV2.QueryParams['sort'], app.bsky.unspecced.getPostThreadV2.$Params['sort'],
string string
> >
export type ThreadViewOption = 'linear' | 'tree' export type ThreadViewOption = 'linear' | 'tree'
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -67,7 +66,7 @@ export function useProfileFollowersQuery(
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.graph.getFollowers.$OutputBody> InfiniteData<app.bsky.graph.getFollowers.$OutputBody>
>({ >({
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -72,7 +71,7 @@ export function useProfileFollowsQuery(
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.graph.getFollows.$OutputBody> InfiniteData<app.bsky.graph.getFollows.$OutputBody>
>({ >({
+15 -22
View File
@@ -1,18 +1,11 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import { import {type Client, type Un$Typed} from '@atproto/lex'
type AppBskyActorDefs,
type AppBskyActorGetProfile,
type AppBskyActorGetProfiles,
type AppBskyGraphGetFollows,
AtUri,
type Un$Typed,
} from '@atproto/api'
import {type Client} from '@atproto/lex'
import { import {
type AtIdentifierString, type AtIdentifierString,
type AtUriString, type AtUriString,
type DidString, type DidString,
toDatetimeString, toDatetimeString,
AtUri,
} from '@atproto/syntax' } from '@atproto/syntax'
import { import {
deleteFollow, deleteFollow,
@@ -82,7 +75,7 @@ export function useProfileQuery({
}) { }) {
const client = useAppviewClient() const client = useAppviewClient()
const {getUnstableProfile} = useUnstableProfileViewCache() const {getUnstableProfile} = useUnstableProfileViewCache()
return useQuery<AppBskyActorDefs.ProfileViewDetailed>({ return useQuery<app.bsky.actor.defs.ProfileViewDetailed>({
// WARNING // WARNING
// this staleTime is load-bearing // this staleTime is load-bearing
// if you remove it, the UI infinite-loops // if you remove it, the UI infinite-loops
@@ -97,7 +90,7 @@ export function useProfileQuery({
}, },
placeholderData: () => { placeholderData: () => {
if (!did) return if (!did) return
return getUnstableProfile(did) as AppBskyActorDefs.ProfileViewDetailed return getUnstableProfile(did) as app.bsky.actor.defs.ProfileViewDetailed
}, },
enabled: !!did, enabled: !!did,
}) })
@@ -145,7 +138,7 @@ export function usePrefetchProfileQuery() {
} }
interface ProfileUpdateParams { interface ProfileUpdateParams {
profile: AppBskyActorDefs.ProfileViewDetailed profile: app.bsky.actor.defs.ProfileViewDetailed
updates: updates:
| Un$Typed<app.bsky.actor.profile.Main> | Un$Typed<app.bsky.actor.profile.Main>
| (( | ((
@@ -153,7 +146,7 @@ interface ProfileUpdateParams {
) => Un$Typed<app.bsky.actor.profile.Main>) ) => Un$Typed<app.bsky.actor.profile.Main>)
newUserAvatar?: ImageMeta | undefined | null newUserAvatar?: ImageMeta | undefined | null
newUserBanner?: ImageMeta | undefined | null newUserBanner?: ImageMeta | undefined | null
checkCommitted?: (res: AppBskyActorGetProfile.Response) => boolean checkCommitted?: (res: app.bsky.actor.getProfile.Response) => boolean
} }
export function useProfileUpdateMutation() { export function useProfileUpdateMutation() {
const queryClient = useQueryClient() const queryClient = useQueryClient()
@@ -303,7 +296,7 @@ export function useProfileFollowMutationQueue(
// Optimistically update profile follows cache for avatar displays // Optimistically update profile follows cache for avatar displays
if (currentAccount?.did) { if (currentAccount?.did) {
type FollowsQueryData = type FollowsQueryData =
InfiniteData<AppBskyGraphGetFollows.OutputSchema> InfiniteData<app.bsky.graph.getFollows.$OutputBody>
queryClient.setQueryData<FollowsQueryData>( queryClient.setQueryData<FollowsQueryData>(
PROFILE_FOLLOWS_RQKEY(currentAccount.did), PROFILE_FOLLOWS_RQKEY(currentAccount.did),
old => { old => {
@@ -320,7 +313,7 @@ export function useProfileFollowMutationQueue(
{ {
...old.pages[0], ...old.pages[0],
follows: [ follows: [
profile as AppBskyActorDefs.ProfileView, profile as app.bsky.actor.defs.ProfileView,
...old.pages[0].follows, ...old.pages[0].follows,
], ],
}, },
@@ -390,7 +383,7 @@ function useProfileFollowMutation(
return useMutation<{uri: string; cid: string}, Error, {did: string}>({ return useMutation<{uri: string; cid: string}, Error, {did: string}>({
mutationFn: async ({did}) => { mutationFn: async ({did}) => {
let ownProfile: AppBskyActorDefs.ProfileViewDetailed | undefined let ownProfile: app.bsky.actor.defs.ProfileViewDetailed | undefined
if (currentAccount) { if (currentAccount) {
ownProfile = findProfileQueryData(queryClient, currentAccount.did) ownProfile = findProfileQueryData(queryClient, currentAccount.did)
} }
@@ -686,7 +679,7 @@ function useProfileUnblockMutation() {
async function whenAppViewReady( async function whenAppViewReady(
client: Client, client: Client,
actor: string, actor: string,
fn: (res: AppBskyActorGetProfile.Response) => boolean, fn: (res: app.bsky.actor.getProfile.Response) => boolean,
) { ) {
await until( await until(
5, // 5 tries 5, // 5 tries
@@ -709,9 +702,9 @@ async function whenAppViewReady(
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileViewDetailed, void> { ): Generator<app.bsky.actor.defs.ProfileViewDetailed, void> {
const profileQueryDatas = const profileQueryDatas =
queryClient.getQueriesData<AppBskyActorDefs.ProfileViewDetailed>({ queryClient.getQueriesData<app.bsky.actor.defs.ProfileViewDetailed>({
queryKey: [RQKEY_ROOT], queryKey: [RQKEY_ROOT],
}) })
for (const [_queryKey, queryData] of profileQueryDatas) { for (const [_queryKey, queryData] of profileQueryDatas) {
@@ -723,7 +716,7 @@ export function* findAllProfilesInQueryData(
} }
} }
const profilesQueryDatas = const profilesQueryDatas =
queryClient.getQueriesData<AppBskyActorGetProfiles.OutputSchema>({ queryClient.getQueriesData<app.bsky.actor.getProfiles.$OutputBody>({
queryKey: [profilesQueryKeyRoot], queryKey: [profilesQueryKeyRoot],
}) })
for (const [_queryKey, queryData] of profilesQueryDatas) { for (const [_queryKey, queryData] of profilesQueryDatas) {
@@ -741,8 +734,8 @@ export function* findAllProfilesInQueryData(
export function findProfileQueryData( export function findProfileQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): AppBskyActorDefs.ProfileViewDetailed | undefined { ): app.bsky.actor.defs.ProfileViewDetailed | undefined {
return queryClient.getQueryData<AppBskyActorDefs.ProfileViewDetailed>( return queryClient.getQueryData<app.bsky.actor.defs.ProfileViewDetailed>(
RQKEY(did), RQKEY(did),
) )
} }
+1 -2
View File
@@ -1,6 +1,5 @@
import {AtUri} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {type HandleString} from '@atproto/syntax' import {type HandleString, AtUri} from '@atproto/syntax'
import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query' import {type QueryClient, queryOptions, useQuery} from '@tanstack/react-query'
import {STALE} from '#/state/queries' import {STALE} from '#/state/queries'
+3 -4
View File
@@ -1,11 +1,10 @@
import {app} from '#/lexicons'
/* /*
* Pure helpers for lifting structured `app.bsky.feed.searchPosts` params out of * Pure helpers for lifting structured `app.bsky.feed.searchPosts` params out of
* a free-text query. Kept free of React Native imports so it can be unit * a free-text query. Kept free of React Native imports so it can be unit
* tested in isolation (the search-posts query hook re-exports these). * tested in isolation (the search-posts query hook re-exports these).
*/ */
import {type AppBskyFeedSearchPostsV2} from '@atproto/api'
import { import {
filtersToApiParams, filtersToApiParams,
type SearchFilters, type SearchFilters,
@@ -204,9 +203,9 @@ function mergeList(a?: string[], b?: string[]): string[] | undefined {
export function buildSearchPostsV2Filters( export function buildSearchPostsV2Filters(
embedded: Omit<ExtractedSearchParams, 'q'>, embedded: Omit<ExtractedSearchParams, 'q'>,
filters?: SearchFilters, filters?: SearchFilters,
): AppBskyFeedSearchPostsV2.QueryParams { ): app.bsky.feed.searchPostsV2.$Params {
const apiFilters = filters ? filtersToApiParams(filters) : {} const apiFilters = filters ? filtersToApiParams(filters) : {}
const params: AppBskyFeedSearchPostsV2.QueryParams = {} const params: app.bsky.feed.searchPostsV2.$Params = {}
const authors = mergeList( const authors = mergeList(
embedded.author ? [embedded.author] : undefined, embedded.author ? [embedded.author] : undefined,
+2 -2
View File
@@ -1,5 +1,4 @@
import {useCallback, useMemo, useRef} from 'react' import {useCallback, useMemo, useRef} from 'react'
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
@@ -7,6 +6,7 @@ import {
useInfiniteQuery, useInfiniteQuery,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {AtUri} from '@atproto/syntax'
import {moderatePost} from '#/lib/moderation/subjects' import {moderatePost} from '#/lib/moderation/subjects'
import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useAppviewClient} from '#/state/session' import {useAppviewClient} from '#/state/session'
@@ -180,7 +180,7 @@ export function useSearchPostsV2Query({
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, undefined> { ): Generator<app.bsky.feed.defs.PostView, undefined> {
const queryDatas = queryClient.getQueriesData< const queryDatas = queryClient.getQueriesData<
InfiniteData<app.bsky.feed.searchPostsV2.$OutputBody> InfiniteData<app.bsky.feed.searchPostsV2.$OutputBody>
>({ >({
+14 -19
View File
@@ -1,8 +1,3 @@
import {
AppBskyFeedDefs,
AppBskyGraphDefs,
AppBskyGraphStarterpack,
} from '@atproto/api'
import {type Client, type LexValue} from '@atproto/lex' import {type Client, type LexValue} from '@atproto/lex'
import {AtUri, type AtUriString, toDatetimeString} from '@atproto/syntax' import {AtUri, type AtUriString, toDatetimeString} from '@atproto/syntax'
import {RichText} from '@bsky.app/sdk/richtext' import {RichText} from '@bsky.app/sdk/richtext'
@@ -57,7 +52,7 @@ export function useStarterPackQuery({
}) { }) {
const client = useAppviewClient() const client = useAppviewClient()
return useQuery<AppBskyGraphDefs.StarterPackView>({ return useQuery<app.bsky.graph.defs.StarterPackView>({
queryKey: RQKEY(uri ? {uri} : {did, rkey}), queryKey: RQKEY(uri ? {uri} : {did, rkey}),
queryFn: async () => { queryFn: async () => {
if (!uri) { if (!uri) {
@@ -92,7 +87,7 @@ interface UseCreateStarterPackMutationParams {
name: string name: string
description?: string description?: string
profiles: bsky.profile.AnyProfileView[] profiles: bsky.profile.AnyProfileView[]
feeds?: AppBskyFeedDefs.GeneratorView[] feeds?: app.bsky.feed.defs.GeneratorView[]
} }
export function useCreateStarterPackMutation({ export function useCreateStarterPackMutation({
@@ -173,8 +168,8 @@ export function useEditStarterPackMutation({
void, void,
Error, Error,
UseCreateStarterPackMutationParams & { UseCreateStarterPackMutationParams & {
currentStarterPack: AppBskyGraphDefs.StarterPackView currentStarterPack: app.bsky.graph.defs.StarterPackView
currentListItems: AppBskyGraphDefs.ListItemView[] currentListItems: app.bsky.graph.defs.ListItemView[]
} }
>({ >({
mutationFn: async ({ mutationFn: async ({
@@ -373,33 +368,33 @@ async function whenAppViewReady(
export function precacheStarterPack( export function precacheStarterPack(
queryClient: QueryClient, queryClient: QueryClient,
starterPack: starterPack:
| AppBskyGraphDefs.StarterPackViewBasic | app.bsky.graph.defs.StarterPackViewBasic
| AppBskyGraphDefs.StarterPackView, | app.bsky.graph.defs.StarterPackView,
) { ) {
if (!AppBskyGraphStarterpack.isRecord(starterPack.record)) { if (!bsky.isType(app.bsky.graph.starterpack, starterPack.record)) {
return return
} }
let starterPackView: AppBskyGraphDefs.StarterPackView | undefined let starterPackView: app.bsky.graph.defs.StarterPackView | undefined
if (AppBskyGraphDefs.isStarterPackView(starterPack)) { if (bsky.isType(app.bsky.graph.defs.starterPackView, starterPack)) {
starterPackView = starterPack starterPackView = starterPack
} else if ( } else if (
AppBskyGraphDefs.isStarterPackViewBasic(starterPack) && bsky.isType(app.bsky.graph.defs.starterPackViewBasic, starterPack) &&
bsky.validate(starterPack.record, AppBskyGraphStarterpack.validateRecord) bsky.matches(app.bsky.graph.starterpack, starterPack.record)
) { ) {
let feeds: AppBskyFeedDefs.GeneratorView[] | undefined let feeds: app.bsky.feed.defs.GeneratorView[] | undefined
if (starterPack.record.feeds) { if (starterPack.record.feeds) {
feeds = [] feeds = []
for (const feed of starterPack.record.feeds) { for (const feed of starterPack.record.feeds) {
// note: types are wrong? claims to be `FeedItem`, but we actually // note: types are wrong? claims to be `FeedItem`, but we actually
// get un$typed `GeneratorView` objects here -sfn // get un$typed `GeneratorView` objects here -sfn
if (bsky.validate(feed, AppBskyFeedDefs.validateGeneratorView)) { if (bsky.matches(app.bsky.feed.defs.generatorView, feed)) {
feeds.push(feed) feeds.push(feed)
} }
} }
} }
const listView: AppBskyGraphDefs.ListViewBasic = { const listView: app.bsky.graph.defs.ListViewBasic = {
uri: starterPack.record.list, uri: starterPack.record.list,
// This will be populated once the data from server is fetched // This will be populated once the data from server is fetched
cid: '', cid: '',
+1 -2
View File
@@ -1,5 +1,4 @@
import {useCallback, useMemo} from 'react' import {useCallback, useMemo} from 'react'
import {type AppBskyActorDefs} from '@atproto/api'
import {type DidString} from '@atproto/syntax' import {type DidString} from '@atproto/syntax'
import { import {
type InfiniteData, type InfiniteData,
@@ -103,7 +102,7 @@ export function useSuggestedFollowsByActorWithDismiss({
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileView, void> { ): Generator<app.bsky.actor.defs.ProfileView, void> {
yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did) yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did)
yield* findAllProfilesInSuggestedFollowsByActorQueryData(queryClient, did) yield* findAllProfilesInSuggestedFollowsByActorQueryData(queryClient, did)
} }
+2 -3
View File
@@ -1,4 +1,3 @@
import {type AppBskyFeedDefs} from '@atproto/api'
import {type Client} from '@atproto/lex' import {type Client} from '@atproto/lex'
import {AtUri, type HandleString} from '@atproto/syntax' import {AtUri, type HandleString} from '@atproto/syntax'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
@@ -67,7 +66,7 @@ export function useThreadgateViewQuery({
initialData, initialData,
}: { }: {
postUri?: string postUri?: string
initialData?: AppBskyFeedDefs.ThreadgateView initialData?: app.bsky.feed.defs.ThreadgateView
} = {}) { } = {}) {
const getPost = useGetPost() const getPost = useGetPost()
@@ -248,7 +247,7 @@ export function useSetThreadgateAllowMutation() {
}) })
}, },
async onSuccess(_, {postUri, allow}) { async onSuccess(_, {postUri, allow}) {
const data = await retry<AppBskyFeedDefs.ThreadgateView | undefined>( const data = await retry<app.bsky.feed.defs.ThreadgateView | undefined>(
5, // 5 tries 5, // 5 tries
_e => true, _e => true,
async () => { async () => {
+1 -2
View File
@@ -1,4 +1,3 @@
import {type AppBskyFeedDefs} from '@atproto/api'
import {type AtUriString, toDatetimeString} from '@atproto/syntax' import {type AtUriString, toDatetimeString} from '@atproto/syntax'
import {type ThreadgateAllowUISetting} from '#/state/queries/threadgate/types' import {type ThreadgateAllowUISetting} from '#/state/queries/threadgate/types'
@@ -12,7 +11,7 @@ import * as bsky from '#/types/bsky'
* through `com.atproto.repo.putRecord`, whose body is typed as a lex `LexMap`. * through `com.atproto.repo.putRecord`, whose body is typed as a lex `LexMap`.
*/ */
export function threadgateViewToAllowUISetting( export function threadgateViewToAllowUISetting(
threadgateView: AppBskyFeedDefs.ThreadgateView | undefined, threadgateView: app.bsky.feed.defs.ThreadgateView | undefined,
): ThreadgateAllowUISetting[] { ): ThreadgateAllowUISetting[] {
// Validate the record for clarity, since backwards compat code is a little confusing // Validate the record for clarity, since backwards compat code is a little confusing
const threadgate = const threadgate =
+6 -6
View File
@@ -1,27 +1,27 @@
import {app} from '#/lexicons'
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api'
/** /**
* See the `below` param on {@link AppBskyUnspeccedGetPostThreadV2.QueryParams} * See the `below` param on {@link app.bsky.unspecced.getPostThreadV2.$Params}
*/ */
export const LINEAR_VIEW_BELOW = 10 export const LINEAR_VIEW_BELOW = 10
/** /**
* See the `branchingFactor` param on {@link AppBskyUnspeccedGetPostThreadV2.QueryParams} * See the `branchingFactor` param on {@link app.bsky.unspecced.getPostThreadV2.$Params}
*/ */
export const LINEAR_VIEW_BF = 1 export const LINEAR_VIEW_BF = 1
/** /**
* See the `below` param on {@link AppBskyUnspeccedGetPostThreadV2.QueryParams} * See the `below` param on {@link app.bsky.unspecced.getPostThreadV2.$Params}
*/ */
export const TREE_VIEW_BELOW = 4 export const TREE_VIEW_BELOW = 4
/** /**
* See the `branchingFactor` param on {@link AppBskyUnspeccedGetPostThreadV2.QueryParams} * See the `branchingFactor` param on {@link app.bsky.unspecced.getPostThreadV2.$Params}
*/ */
export const TREE_VIEW_BF = undefined export const TREE_VIEW_BF = undefined
/** /**
* See the `below` param on {@link AppBskyUnspeccedGetPostThreadV2.QueryParams} * See the `below` param on {@link app.bsky.unspecced.getPostThreadV2.$Params}
*/ */
export const TREE_VIEW_BELOW_DESKTOP = 6 export const TREE_VIEW_BELOW_DESKTOP = 6
+37 -34
View File
@@ -1,19 +1,14 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {
type $Typed,
type AppBskyActorDefs,
type AppBskyFeedDefs,
AppBskyUnspeccedDefs,
type AppBskyUnspeccedGetPostThreadOtherV2,
type AppBskyUnspeccedGetPostThreadV2,
AtUri,
} from '@atproto/api'
import {type QueryClient, useQueryClient} from '@tanstack/react-query' import {type QueryClient, useQueryClient} from '@tanstack/react-query'
import { import {
dangerousGetPostShadow, dangerousGetPostShadow,
updatePostShadow, updatePostShadow,
} from '#/state/cache/post-shadow' } from '#/state/cache/post-shadow'
import {type $Typed} from '@atproto/lex'
import {AtUri} from '@atproto/syntax'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery' import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews' import {findAllPostsInQueryData as findAllPostsInExploreFeedPreviewsQueryData} from '#/state/queries/explore-feed-previews'
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed' import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '#/state/queries/notifications/feed'
@@ -51,18 +46,18 @@ export function createCacheMutator({
return { return {
insertReplies( insertReplies(
parentUri: string, parentUri: string,
replies: AppBskyUnspeccedGetPostThreadV2.ThreadItem[], replies: app.bsky.unspecced.getPostThreadV2.ThreadItem[],
) { ) {
/* /*
* Main thread query mutator. * Main thread query mutator.
*/ */
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>( queryClient.setQueryData<app.bsky.unspecced.getPostThreadV2.$OutputBody>(
postThreadQueryKey, postThreadQueryKey,
data => { data => {
if (!data) return if (!data) return
return { return {
...data, ...data,
thread: mutator<AppBskyUnspeccedGetPostThreadV2.ThreadItem>([ thread: mutator<app.bsky.unspecced.getPostThreadV2.ThreadItem>([
...data.thread, ...data.thread,
]), ]),
} }
@@ -72,15 +67,15 @@ export function createCacheMutator({
/* /*
* Additional replies query mutator. * Additional replies query mutator.
*/ */
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadOtherV2.OutputSchema>( queryClient.setQueryData<app.bsky.unspecced.getPostThreadOtherV2.$OutputBody>(
postThreadOtherQueryKey, postThreadOtherQueryKey,
data => { data => {
if (!data) return if (!data) return
return { return {
...data, ...data,
thread: mutator<AppBskyUnspeccedGetPostThreadOtherV2.ThreadItem>([ thread: mutator<app.bsky.unspecced.getPostThreadOtherV2.ThreadItem>(
...data.thread, [...data.thread],
]), ),
} }
}, },
) )
@@ -89,7 +84,10 @@ export function createCacheMutator({
for (let i = 0; i < thread.length; i++) { for (let i = 0; i < thread.length; i++) {
const parent = thread[i] const parent = thread[i]
if (!AppBskyUnspeccedDefs.isThreadItemPost(parent.value)) continue if (
!bsky.isType(app.bsky.unspecced.defs.threadItemPost, parent.value)
)
continue
if (parent.uri !== parentUri) continue if (parent.uri !== parentUri) continue
/* /*
@@ -124,7 +122,8 @@ export function createCacheMutator({
const isParentRoot = parent.depth === 0 const isParentRoot = parent.depth === 0
const isParentBelowRoot = parent.depth > 0 const isParentBelowRoot = parent.depth > 0
const optimisticReply = replies.at(0) const optimisticReply = replies.at(0)
const opIsReplier = AppBskyUnspeccedDefs.isThreadItemPost( const opIsReplier = bsky.isType(
app.bsky.unspecced.defs.threadItemPost,
optimisticReply?.value, optimisticReply?.value,
) )
? opDid === optimisticReply.value.post.author.did ? opDid === optimisticReply.value.post.author.did
@@ -172,8 +171,8 @@ export function createCacheMutator({
* Unused atm, post shadow does the trick, but it would be nice to clean up * Unused atm, post shadow does the trick, but it would be nice to clean up
* the whole sub-tree on deletes. * the whole sub-tree on deletes.
*/ */
deletePost(post: AppBskyUnspeccedGetPostThreadV2.ThreadItem) { deletePost(post: app.bsky.unspecced.getPostThreadV2.ThreadItem) {
queryClient.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>( queryClient.setQueryData<app.bsky.unspecced.getPostThreadV2.$OutputBody>(
postThreadQueryKey, postThreadQueryKey,
queryData => { queryData => {
if (!queryData) return if (!queryData) return
@@ -182,7 +181,10 @@ export function createCacheMutator({
for (let i = 0; i < thread.length; i++) { for (let i = 0; i < thread.length; i++) {
const existingPost = thread[i] const existingPost = thread[i]
if (!AppBskyUnspeccedDefs.isThreadItemPost(post.value)) continue if (
!bsky.isType(app.bsky.unspecced.defs.threadItemPost, post.value)
)
continue
if (existingPost.uri === post.uri) { if (existingPost.uri === post.uri) {
const branch = getBranch(thread, i, existingPost.depth) const branch = getBranch(thread, i, existingPost.depth)
@@ -204,7 +206,7 @@ export function createCacheMutator({
export function getThreadPlaceholder( export function getThreadPlaceholder(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): $Typed<AppBskyUnspeccedGetPostThreadV2.ThreadItem> | void { ): $Typed<app.bsky.unspecced.getPostThreadV2.ThreadItem> | void {
let partial let partial
for (let item of getThreadPlaceholderCandidates(queryClient, uri)) { for (let item of getThreadPlaceholderCandidates(queryClient, uri)) {
/* /*
@@ -231,8 +233,8 @@ export function* getThreadPlaceholderCandidates(
uri: string, uri: string,
): Generator< ): Generator<
$Typed< $Typed<
Omit<AppBskyUnspeccedGetPostThreadV2.ThreadItem, 'value'> & { Omit<app.bsky.unspecced.getPostThreadV2.ThreadItem, 'value'> & {
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost> value: $Typed<app.bsky.unspecced.defs.ThreadItemPost>
} }
>, >,
void void
@@ -276,10 +278,10 @@ export function* getThreadPlaceholderCandidates(
export function* findAllPostsInQueryData( export function* findAllPostsInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
uri: string, uri: string,
): Generator<AppBskyFeedDefs.PostView, void> { ): Generator<app.bsky.feed.defs.PostView, void> {
const atUri = new AtUri(uri) const atUri = new AtUri(uri)
const queryDatas = const queryDatas =
queryClient.getQueriesData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>({ queryClient.getQueriesData<app.bsky.unspecced.getPostThreadV2.$OutputBody>({
queryKey: [postThreadQueryKeyRoot], queryKey: [postThreadQueryKeyRoot],
}) })
@@ -289,7 +291,7 @@ export function* findAllPostsInQueryData(
const {thread} = queryData const {thread} = queryData
for (const item of thread) { for (const item of thread) {
if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { if (bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value)) {
if (didOrHandleUriMatches(atUri, item.value.post)) { if (didOrHandleUriMatches(atUri, item.value.post)) {
yield item.value.post yield item.value.post
} }
@@ -306,9 +308,9 @@ export function* findAllPostsInQueryData(
export function* findAllProfilesInQueryData( export function* findAllProfilesInQueryData(
queryClient: QueryClient, queryClient: QueryClient,
did: string, did: string,
): Generator<AppBskyActorDefs.ProfileViewBasic, void> { ): Generator<app.bsky.actor.defs.ProfileViewBasic, void> {
const queryDatas = const queryDatas =
queryClient.getQueriesData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>({ queryClient.getQueriesData<app.bsky.unspecced.getPostThreadV2.$OutputBody>({
queryKey: [postThreadQueryKeyRoot], queryKey: [postThreadQueryKeyRoot],
}) })
@@ -318,7 +320,7 @@ export function* findAllProfilesInQueryData(
const {thread} = queryData const {thread} = queryData
for (const item of thread) { for (const item of thread) {
if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { if (bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value)) {
if (item.value.post.author.did === did) { if (item.value.post.author.did === did) {
yield item.value.post.author yield item.value.post.author
} }
@@ -337,14 +339,15 @@ export function useUpdatePostThreadThreadgateQueryCache() {
const context = usePostThreadContext() const context = usePostThreadContext()
return useCallback( return useCallback(
(threadgate: AppBskyFeedDefs.ThreadgateView) => { (threadgate: app.bsky.feed.defs.ThreadgateView) => {
if (!context) return if (!context) return
function mutator<T>(thread: ApiThreadItem[]): T[] { function mutator<T>(thread: ApiThreadItem[]): T[] {
for (let i = 0; i < thread.length; i++) { for (let i = 0; i < thread.length; i++) {
const item = thread[i] const item = thread[i]
if (!AppBskyUnspeccedDefs.isThreadItemPost(item.value)) continue if (!bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value))
continue
if (item.depth === 0) { if (item.depth === 0) {
thread.splice(i, 1, { thread.splice(i, 1, {
@@ -363,13 +366,13 @@ export function useUpdatePostThreadThreadgateQueryCache() {
return thread as T[] return thread as T[]
} }
qc.setQueryData<AppBskyUnspeccedGetPostThreadV2.OutputSchema>( qc.setQueryData<app.bsky.unspecced.getPostThreadV2.$OutputBody>(
context.postThreadQueryKey, context.postThreadQueryKey,
data => { data => {
if (!data) return if (!data) return
return { return {
...data, ...data,
thread: mutator<AppBskyUnspeccedGetPostThreadV2.ThreadItem>([ thread: mutator<app.bsky.unspecced.getPostThreadV2.ThreadItem>([
...data.thread, ...data.thread,
]), ]),
} }
+46 -15
View File
@@ -1,4 +1,3 @@
import {AppBskyUnspeccedDefs} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import { import {
@@ -14,6 +13,8 @@ import {
getTraversalMetadata, getTraversalMetadata,
storeTraversalMetadata, storeTraversalMetadata,
} from '#/state/queries/usePostThread/utils' } from '#/state/queries/usePostThread/utils'
import * as bsky from '#/types/bsky'
import {app} from '#/lexicons'
import * as views from '#/state/queries/usePostThread/views' import * as views from '#/state/queries/usePostThread/views'
export function sortAndAnnotateThreadItems( export function sortAndAnnotateThreadItems(
@@ -46,7 +47,7 @@ export function sortAndAnnotateThreadItems(
let parentMetadata: TraversalMetadata | undefined let parentMetadata: TraversalMetadata | undefined
let metadata: TraversalMetadata | undefined let metadata: TraversalMetadata | undefined
if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { if (bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value)) {
parentMetadata = metadatas.get( parentMetadata = metadatas.get(
getPostRecord(item.value.post).reply?.parent?.uri || '', getPostRecord(item.value.post).reply?.parent?.uri || '',
) )
@@ -65,13 +66,24 @@ export function sortAndAnnotateThreadItems(
* _up_ from there. * _up_ from there.
*/ */
} else if (item.depth === 0) { } else if (item.depth === 0) {
if (AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(item.value)) { if (
bsky.isType(
app.bsky.unspecced.defs.threadItemNoUnauthenticated,
item.value,
)
) {
threadItems.push(views.threadPostNoUnauthenticated(item)) threadItems.push(views.threadPostNoUnauthenticated(item))
} else if (AppBskyUnspeccedDefs.isThreadItemNotFound(item.value)) { } else if (
bsky.isType(app.bsky.unspecced.defs.threadItemNotFound, item.value)
) {
threadItems.push(views.threadPostNotFound(item)) threadItems.push(views.threadPostNotFound(item))
} else if (AppBskyUnspeccedDefs.isThreadItemBlocked(item.value)) { } else if (
bsky.isType(app.bsky.unspecced.defs.threadItemBlocked, item.value)
) {
threadItems.push(views.threadPostBlocked(item)) threadItems.push(views.threadPostBlocked(item))
} else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { } else if (
bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value)
) {
const post = views.threadPost({ const post = views.threadPost({
uri: item.uri, uri: item.uri,
depth: item.depth, depth: item.depth,
@@ -85,7 +97,10 @@ export function sortAndAnnotateThreadItems(
const parent = thread[pi] const parent = thread[pi]
if ( if (
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(parent.value) bsky.isType(
app.bsky.unspecced.defs.threadItemNoUnauthenticated,
parent.value,
)
) { ) {
const post = views.threadPostNoUnauthenticated(parent) const post = views.threadPostNoUnauthenticated(parent)
post.ui = getThreadPostNoUnauthenticatedUI({ post.ui = getThreadPostNoUnauthenticatedUI({
@@ -97,13 +112,22 @@ export function sortAndAnnotateThreadItems(
threadItems.unshift(post) threadItems.unshift(post)
// for now, break parent traversal at first no-unauthed // for now, break parent traversal at first no-unauthed
break parentTraversal break parentTraversal
} else if (AppBskyUnspeccedDefs.isThreadItemNotFound(parent.value)) { } else if (
bsky.isType(
app.bsky.unspecced.defs.threadItemNotFound,
parent.value,
)
) {
threadItems.unshift(views.threadPostNotFound(parent)) threadItems.unshift(views.threadPostNotFound(parent))
break parentTraversal break parentTraversal
} else if (AppBskyUnspeccedDefs.isThreadItemBlocked(parent.value)) { } else if (
bsky.isType(app.bsky.unspecced.defs.threadItemBlocked, parent.value)
) {
threadItems.unshift(views.threadPostBlocked(parent)) threadItems.unshift(views.threadPostBlocked(parent))
break parentTraversal break parentTraversal
} else if (AppBskyUnspeccedDefs.isThreadItemPost(parent.value)) { } else if (
bsky.isType(app.bsky.unspecced.defs.threadItemPost, parent.value)
) {
threadItems.unshift( threadItems.unshift(
views.threadPost({ views.threadPost({
uri: parent.uri, uri: parent.uri,
@@ -123,16 +147,21 @@ export function sortAndAnnotateThreadItems(
* we could. * we could.
*/ */
const shouldBreak = const shouldBreak =
AppBskyUnspeccedDefs.isThreadItemNoUnauthenticated(item.value) || bsky.isType(
AppBskyUnspeccedDefs.isThreadItemNotFound(item.value) || app.bsky.unspecced.defs.threadItemNoUnauthenticated,
AppBskyUnspeccedDefs.isThreadItemBlocked(item.value) item.value,
) ||
bsky.isType(app.bsky.unspecced.defs.threadItemNotFound, item.value) ||
bsky.isType(app.bsky.unspecced.defs.threadItemBlocked, item.value)
if (shouldBreak) { if (shouldBreak) {
const branch = getBranch(thread, i, item.depth) const branch = getBranch(thread, i, item.depth)
// could insert tombstone // could insert tombstone
i = branch.end i = branch.end
continue traversal continue traversal
} else if (AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { } else if (
bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value)
) {
if (parentMetadata) { if (parentMetadata) {
/* /*
* Set this value before incrementing the `repliesSeenCounter` later * Set this value before incrementing the `repliesSeenCounter` later
@@ -180,7 +209,9 @@ export function sortAndAnnotateThreadItems(
for (let ci = startIndex; ci <= branch.end; ci++) { for (let ci = startIndex; ci <= branch.end; ci++) {
const child = thread[ci] const child = thread[ci]
if (AppBskyUnspeccedDefs.isThreadItemPost(child.value)) { if (
bsky.isType(app.bsky.unspecced.defs.threadItemPost, child.value)
) {
const childParentMetadata = metadatas.get( const childParentMetadata = metadatas.get(
getPostRecord(child.value.post).reply?.parent?.uri || '', getPostRecord(child.value.post).reply?.parent?.uri || '',
) )
+14 -21
View File
@@ -1,16 +1,9 @@
import {
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyFeedThreadgate,
type AppBskyUnspeccedDefs,
type AppBskyUnspeccedGetPostThreadOtherV2,
type AppBskyUnspeccedGetPostThreadV2,
} from '@atproto/api'
import {type ModerationDecision} from '@bsky.app/sdk/moderation' import {type ModerationDecision} from '@bsky.app/sdk/moderation'
import {app} from '#/lexicons'
export type ApiThreadItem = export type ApiThreadItem =
| AppBskyUnspeccedGetPostThreadV2.ThreadItem | app.bsky.unspecced.getPostThreadV2.ThreadItem
| AppBskyUnspeccedGetPostThreadOtherV2.ThreadItem | app.bsky.unspecced.getPostThreadOtherV2.ThreadItem
export const postThreadQueryKeyRoot = 'post-thread-v2' as const export const postThreadQueryKeyRoot = 'post-thread-v2' as const
@@ -18,13 +11,13 @@ export const createPostThreadQueryKey = (props: PostThreadParams) =>
[postThreadQueryKeyRoot, props] as const [postThreadQueryKeyRoot, props] as const
export const createPostThreadOtherQueryKey = ( export const createPostThreadOtherQueryKey = (
props: Omit<AppBskyUnspeccedGetPostThreadOtherV2.QueryParams, 'anchor'> & { props: Omit<app.bsky.unspecced.getPostThreadOtherV2.$Params, 'anchor'> & {
anchor?: string anchor?: string
}, },
) => [postThreadQueryKeyRoot, 'other', props] as const ) => [postThreadQueryKeyRoot, 'other', props] as const
export type PostThreadParams = Pick< export type PostThreadParams = Pick<
AppBskyUnspeccedGetPostThreadV2.QueryParams, app.bsky.unspecced.getPostThreadV2.$Params,
'sort' 'sort'
> & { > & {
anchor?: string anchor?: string
@@ -33,9 +26,9 @@ export type PostThreadParams = Pick<
export type UsePostThreadQueryResult = { export type UsePostThreadQueryResult = {
hasOtherReplies: boolean hasOtherReplies: boolean
thread: AppBskyUnspeccedGetPostThreadV2.ThreadItem[] thread: app.bsky.unspecced.getPostThreadV2.ThreadItem[]
threadgate?: Omit<AppBskyFeedDefs.ThreadgateView, 'record'> & { threadgate?: Omit<app.bsky.feed.defs.ThreadgateView, 'record'> & {
record: AppBskyFeedThreadgate.Record record: app.bsky.feed.threadgate.Main
} }
} }
@@ -45,9 +38,9 @@ export type ThreadItem =
key: string key: string
uri: string uri: string
depth: number depth: number
value: Omit<AppBskyUnspeccedDefs.ThreadItemPost, 'post'> & { value: Omit<app.bsky.unspecced.defs.ThreadItemPost, 'post'> & {
post: Omit<AppBskyFeedDefs.PostView, 'record'> & { post: Omit<app.bsky.feed.defs.PostView, 'record'> & {
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
} }
} }
isBlurred: boolean isBlurred: boolean
@@ -67,7 +60,7 @@ export type ThreadItem =
key: string key: string
uri: string uri: string
depth: number depth: number
value: AppBskyUnspeccedDefs.ThreadItemNoUnauthenticated value: app.bsky.unspecced.defs.ThreadItemNoUnauthenticated
ui: { ui: {
showParentReplyLine: boolean showParentReplyLine: boolean
showChildReplyLine: boolean showChildReplyLine: boolean
@@ -78,14 +71,14 @@ export type ThreadItem =
key: string key: string
uri: string uri: string
depth: number depth: number
value: AppBskyUnspeccedDefs.ThreadItemNotFound value: app.bsky.unspecced.defs.ThreadItemNotFound
} }
| { | {
type: 'threadPostBlocked' type: 'threadPostBlocked'
key: string key: string
uri: string uri: string
depth: number depth: number
value: AppBskyUnspeccedDefs.ThreadItemBlocked value: app.bsky.unspecced.defs.ThreadItemBlocked
} }
| { | {
type: 'replyComposer' type: 'replyComposer'
+9 -24
View File
@@ -1,38 +1,23 @@
import {
type AppBskyFeedDefs,
AppBskyFeedPost,
AppBskyFeedThreadgate,
AppBskyUnspeccedDefs,
type AppBskyUnspeccedGetPostThreadV2,
AtUri,
} from '@atproto/api'
import { import {
type ApiThreadItem, type ApiThreadItem,
type ThreadItem, type ThreadItem,
type TraversalMetadata, type TraversalMetadata,
} from '#/state/queries/usePostThread/types' } from '#/state/queries/usePostThread/types'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {isDevMode} from '#/storage/hooks/dev-mode' import {isDevMode} from '#/storage/hooks/dev-mode'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export function getThreadgateRecord( export function getThreadgateRecord(
view: AppBskyUnspeccedGetPostThreadV2.OutputSchema['threadgate'], view: app.bsky.unspecced.getPostThreadV2.$OutputBody['threadgate'],
) { ) {
return bsky.dangerousIsType<AppBskyFeedThreadgate.Record>( return bsky.isType(app.bsky.feed.threadgate, view?.record)
view?.record,
AppBskyFeedThreadgate.isRecord,
)
? view?.record ? view?.record
: undefined : undefined
} }
export function getRootPostAtUri(post: AppBskyFeedDefs.PostView) { export function getRootPostAtUri(post: app.bsky.feed.defs.PostView) {
if ( if (bsky.isType(app.bsky.feed.post, post.record)) {
bsky.dangerousIsType<AppBskyFeedPost.Record>(
post.record,
AppBskyFeedPost.isRecord,
)
) {
/** /**
* If the record has no `reply` field, it is a root post. * If the record has no `reply` field, it is a root post.
*/ */
@@ -45,8 +30,8 @@ export function getRootPostAtUri(post: AppBskyFeedDefs.PostView) {
} }
} }
export function getPostRecord(post: AppBskyFeedDefs.PostView) { export function getPostRecord(post: app.bsky.feed.defs.PostView) {
return post.record as AppBskyFeedPost.Record return post.record as app.bsky.feed.post.Main
} }
export function getTraversalMetadata({ export function getTraversalMetadata({
@@ -60,7 +45,7 @@ export function getTraversalMetadata({
nextItem?: ApiThreadItem nextItem?: ApiThreadItem
parentMetadata?: TraversalMetadata parentMetadata?: TraversalMetadata
}): TraversalMetadata { }): TraversalMetadata {
if (!AppBskyUnspeccedDefs.isThreadItemPost(item.value)) { if (!bsky.isType(app.bsky.unspecced.defs.threadItemPost, item.value)) {
throw new Error(`Expected thread item to be a post`) throw new Error(`Expected thread item to be a post`)
} }
const repliesCount = item.value.post.replyCount || 0 const repliesCount = item.value.post.replyCount || 0
+12 -17
View File
@@ -1,13 +1,8 @@
import {
type $Typed,
type AppBskyFeedDefs,
type AppBskyFeedPost,
type AppBskyUnspeccedDefs,
type AppBskyUnspeccedGetPostThreadV2,
AtUri,
} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation' import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {type $Typed} from '@atproto/lex'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {moderatePost} from '#/lib/moderation/subjects' import {moderatePost} from '#/lib/moderation/subjects'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import { import {
@@ -26,7 +21,7 @@ export function threadPostNoUnauthenticated({
key: uri, key: uri,
uri, uri,
depth, depth,
value: value as AppBskyUnspeccedDefs.ThreadItemNoUnauthenticated, value: value as app.bsky.unspecced.defs.ThreadItemNoUnauthenticated,
// @ts-ignore populated by the traversal // @ts-ignore populated by the traversal
ui: {}, ui: {},
} }
@@ -42,7 +37,7 @@ export function threadPostNotFound({
key: uri, key: uri,
uri, uri,
depth, depth,
value: value as AppBskyUnspeccedDefs.ThreadItemNotFound, value: value as app.bsky.unspecced.defs.ThreadItemNotFound,
} }
} }
@@ -56,7 +51,7 @@ export function threadPostBlocked({
key: uri, key: uri,
uri, uri,
depth, depth,
value: value as AppBskyUnspeccedDefs.ThreadItemBlocked, value: value as app.bsky.unspecced.defs.ThreadItemBlocked,
} }
} }
@@ -69,7 +64,7 @@ export function threadPost({
}: { }: {
uri: string uri: string
depth: number depth: number
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost> value: $Typed<app.bsky.unspecced.defs.ThreadItemPost>
moderationOpts: ModerationOpts moderationOpts: ModerationOpts
threadgateHiddenReplies: Set<string> threadgateHiddenReplies: Set<string>
}): Extract<ThreadItem, {type: 'threadPost'}> { }): Extract<ThreadItem, {type: 'threadPost'}> {
@@ -91,8 +86,8 @@ export function threadPost({
* Do not spread anything here, load bearing for post shadow strict * Do not spread anything here, load bearing for post shadow strict
* equality reference checks. * equality reference checks.
*/ */
post: value.post as Omit<AppBskyFeedDefs.PostView, 'record'> & { post: value.post as Omit<app.bsky.feed.defs.PostView, 'record'> & {
record: AppBskyFeedPost.Record record: app.bsky.feed.post.Main
}, },
}, },
isBlurred, isBlurred,
@@ -161,10 +156,10 @@ export function skeleton({
} }
export function postViewToThreadPlaceholder( export function postViewToThreadPlaceholder(
post: AppBskyFeedDefs.PostView, post: app.bsky.feed.defs.PostView,
): $Typed< ): $Typed<
Omit<AppBskyUnspeccedGetPostThreadV2.ThreadItem, 'value'> & { Omit<app.bsky.unspecced.getPostThreadV2.ThreadItem, 'value'> & {
value: $Typed<AppBskyUnspeccedDefs.ThreadItemPost> value: $Typed<app.bsky.unspecced.defs.ThreadItemPost>
} }
> { > {
return { return {
+12 -25
View File
@@ -1,17 +1,11 @@
import {
type AppBskyActorDefs,
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
type AppBskyFeedDefs,
AppBskyFeedPost,
type AtUri,
} from '@atproto/api'
import { import {
type InfiniteData, type InfiniteData,
type QueryClient, type QueryClient,
type QueryKey, type QueryKey,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import {type AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [ export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [
@@ -93,7 +87,7 @@ export async function truncateAndInvalidate<T = any>(
// of the currentUri that is being checked. // of the currentUri that is being checked.
export function didOrHandleUriMatches( export function didOrHandleUriMatches(
atUri: AtUri, atUri: AtUri,
record: {uri: string; author: AppBskyActorDefs.ProfileViewBasic}, record: {uri: string; author: app.bsky.actor.defs.ProfileViewBasic},
) { ) {
if (atUri.host.startsWith('did:')) { if (atUri.host.startsWith('did:')) {
return atUri.href === record.uri return atUri.href === record.uri
@@ -104,26 +98,19 @@ export function didOrHandleUriMatches(
export function getEmbeddedPost( export function getEmbeddedPost(
v: unknown, v: unknown,
): AppBskyEmbedRecord.ViewRecord | undefined { ): app.bsky.embed.record.ViewRecord | undefined {
if ( if (bsky.isType(app.bsky.embed.record.view, v)) {
bsky.dangerousIsType<AppBskyEmbedRecord.View>(v, AppBskyEmbedRecord.isView)
) {
if ( if (
AppBskyEmbedRecord.isViewRecord(v.record) && bsky.isType(app.bsky.embed.record.viewRecord, v.record) &&
AppBskyFeedPost.isRecord(v.record.value) bsky.isType(app.bsky.feed.post, v.record.value)
) { ) {
return v.record return v.record
} }
} }
if ( if (bsky.isType(app.bsky.embed.recordWithMedia.view, v)) {
bsky.dangerousIsType<AppBskyEmbedRecordWithMedia.View>(
v,
AppBskyEmbedRecordWithMedia.isView,
)
) {
if ( if (
AppBskyEmbedRecord.isViewRecord(v.record.record) && bsky.isType(app.bsky.embed.record.viewRecord, v.record.record) &&
AppBskyFeedPost.isRecord(v.record.record.value) bsky.isType(app.bsky.feed.post, v.record.record.value)
) { ) {
return v.record.record return v.record.record
} }
@@ -131,8 +118,8 @@ export function getEmbeddedPost(
} }
export function embedViewRecordToPostView( export function embedViewRecordToPostView(
v: AppBskyEmbedRecord.ViewRecord, v: app.bsky.embed.record.ViewRecord,
): AppBskyFeedDefs.PostView { ): app.bsky.feed.defs.PostView {
return { return {
uri: v.uri, uri: v.uri,
cid: v.cid, cid: v.cid,
@@ -1,4 +1,3 @@
import {type AppBskyActorDefs} from '@atproto/api'
import {type AtIdentifierString, AtUri} from '@atproto/syntax' import {type AtIdentifierString, AtUri} from '@atproto/syntax'
import {useMutation} from '@tanstack/react-query' import {useMutation} from '@tanstack/react-query'
@@ -22,7 +21,7 @@ export function useVerificationsRemoveMutation() {
verifications, verifications,
}: { }: {
profile: bsky.profile.AnyProfileView profile: bsky.profile.AnyProfileView
verifications: AppBskyActorDefs.VerificationView[] verifications: app.bsky.actor.defs.VerificationView[]
}) { }) {
if (!currentAccount) { if (!currentAccount) {
throw new Error('User not logged in') throw new Error('User not logged in')
+5 -9
View File
@@ -1,14 +1,10 @@
import {createContext, useContext, useMemo, useState} from 'react' import {createContext, useContext, useMemo, useState} from 'react'
import {
type AppBskyActorDefs,
type AppBskyFeedDefs,
type AppBskyUnspeccedGetPostThreadV2,
} 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 {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
import {postUriToRelativePath, toBskyAppUrl} from '#/lib/strings/url-helpers' import {postUriToRelativePath, toBskyAppUrl} from '#/lib/strings/url-helpers'
import {purgeTemporaryImageFiles} from '#/state/gallery' import {purgeTemporaryImageFiles} from '#/state/gallery'
@@ -24,15 +20,15 @@ export interface ComposerOptsPostRef {
cid: string cid: string
text: string text: string
langs?: string[] langs?: string[]
author: AppBskyActorDefs.ProfileViewBasic author: app.bsky.actor.defs.ProfileViewBasic
embed?: AppBskyFeedDefs.PostView['embed'] embed?: app.bsky.feed.defs.PostView['embed']
moderation?: ModerationDecision moderation?: ModerationDecision
} }
export type OnPostSuccessData = export type OnPostSuccessData =
| { | {
replyToUri?: string replyToUri?: string
posts: AppBskyUnspeccedGetPostThreadV2.ThreadItem[] posts: app.bsky.unspecced.getPostThreadV2.ThreadItem[]
} }
| undefined | undefined
@@ -48,7 +44,7 @@ export interface ComposerOpts {
replyTo?: ComposerOptsPostRef replyTo?: ComposerOptsPostRef
onPost?: (postUri: string | undefined) => void onPost?: (postUri: string | undefined) => void
onPostSuccess?: (data: OnPostSuccessData) => void onPostSuccess?: (data: OnPostSuccessData) => void
quote?: AppBskyFeedDefs.PostView quote?: app.bsky.feed.defs.PostView
mention?: string // handle of user to mention mention?: string // handle of user to mention
text?: string text?: string
imageUris?: {uri: string; width: number; height: number; altText?: string}[] imageUris?: {uri: string; width: number; height: number; altText?: string}[]
+3 -3
View File
@@ -1,5 +1,5 @@
import {createContext, useCallback, useContext, useMemo, useState} from 'react' import {createContext, useCallback, useContext, useMemo, useState} from 'react'
import {type AppBskyFeedThreadgate} from '@atproto/api' import {app} from '#/lexicons'
type StateContext = { type StateContext = {
uris: Set<string> uris: Set<string>
@@ -74,7 +74,7 @@ export function useThreadgateHiddenReplyUrisAPI() {
export function useMergedThreadgateHiddenReplies({ export function useMergedThreadgateHiddenReplies({
threadgateRecord, threadgateRecord,
}: { }: {
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: app.bsky.feed.threadgate.Main
}) { }) {
const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris() const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris()
return useMemo(() => { return useMemo(() => {
@@ -89,7 +89,7 @@ export function useMergedThreadgateHiddenReplies({
export function useMergeThreadgateHiddenReplies() { export function useMergeThreadgateHiddenReplies() {
const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris() const {uris, recentlyUnhiddenUris} = useThreadgateHiddenReplyUris()
return useCallback( return useCallback(
(threadgate?: AppBskyFeedThreadgate.Record) => { (threadgate?: app.bsky.feed.threadgate.Main) => {
const set = new Set([...(threadgate?.hiddenReplies || []), ...uris]) const set = new Set([...(threadgate?.hiddenReplies || []), ...uris])
for (const uri of recentlyUnhiddenUris) { for (const uri of recentlyUnhiddenUris) {
set.delete(uri) set.delete(uri)
+3 -2
View File
@@ -1,6 +1,7 @@
import {useEffect, useId, useState} from 'react' import {useEffect, useId, useState} from 'react'
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
import {AtUri} from '@atproto/syntax'
import {app} from '#/lexicons'
import {Logger} from '#/logger' import {Logger} from '#/logger'
import {type FeedSourceInfo} from '#/state/queries/feed' import {type FeedSourceInfo} from '#/state/queries/feed'
@@ -10,7 +11,7 @@ import {type FeedSourceInfo} from '#/state/queries/feed'
const logger = Logger.create(Logger.Context.PostSource) const logger = Logger.create(Logger.Context.PostSource)
export type PostSource = { export type PostSource = {
post: AppBskyFeedDefs.FeedViewPost post: app.bsky.feed.defs.FeedViewPost
feedSourceInfo?: FeedSourceInfo feedSourceInfo?: FeedSourceInfo
} }