diff options
Diffstat (limited to 'src/state/queries')
-rw-r--r-- | src/state/queries/actor-autocomplete.ts | 9 | ||||
-rw-r--r-- | src/state/queries/index.ts | 8 | ||||
-rw-r--r-- | src/state/queries/messages/conversation.ts | 25 | ||||
-rw-r--r-- | src/state/queries/messages/get-convo-for-members.ts | 39 | ||||
-rw-r--r-- | src/state/queries/messages/leave-conversation.ts | 68 | ||||
-rw-r--r-- | src/state/queries/messages/list-converations.ts | 29 | ||||
-rw-r--r-- | src/state/queries/messages/mute-conversation.ts | 84 | ||||
-rw-r--r-- | src/state/queries/messages/temp-headers.ts | 11 | ||||
-rw-r--r-- | src/state/queries/notifications/feed.ts | 2 | ||||
-rw-r--r-- | src/state/queries/notifications/unread.tsx | 2 | ||||
-rw-r--r-- | src/state/queries/post-feed.ts | 16 | ||||
-rw-r--r-- | src/state/queries/preferences/index.ts | 48 | ||||
-rw-r--r-- | src/state/queries/suggested-follows.ts | 28 |
13 files changed, 303 insertions, 66 deletions
diff --git a/src/state/queries/actor-autocomplete.ts b/src/state/queries/actor-autocomplete.ts index 98b5aa17e..8708a244b 100644 --- a/src/state/queries/actor-autocomplete.ts +++ b/src/state/queries/actor-autocomplete.ts @@ -6,7 +6,8 @@ import {isJustAMute} from '#/lib/moderation' import {logger} from '#/logger' import {STALE} from '#/state/queries' import {useAgent} from '#/state/session' -import {DEFAULT_LOGGED_OUT_PREFERENCES, useModerationOpts} from './preferences' +import {useModerationOpts} from '../preferences/moderation-opts' +import {DEFAULT_LOGGED_OUT_PREFERENCES} from './preferences' const DEFAULT_MOD_OPTS = { userDid: undefined, @@ -23,7 +24,11 @@ export function useActorAutocompleteQuery( const moderationOpts = useModerationOpts() const {getAgent} = useAgent() - prefix = prefix.toLowerCase() + prefix = prefix.toLowerCase().trim() + if (prefix.endsWith('.')) { + // Going from "foo" to "foo." should not clear matches. + prefix = prefix.slice(0, -1) + } return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({ staleTime: STALE.MINUTES.ONE, diff --git a/src/state/queries/index.ts b/src/state/queries/index.ts index e30528ca1..0635bf316 100644 --- a/src/state/queries/index.ts +++ b/src/state/queries/index.ts @@ -1,11 +1,3 @@ -import {BskyAgent} from '@atproto/api' - -import {PUBLIC_BSKY_SERVICE} from '#/lib/constants' - -export const PUBLIC_BSKY_AGENT = new BskyAgent({ - service: PUBLIC_BSKY_SERVICE, -}) - export const STALE = { SECONDS: { FIFTEEN: 1e3 * 15, diff --git a/src/state/queries/messages/conversation.ts b/src/state/queries/messages/conversation.ts new file mode 100644 index 000000000..9456861d2 --- /dev/null +++ b/src/state/queries/messages/conversation.ts @@ -0,0 +1,25 @@ +import {BskyAgent} from '@atproto-labs/api' +import {useQuery} from '@tanstack/react-query' + +import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage' +import {useHeaders} from './temp-headers' + +const RQKEY_ROOT = 'convo' +export const RQKEY = (convoId: string) => [RQKEY_ROOT, convoId] + +export function useConvoQuery(convoId: string) { + const headers = useHeaders() + const {serviceUrl} = useDmServiceUrlStorage() + + return useQuery({ + queryKey: RQKEY(convoId), + queryFn: async () => { + const agent = new BskyAgent({service: serviceUrl}) + const {data} = await agent.api.chat.bsky.convo.getConvo( + {convoId}, + {headers}, + ) + return data.convo + }, + }) +} diff --git a/src/state/queries/messages/get-convo-for-members.ts b/src/state/queries/messages/get-convo-for-members.ts new file mode 100644 index 000000000..0a657c07e --- /dev/null +++ b/src/state/queries/messages/get-convo-for-members.ts @@ -0,0 +1,39 @@ +import {BskyAgent, ChatBskyConvoGetConvoForMembers} from '@atproto-labs/api' +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {logger} from '#/logger' +import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage' +import {RQKEY as CONVO_KEY} from './conversation' +import {useHeaders} from './temp-headers' + +export function useGetConvoForMembers({ + onSuccess, + onError, +}: { + onSuccess?: (data: ChatBskyConvoGetConvoForMembers.OutputSchema) => void + onError?: (error: Error) => void +}) { + const queryClient = useQueryClient() + const headers = useHeaders() + const {serviceUrl} = useDmServiceUrlStorage() + + return useMutation({ + mutationFn: async (members: string[]) => { + const agent = new BskyAgent({service: serviceUrl}) + const {data} = await agent.api.chat.bsky.convo.getConvoForMembers( + {members: members}, + {headers}, + ) + + return data + }, + onSuccess: data => { + queryClient.setQueryData(CONVO_KEY(data.convo.id), data.convo) + onSuccess?.(data) + }, + onError: error => { + logger.error(error) + onError?.(error) + }, + }) +} diff --git a/src/state/queries/messages/leave-conversation.ts b/src/state/queries/messages/leave-conversation.ts new file mode 100644 index 000000000..0dd67fa0b --- /dev/null +++ b/src/state/queries/messages/leave-conversation.ts @@ -0,0 +1,68 @@ +import { + BskyAgent, + ChatBskyConvoLeaveConvo, + ChatBskyConvoListConvos, +} from '@atproto-labs/api' +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {logger} from '#/logger' +import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage' +import {RQKEY as CONVO_LIST_KEY} from './list-converations' +import {useHeaders} from './temp-headers' + +export function useLeaveConvo( + convoId: string, + { + onSuccess, + onError, + }: { + onSuccess?: (data: ChatBskyConvoLeaveConvo.OutputSchema) => void + onError?: (error: Error) => void + }, +) { + const queryClient = useQueryClient() + const headers = useHeaders() + const {serviceUrl} = useDmServiceUrlStorage() + + return useMutation({ + mutationFn: async () => { + const agent = new BskyAgent({service: serviceUrl}) + const {data} = await agent.api.chat.bsky.convo.leaveConvo( + {convoId}, + {headers, encoding: 'application/json'}, + ) + + return data + }, + onMutate: () => { + queryClient.setQueryData( + CONVO_LIST_KEY, + (old?: { + pageParams: Array<string | undefined> + pages: Array<ChatBskyConvoListConvos.OutputSchema> + }) => { + console.log('old', old) + if (!old) return old + return { + ...old, + pages: old.pages.map(page => { + return { + ...page, + convos: page.convos.filter(convo => convo.id !== convoId), + } + }), + } + }, + ) + }, + onSuccess: data => { + queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY}) + onSuccess?.(data) + }, + onError: error => { + logger.error(error) + queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY}) + onError?.(error) + }, + }) +} diff --git a/src/state/queries/messages/list-converations.ts b/src/state/queries/messages/list-converations.ts new file mode 100644 index 000000000..1e4ecb6d7 --- /dev/null +++ b/src/state/queries/messages/list-converations.ts @@ -0,0 +1,29 @@ +import {BskyAgent} from '@atproto-labs/api' +import {useInfiniteQuery} from '@tanstack/react-query' + +import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage' +import {useHeaders} from './temp-headers' + +export const RQKEY = ['convo-list'] +type RQPageParam = string | undefined + +export function useListConvos({refetchInterval}: {refetchInterval: number}) { + const headers = useHeaders() + const {serviceUrl} = useDmServiceUrlStorage() + + return useInfiniteQuery({ + queryKey: RQKEY, + queryFn: async ({pageParam}) => { + const agent = new BskyAgent({service: serviceUrl}) + const {data} = await agent.api.chat.bsky.convo.listConvos( + {cursor: pageParam}, + {headers}, + ) + + return data + }, + initialPageParam: undefined as RQPageParam, + getNextPageParam: lastPage => lastPage.cursor, + refetchInterval, + }) +} diff --git a/src/state/queries/messages/mute-conversation.ts b/src/state/queries/messages/mute-conversation.ts new file mode 100644 index 000000000..4840c65ad --- /dev/null +++ b/src/state/queries/messages/mute-conversation.ts @@ -0,0 +1,84 @@ +import { + BskyAgent, + ChatBskyConvoMuteConvo, + ChatBskyConvoUnmuteConvo, +} from '@atproto-labs/api' +import {useMutation, useQueryClient} from '@tanstack/react-query' + +import {logger} from '#/logger' +import {useDmServiceUrlStorage} from '#/screens/Messages/Temp/useDmServiceUrlStorage' +import {RQKEY as CONVO_KEY} from './conversation' +import {RQKEY as CONVO_LIST_KEY} from './list-converations' +import {useHeaders} from './temp-headers' + +export function useMuteConvo( + convoId: string, + { + onSuccess, + onError, + }: { + onSuccess?: (data: ChatBskyConvoMuteConvo.OutputSchema) => void + onError?: (error: Error) => void + }, +) { + const queryClient = useQueryClient() + const headers = useHeaders() + const {serviceUrl} = useDmServiceUrlStorage() + + return useMutation({ + mutationFn: async () => { + const agent = new BskyAgent({service: serviceUrl}) + const {data} = await agent.api.chat.bsky.convo.muteConvo( + {convoId}, + {headers, encoding: 'application/json'}, + ) + + return data + }, + onSuccess: data => { + queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY}) + queryClient.invalidateQueries({queryKey: CONVO_KEY(convoId)}) + onSuccess?.(data) + }, + onError: error => { + logger.error(error) + onError?.(error) + }, + }) +} + +export function useUnmuteConvo( + convoId: string, + { + onSuccess, + onError, + }: { + onSuccess?: (data: ChatBskyConvoUnmuteConvo.OutputSchema) => void + onError?: (error: Error) => void + }, +) { + const queryClient = useQueryClient() + const headers = useHeaders() + const {serviceUrl} = useDmServiceUrlStorage() + + return useMutation({ + mutationFn: async () => { + const agent = new BskyAgent({service: serviceUrl}) + const {data} = await agent.api.chat.bsky.convo.unmuteConvo( + {convoId}, + {headers, encoding: 'application/json'}, + ) + + return data + }, + onSuccess: data => { + queryClient.invalidateQueries({queryKey: CONVO_LIST_KEY}) + queryClient.invalidateQueries({queryKey: CONVO_KEY(convoId)}) + onSuccess?.(data) + }, + onError: error => { + logger.error(error) + onError?.(error) + }, + }) +} diff --git a/src/state/queries/messages/temp-headers.ts b/src/state/queries/messages/temp-headers.ts new file mode 100644 index 000000000..9e46e8a61 --- /dev/null +++ b/src/state/queries/messages/temp-headers.ts @@ -0,0 +1,11 @@ +import {useSession} from '#/state/session' + +// toy auth +export const useHeaders = () => { + const {currentAccount} = useSession() + return { + get Authorization() { + return currentAccount!.did + }, + } +} diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts index 1f2199901..80e5a4c47 100644 --- a/src/state/queries/notifications/feed.ts +++ b/src/state/queries/notifications/feed.ts @@ -28,8 +28,8 @@ import { import {useMutedThreads} from '#/state/muted-threads' import {useAgent} from '#/state/session' +import {useModerationOpts} from '../../preferences/moderation-opts' import {STALE} from '..' -import {useModerationOpts} from '../preferences' import {embedViewRecordToPostView, getEmbeddedPost} from '../util' import {FeedPage} from './types' import {useUnreadNotificationsApi} from './unread' diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index 1c569e2a0..80333b524 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -13,7 +13,7 @@ import {logger} from '#/logger' import {isNative} from '#/platform/detection' import {useMutedThreads} from '#/state/muted-threads' import {useAgent, useSession} from '#/state/session' -import {useModerationOpts} from '../preferences' +import {useModerationOpts} from '../../preferences/moderation-opts' import {truncateAndInvalidate} from '../util' import {RQKEY as RQKEY_NOTIFS} from './feed' import {CachedFeedPage, FeedPage} from './types' diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index 747dba02e..827f8a2a8 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -15,6 +15,7 @@ import { } from '@tanstack/react-query' import {HomeFeedAPI} from '#/lib/api/feed/home' +import {aggregateUserInterests} from '#/lib/api/feed/utils' import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' import {logger} from '#/logger' import {STALE} from '#/state/queries' @@ -31,7 +32,8 @@ import {FeedTuner, FeedTunerFn, NoopFeedTuner} from 'lib/api/feed-manip' import {BSKY_FEED_OWNER_DIDS} from 'lib/constants' import {KnownError} from '#/view/com/posts/FeedErrorMessage' import {useFeedTuners} from '../preferences/feed-tuners' -import {useModerationOpts} from './preferences' +import {useModerationOpts} from '../preferences/moderation-opts' +import {usePreferencesQuery} from './preferences' import {embedViewRecordToPostView, getEmbeddedPost} from './util' type ActorDid = string @@ -102,8 +104,11 @@ export function usePostFeedQuery( ) { const feedTuners = useFeedTuners(feedDesc) const moderationOpts = useModerationOpts() + const {data: preferences} = usePreferencesQuery() + const enabled = + opts?.enabled !== false && Boolean(moderationOpts) && Boolean(preferences) + const userInterests = aggregateUserInterests(preferences) const {getAgent} = useAgent() - const enabled = opts?.enabled !== false && Boolean(moderationOpts) const lastRun = useRef<{ data: InfiniteData<FeedPageUnselected> args: typeof selectArgs @@ -141,6 +146,7 @@ export function usePostFeedQuery( feedDesc, feedParams: params || {}, feedTuners, + userInterests, // Not in the query key because they don't change. getAgent, }), cursor: undefined, @@ -371,11 +377,13 @@ function createApi({ feedDesc, feedParams, feedTuners, + userInterests, getAgent, }: { feedDesc: FeedDescriptor feedParams: FeedParams feedTuners: FeedTunerFn[] + userInterests?: string getAgent: () => BskyAgent }) { if (feedDesc === 'home') { @@ -384,9 +392,10 @@ function createApi({ getAgent, feedParams, feedTuners, + userInterests, }) } else { - return new HomeFeedAPI({getAgent}) + return new HomeFeedAPI({getAgent, userInterests}) } } else if (feedDesc === 'following') { return new FollowingFeedAPI({getAgent}) @@ -401,6 +410,7 @@ function createApi({ return new CustomFeedAPI({ getAgent, feedParams: {feed}, + userInterests, }) } else if (feedDesc.startsWith('list')) { const [_, list] = feedDesc.split('|') diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 06e47391f..f51eaac2a 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -1,28 +1,24 @@ -import {createContext, useContext, useMemo} from 'react' import { AppBskyActorDefs, - BSKY_LABELER_DID, BskyFeedViewPreference, LabelPreference, - ModerationOpts, } from '@atproto/api' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {track} from '#/lib/analytics/analytics' +import {replaceEqualDeep} from '#/lib/functions' import {getAge} from '#/lib/strings/time' -import {useHiddenPosts, useLabelDefinitions} from '#/state/preferences' import {STALE} from '#/state/queries' import { DEFAULT_HOME_FEED_PREFS, DEFAULT_LOGGED_OUT_PREFERENCES, DEFAULT_THREAD_VIEW_PREFS, } from '#/state/queries/preferences/const' -import {DEFAULT_LOGGED_OUT_LABEL_PREFERENCES} from '#/state/queries/preferences/moderation' import { ThreadViewPreferences, UsePreferencesQueryResponse, } from '#/state/queries/preferences/types' -import {useAgent, useSession} from '#/state/session' +import {useAgent} from '#/state/session' import {saveLabelers} from '#/state/session/agent-config' export * from '#/state/queries/preferences/const' @@ -36,7 +32,7 @@ export function usePreferencesQuery() { const {getAgent} = useAgent() return useQuery({ staleTime: STALE.SECONDS.FIFTEEN, - structuralSharing: true, + structuralSharing: replaceEqualDeep, refetchOnWindowFocus: true, queryKey: preferencesQueryKey, queryFn: async () => { @@ -79,44 +75,6 @@ export function usePreferencesQuery() { }) } -// used in the moderation state devtool -export const moderationOptsOverrideContext = createContext< - ModerationOpts | undefined ->(undefined) - -export function useModerationOpts() { - const override = useContext(moderationOptsOverrideContext) - const {currentAccount} = useSession() - const prefs = usePreferencesQuery() - const {labelDefs} = useLabelDefinitions() - const hiddenPosts = useHiddenPosts() // TODO move this into pds-stored prefs - const opts = useMemo<ModerationOpts | undefined>(() => { - if (override) { - return override - } - if (!prefs.data) { - return - } - return { - userDid: currentAccount?.did, - prefs: { - ...prefs.data.moderationPrefs, - labelers: prefs.data.moderationPrefs.labelers.length - ? prefs.data.moderationPrefs.labelers - : [ - { - did: BSKY_LABELER_DID, - labels: DEFAULT_LOGGED_OUT_LABEL_PREFERENCES, - }, - ], - hiddenPosts: hiddenPosts || [], - }, - labelDefs, - } - }, [override, currentAccount, labelDefs, prefs.data, hiddenPosts]) - return opts -} - export function useClearPreferencesMutation() { const queryClient = useQueryClient() const {getAgent} = useAgent() diff --git a/src/state/queries/suggested-follows.ts b/src/state/queries/suggested-follows.ts index 936912ab3..7740b1977 100644 --- a/src/state/queries/suggested-follows.ts +++ b/src/state/queries/suggested-follows.ts @@ -12,9 +12,15 @@ import { useQuery, } from '@tanstack/react-query' +import { + aggregateUserInterests, + createBskyTopicsHeader, +} from '#/lib/api/feed/utils' +import {getContentLanguages} from '#/state/preferences/languages' import {STALE} from '#/state/queries' -import {useModerationOpts} from '#/state/queries/preferences' +import {usePreferencesQuery} from '#/state/queries/preferences' import {useAgent, useSession} from '#/state/session' +import {useModerationOpts} from '../preferences/moderation-opts' const suggestedFollowsQueryKeyRoot = 'suggested-follows' const suggestedFollowsQueryKey = [suggestedFollowsQueryKeyRoot] @@ -29,6 +35,7 @@ export function useSuggestedFollowsQuery() { const {currentAccount} = useSession() const {getAgent} = useAgent() const moderationOpts = useModerationOpts() + const {data: preferences} = usePreferencesQuery() return useInfiniteQuery< AppBskyActorGetSuggestions.OutputSchema, @@ -37,14 +44,23 @@ export function useSuggestedFollowsQuery() { QueryKey, string | undefined >({ - enabled: !!moderationOpts, + enabled: !!moderationOpts && !!preferences, staleTime: STALE.HOURS.ONE, queryKey: suggestedFollowsQueryKey, queryFn: async ({pageParam}) => { - const res = await getAgent().app.bsky.actor.getSuggestions({ - limit: 25, - cursor: pageParam, - }) + const contentLangs = getContentLanguages().join(',') + const res = await getAgent().app.bsky.actor.getSuggestions( + { + limit: 25, + cursor: pageParam, + }, + { + headers: { + ...createBskyTopicsHeader(aggregateUserInterests(preferences)), + 'Accept-Language': contentLangs, + }, + }, + ) res.data.actors = res.data.actors .filter( |