about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2023-11-16 17:28:50 -0600
committerGitHub <noreply@github.com>2023-11-16 15:28:50 -0800
commit6dfb2a232f89e96c6cca25430afecbce50d08a0f (patch)
tree52c33ca3693e7514cafe8c6bb2cce68949d275d6
parentf89dc6380171db1d402f5e774103355991aafdea (diff)
downloadvoidsky-6dfb2a232f89e96c6cca25430afecbce50d08a0f.tar.zst
Caching heuristics (#1938)
* Tempfix profile load

* First pass at staleTime
-rw-r--r--src/state/queries/actor-autocomplete.ts7
-rw-r--r--src/state/queries/app-passwords.ts5
-rw-r--r--src/state/queries/feed.ts2
-rw-r--r--src/state/queries/handle.ts9
-rw-r--r--src/state/queries/index.ts11
-rw-r--r--src/state/queries/invites.ts2
-rw-r--r--src/state/queries/list-members.ts5
-rw-r--r--src/state/queries/list-memberships.ts7
-rw-r--r--src/state/queries/list.ts2
-rw-r--r--src/state/queries/my-blocked-accounts.ts5
-rw-r--r--src/state/queries/my-follows.ts2
-rw-r--r--src/state/queries/my-lists.ts7
-rw-r--r--src/state/queries/my-muted-accounts.ts5
-rw-r--r--src/state/queries/notifications/feed.ts2
-rw-r--r--src/state/queries/post-feed.ts2
-rw-r--r--src/state/queries/post-liked-by.ts6
-rw-r--r--src/state/queries/post-reposted-by.ts6
-rw-r--r--src/state/queries/post-thread.ts3
-rw-r--r--src/state/queries/post.ts8
-rw-r--r--src/state/queries/preferences/index.ts2
-rw-r--r--src/state/queries/profile-extra-info.ts6
-rw-r--r--src/state/queries/profile-feedgens.ts6
-rw-r--r--src/state/queries/profile-followers.ts5
-rw-r--r--src/state/queries/profile-follows.ts6
-rw-r--r--src/state/queries/profile-lists.ts5
-rw-r--r--src/state/queries/profile.ts6
-rw-r--r--src/state/queries/resolve-uri.ts5
-rw-r--r--src/state/queries/service.ts3
-rw-r--r--src/state/queries/suggested-feeds.ts2
-rw-r--r--src/state/queries/suggested-follows.ts2
30 files changed, 122 insertions, 22 deletions
diff --git a/src/state/queries/actor-autocomplete.ts b/src/state/queries/actor-autocomplete.ts
index de7a2e1f6..e7a8eb30d 100644
--- a/src/state/queries/actor-autocomplete.ts
+++ b/src/state/queries/actor-autocomplete.ts
@@ -5,6 +5,7 @@ import {useQuery, useQueryClient} from '@tanstack/react-query'
 import {logger} from '#/logger'
 import {useSession} from '#/state/session'
 import {useMyFollowsQuery} from '#/state/queries/my-follows'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = (prefix: string) => ['actor-autocomplete', prefix]
 
@@ -13,8 +14,7 @@ export function useActorAutocompleteQuery(prefix: string) {
   const {data: follows, isFetching} = useMyFollowsQuery()
 
   return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({
-    // cached for 1 min
-    staleTime: 60 * 1000,
+    staleTime: STALE.MINUTES.ONE,
     queryKey: RQKEY(prefix || ''),
     async queryFn() {
       const res = prefix
@@ -41,8 +41,7 @@ export function useActorAutocompleteFn() {
       if (query) {
         try {
           res = await queryClient.fetchQuery({
-            // cached for 1 min
-            staleTime: 60 * 1000,
+            staleTime: STALE.MINUTES.ONE,
             queryKey: RQKEY(query || ''),
             queryFn: () =>
               agent.searchActorsTypeahead({
diff --git a/src/state/queries/app-passwords.ts b/src/state/queries/app-passwords.ts
index 22de75ac3..e79a26077 100644
--- a/src/state/queries/app-passwords.ts
+++ b/src/state/queries/app-passwords.ts
@@ -1,12 +1,15 @@
 import {ComAtprotoServerCreateAppPassword} from '@atproto/api'
 import {useQuery, useQueryClient, useMutation} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = () => ['app-passwords']
 
 export function useAppPasswordsQuery() {
   const {agent} = useSession()
   return useQuery({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(),
     queryFn: async () => {
       const res = await agent.com.atproto.server.listAppPasswords({})
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
index 4ec82c6fb..f7692aff3 100644
--- a/src/state/queries/feed.ts
+++ b/src/state/queries/feed.ts
@@ -20,6 +20,7 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names'
 import {sanitizeHandle} from '#/lib/strings/handles'
 import {useSession} from '#/state/session'
 import {usePreferencesQuery} from '#/state/queries/preferences'
+import {STALE} from '#/state/queries'
 
 export type FeedSourceFeedInfo = {
   type: 'feed'
@@ -139,6 +140,7 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
   const type = getFeedTypeFromUri(uri)
 
   return useQuery({
+    staleTime: STALE.INFINITY,
     queryKey: feedSourceInfoQueryKey({uri}),
     queryFn: async () => {
       let view: FeedSourceInfo
diff --git a/src/state/queries/handle.ts b/src/state/queries/handle.ts
index 4c3296587..d73e068ec 100644
--- a/src/state/queries/handle.ts
+++ b/src/state/queries/handle.ts
@@ -2,6 +2,7 @@ import React from 'react'
 import {useQueryClient, useMutation} from '@tanstack/react-query'
 
 import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const fetchHandleQueryKey = (handleOrDid: string) => ['handle', handleOrDid]
 const fetchDidQueryKey = (handleOrDid: string) => ['did', handleOrDid]
@@ -14,6 +15,7 @@ export function useFetchHandle() {
     async (handleOrDid: string) => {
       if (handleOrDid.startsWith('did:')) {
         const res = await queryClient.fetchQuery({
+          staleTime: STALE.MINUTES.FIVE,
           queryKey: fetchHandleQueryKey(handleOrDid),
           queryFn: () => agent.getProfile({actor: handleOrDid}),
         })
@@ -27,11 +29,17 @@ export function useFetchHandle() {
 
 export function useUpdateHandleMutation() {
   const {agent} = useSession()
+  const queryClient = useQueryClient()
 
   return useMutation({
     mutationFn: async ({handle}: {handle: string}) => {
       await agent.updateHandle({handle})
     },
+    onSuccess(_data, variables) {
+      queryClient.invalidateQueries({
+        queryKey: fetchHandleQueryKey(variables.handle),
+      })
+    },
   })
 }
 
@@ -42,6 +50,7 @@ export function useFetchDid() {
   return React.useCallback(
     async (handleOrDid: string) => {
       return queryClient.fetchQuery({
+        staleTime: STALE.INFINITY,
         queryKey: fetchDidQueryKey(handleOrDid),
         queryFn: async () => {
           let identifier = handleOrDid
diff --git a/src/state/queries/index.ts b/src/state/queries/index.ts
index ae3d1595c..affebb907 100644
--- a/src/state/queries/index.ts
+++ b/src/state/queries/index.ts
@@ -3,3 +3,14 @@ import {BskyAgent} from '@atproto/api'
 export const PUBLIC_BSKY_AGENT = new BskyAgent({
   service: 'https://api.bsky.app',
 })
+
+export const STALE = {
+  MINUTES: {
+    ONE: 1e3 * 60,
+    FIVE: 1e3 * 60 * 5,
+  },
+  HOURS: {
+    ONE: 1e3 * 60 * 60,
+  },
+  INFINITY: Infinity,
+}
diff --git a/src/state/queries/invites.ts b/src/state/queries/invites.ts
index 77494d273..ca850d1b6 100644
--- a/src/state/queries/invites.ts
+++ b/src/state/queries/invites.ts
@@ -2,6 +2,7 @@ import {ComAtprotoServerDefs} from '@atproto/api'
 import {useQuery} from '@tanstack/react-query'
 
 import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 function isInviteAvailable(invite: ComAtprotoServerDefs.InviteCode): boolean {
   return invite.available - invite.uses.length > 0 && !invite.disabled
@@ -15,6 +16,7 @@ export function useInviteCodesQuery() {
   const {agent} = useSession()
 
   return useQuery({
+    staleTime: STALE.HOURS.ONE,
     queryKey: ['inviteCodes'],
     queryFn: async () => {
       const res = await agent.com.atproto.server.getAccountInviteCodes({})
diff --git a/src/state/queries/list-members.ts b/src/state/queries/list-members.ts
index ec5daec90..ef3c955ad 100644
--- a/src/state/queries/list-members.ts
+++ b/src/state/queries/list-members.ts
@@ -1,6 +1,8 @@
 import {AppBskyGraphGetList} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
@@ -16,6 +18,7 @@ export function useListMembersQuery(uri: string) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(uri),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.graph.getList({
diff --git a/src/state/queries/list-memberships.ts b/src/state/queries/list-memberships.ts
index f128c5867..b9bb91a24 100644
--- a/src/state/queries/list-memberships.ts
+++ b/src/state/queries/list-memberships.ts
@@ -16,8 +16,10 @@
 
 import {AtUri} from '@atproto/api'
 import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
-import {useSession} from '../session'
-import {RQKEY as LIST_MEMBERS_RQKEY} from './list-members'
+
+import {useSession} from '#/state/session'
+import {RQKEY as LIST_MEMBERS_RQKEY} from '#/state/queries/list-members'
+import {STALE} from '#/state/queries'
 
 // sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records
 const SANITY_PAGE_LIMIT = 1000
@@ -38,6 +40,7 @@ export interface ListMembersip {
 export function useDangerousListMembershipsQuery() {
   const {agent, currentAccount} = useSession()
   return useQuery<ListMembersip[]>({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(),
     async queryFn() {
       if (!currentAccount) {
diff --git a/src/state/queries/list.ts b/src/state/queries/list.ts
index 4a46a4fbe..2270e722c 100644
--- a/src/state/queries/list.ts
+++ b/src/state/queries/list.ts
@@ -13,12 +13,14 @@ import {invalidate as invalidateMyLists} from './my-lists'
 import {RQKEY as PROFILE_LISTS_RQKEY} from './profile-lists'
 import {uploadBlob} from '#/lib/api'
 import {until} from '#/lib/async/until'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = (uri: string) => ['list', uri]
 
 export function useListQuery(uri?: string) {
   const {agent} = useSession()
   return useQuery<AppBskyGraphDefs.ListView, Error>({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(uri || ''),
     async queryFn() {
       if (!uri) {
diff --git a/src/state/queries/my-blocked-accounts.ts b/src/state/queries/my-blocked-accounts.ts
index 448f7dd67..c15ee5745 100644
--- a/src/state/queries/my-blocked-accounts.ts
+++ b/src/state/queries/my-blocked-accounts.ts
@@ -1,6 +1,8 @@
 import {AppBskyGraphGetBlocks} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = () => ['my-blocked-accounts']
 type RQPageParam = string | undefined
@@ -14,6 +16,7 @@ export function useMyBlockedAccountsQuery() {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.graph.getBlocks({
diff --git a/src/state/queries/my-follows.ts b/src/state/queries/my-follows.ts
index ad6cf837d..64966ca81 100644
--- a/src/state/queries/my-follows.ts
+++ b/src/state/queries/my-follows.ts
@@ -1,6 +1,7 @@
 import {AppBskyActorDefs} from '@atproto/api'
 import {useQuery} from '@tanstack/react-query'
 import {useSession} from '../session'
+import {STALE} from '#/state/queries'
 
 // sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records
 const SANITY_PAGE_LIMIT = 1000
@@ -12,6 +13,7 @@ export const RQKEY = () => ['my-follows']
 export function useMyFollowsQuery() {
   const {agent, currentAccount} = useSession()
   return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({
+    staleTime: STALE.MINUTES.ONE,
     queryKey: RQKEY(),
     async queryFn() {
       if (!currentAccount) {
diff --git a/src/state/queries/my-lists.ts b/src/state/queries/my-lists.ts
index d412cff02..7e996c12c 100644
--- a/src/state/queries/my-lists.ts
+++ b/src/state/queries/my-lists.ts
@@ -1,7 +1,9 @@
 import {AppBskyGraphDefs} from '@atproto/api'
 import {useQuery, QueryClient} from '@tanstack/react-query'
-import {accumulate} from 'lib/async/accumulate'
-import {useSession} from '../session'
+
+import {accumulate} from '#/lib/async/accumulate'
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 export type MyListsFilter = 'all' | 'curate' | 'mod'
 export const RQKEY = (filter: MyListsFilter) => ['my-lists', filter]
@@ -9,6 +11,7 @@ export const RQKEY = (filter: MyListsFilter) => ['my-lists', filter]
 export function useMyListsQuery(filter: MyListsFilter) {
   const {agent, currentAccount} = useSession()
   return useQuery<AppBskyGraphDefs.ListView[]>({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(filter),
     async queryFn() {
       let lists: AppBskyGraphDefs.ListView[] = []
diff --git a/src/state/queries/my-muted-accounts.ts b/src/state/queries/my-muted-accounts.ts
index 109874673..c4408a04e 100644
--- a/src/state/queries/my-muted-accounts.ts
+++ b/src/state/queries/my-muted-accounts.ts
@@ -1,6 +1,8 @@
 import {AppBskyGraphGetMutes} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = () => ['my-muted-accounts']
 type RQPageParam = string | undefined
@@ -14,6 +16,7 @@ export function useMyMutedAccountsQuery() {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.graph.getMutes({
diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts
index 9d491c3a4..c1d9edb44 100644
--- a/src/state/queries/notifications/feed.ts
+++ b/src/state/queries/notifications/feed.ts
@@ -12,6 +12,7 @@ import {useSession} from '../../session'
 import {useModerationOpts} from '../preferences'
 import {shouldFilterNotif} from './util'
 import {useMutedThreads} from '#/state/muted-threads'
+import {STALE} from '#/state/queries'
 
 const GROUPABLE_REASONS = ['like', 'repost', 'follow']
 const PAGE_SIZE = 30
@@ -60,6 +61,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.listNotifications({
diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts
index 72f9dd3bd..17cd1abb6 100644
--- a/src/state/queries/post-feed.ts
+++ b/src/state/queries/post-feed.ts
@@ -13,6 +13,7 @@ import {ListFeedAPI} from 'lib/api/feed/list'
 import {MergeFeedAPI} from 'lib/api/feed/merge'
 import {useModerationOpts} from '#/state/queries/preferences'
 import {logger} from '#/logger'
+import {STALE} from '#/state/queries'
 
 type ActorDid = string
 type AuthorFilter =
@@ -132,6 +133,7 @@ export function usePostFeedQuery(
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(feedDesc, params),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       console.log('fetch', feedDesc, pageParam)
diff --git a/src/state/queries/post-liked-by.ts b/src/state/queries/post-liked-by.ts
index 78ce9f60a..7a2161359 100644
--- a/src/state/queries/post-liked-by.ts
+++ b/src/state/queries/post-liked-by.ts
@@ -1,10 +1,13 @@
 import {AppBskyFeedGetLikes} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
 
+// TODO refactor invalidate on mutate?
 export const RQKEY = (resolvedUri: string) => ['post-liked-by', resolvedUri]
 
 export function usePostLikedByQuery(resolvedUri: string | undefined) {
@@ -16,6 +19,7 @@ export function usePostLikedByQuery(resolvedUri: string | undefined) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.MINUTES.ONE,
     queryKey: RQKEY(resolvedUri || ''),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.getLikes({
diff --git a/src/state/queries/post-reposted-by.ts b/src/state/queries/post-reposted-by.ts
index 15cb377b4..7b2f40417 100644
--- a/src/state/queries/post-reposted-by.ts
+++ b/src/state/queries/post-reposted-by.ts
@@ -1,10 +1,13 @@
 import {AppBskyFeedGetRepostedBy} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
 
+// TODO refactor invalidate on mutate?
 export const RQKEY = (resolvedUri: string) => ['post-reposted-by', resolvedUri]
 
 export function usePostRepostedByQuery(resolvedUri: string | undefined) {
@@ -16,6 +19,7 @@ export function usePostRepostedByQuery(resolvedUri: string | undefined) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.MINUTES.ONE,
     queryKey: RQKEY(resolvedUri || ''),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.getRepostedBy({
diff --git a/src/state/queries/post-thread.ts b/src/state/queries/post-thread.ts
index 6ef41feb3..b22b0aa35 100644
--- a/src/state/queries/post-thread.ts
+++ b/src/state/queries/post-thread.ts
@@ -4,8 +4,10 @@ import {
   AppBskyFeedGetPostThread,
 } from '@atproto/api'
 import {useQuery} from '@tanstack/react-query'
+
 import {useSession} from '#/state/session'
 import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = (uri: string) => ['post-thread', uri]
 type ThreadViewNode = AppBskyFeedGetPostThread.OutputSchema['thread']
@@ -58,6 +60,7 @@ export type ThreadNode =
 export function usePostThreadQuery(uri: string | undefined) {
   const {agent} = useSession()
   return useQuery<ThreadNode, Error>({
+    staleTime: STALE.MINUTES.ONE,
     queryKey: RQKEY(uri || ''),
     async queryFn() {
       const res = await agent.getPostThread({uri: uri!})
diff --git a/src/state/queries/post.ts b/src/state/queries/post.ts
index 2aaa9a347..73d94f434 100644
--- a/src/state/queries/post.ts
+++ b/src/state/queries/post.ts
@@ -1,14 +1,17 @@
 import React from 'react'
 import {AppBskyFeedDefs, AtUri} from '@atproto/api'
 import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query'
-import {useSession} from '../session'
-import {updatePostShadow} from '../cache/post-shadow'
+
+import {useSession} from '#/state/session'
+import {updatePostShadow} from '#/state/cache/post-shadow'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = (postUri: string) => ['post', postUri]
 
 export function usePostQuery(uri: string | undefined) {
   const {agent} = useSession()
   return useQuery<AppBskyFeedDefs.PostView>({
+    staleTime: STALE.MINUTES.ONE,
     queryKey: RQKEY(uri || ''),
     async queryFn() {
       const res = await agent.getPosts({uris: [uri!]})
@@ -28,6 +31,7 @@ export function useGetPost() {
   return React.useCallback(
     async ({uri}: {uri: string}) => {
       return queryClient.fetchQuery({
+        staleTime: STALE.MINUTES.ONE,
         queryKey: RQKEY(uri || ''),
         async queryFn() {
           const urip = new AtUri(uri)
diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts
index 7e16d4e9a..f6ce20ee4 100644
--- a/src/state/queries/preferences/index.ts
+++ b/src/state/queries/preferences/index.ts
@@ -22,6 +22,7 @@ import {
   DEFAULT_THREAD_VIEW_PREFS,
 } from '#/state/queries/preferences/const'
 import {getModerationOpts} from '#/state/queries/preferences/moderation'
+import {STALE} from '#/state/queries'
 
 export * from '#/state/queries/preferences/types'
 export * from '#/state/queries/preferences/moderation'
@@ -33,6 +34,7 @@ export function usePreferencesQuery() {
   const {agent, hasSession} = useSession()
   return useQuery({
     enabled: hasSession,
+    staleTime: STALE.INFINITY,
     queryKey: usePreferencesQueryKey,
     queryFn: async () => {
       const res = await agent.getPreferences()
diff --git a/src/state/queries/profile-extra-info.ts b/src/state/queries/profile-extra-info.ts
index 6ba665086..c30a2c040 100644
--- a/src/state/queries/profile-extra-info.ts
+++ b/src/state/queries/profile-extra-info.ts
@@ -1,6 +1,9 @@
 import {useQuery} from '@tanstack/react-query'
-import {useSession} from '../session'
 
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
+
+// TODO refactor invalidate on mutate?
 export const RQKEY = (did: string) => ['profile-extra-info', did]
 
 /**
@@ -10,6 +13,7 @@ export const RQKEY = (did: string) => ['profile-extra-info', did]
 export function useProfileExtraInfoQuery(did: string) {
   const {agent} = useSession()
   return useQuery({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(did),
     async queryFn() {
       const [listsRes, feedsRes] = await Promise.all([
diff --git a/src/state/queries/profile-feedgens.ts b/src/state/queries/profile-feedgens.ts
index fc0c91c27..693168625 100644
--- a/src/state/queries/profile-feedgens.ts
+++ b/src/state/queries/profile-feedgens.ts
@@ -1,10 +1,13 @@
 import {AppBskyFeedGetActorFeeds} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
 
+// TODO refactor invalidate on mutate?
 export const RQKEY = (did: string) => ['profile-feedgens', did]
 
 export function useProfileFeedgensQuery(
@@ -20,6 +23,7 @@ export function useProfileFeedgensQuery(
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(did),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.feed.getActorFeeds({
diff --git a/src/state/queries/profile-followers.ts b/src/state/queries/profile-followers.ts
index 8e76a20a0..dbad57486 100644
--- a/src/state/queries/profile-followers.ts
+++ b/src/state/queries/profile-followers.ts
@@ -1,6 +1,8 @@
 import {AppBskyGraphGetFollowers} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
@@ -16,6 +18,7 @@ export function useProfileFollowersQuery(did: string | undefined) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.MINUTES.FIVE,
     queryKey: RQKEY(did || ''),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.graph.getFollowers({
diff --git a/src/state/queries/profile-follows.ts b/src/state/queries/profile-follows.ts
index f96cfc107..b9d95c298 100644
--- a/src/state/queries/profile-follows.ts
+++ b/src/state/queries/profile-follows.ts
@@ -1,10 +1,13 @@
 import {AppBskyGraphGetFollows} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
 
+// TODO refactor invalidate on mutate?
 export const RQKEY = (did: string) => ['profile-follows', did]
 
 export function useProfileFollowsQuery(did: string | undefined) {
@@ -16,6 +19,7 @@ export function useProfileFollowsQuery(did: string | undefined) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(did || ''),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.graph.getFollows({
diff --git a/src/state/queries/profile-lists.ts b/src/state/queries/profile-lists.ts
index 7d36af28c..f24c57858 100644
--- a/src/state/queries/profile-lists.ts
+++ b/src/state/queries/profile-lists.ts
@@ -1,6 +1,8 @@
 import {AppBskyGraphGetLists} from '@atproto/api'
 import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 const PAGE_SIZE = 30
 type RQPageParam = string | undefined
@@ -17,6 +19,7 @@ export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
     QueryKey,
     RQPageParam
   >({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(did),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
       const res = await agent.app.bsky.graph.getLists({
diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts
index 0383de338..9805c2713 100644
--- a/src/state/queries/profile.ts
+++ b/src/state/queries/profile.ts
@@ -16,12 +16,14 @@ import {Shadow} from '#/state/cache/types'
 import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
 import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
 import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = (did: string) => ['profile', did]
 
 export function useProfileQuery({did}: {did: string | undefined}) {
   const {agent} = useSession()
   return useQuery({
+    staleTime: STALE.MINUTES.FIVE,
     queryKey: RQKEY(did || ''),
     queryFn: async () => {
       const res = await agent.getProfile({actor: did || ''})
@@ -304,6 +306,7 @@ function useProfileMuteMutation() {
 
 function useProfileUnmuteMutation() {
   const {agent} = useSession()
+  const queryClient = useQueryClient()
   return useMutation<void, Error, {did: string; skipOptimistic?: boolean}>({
     mutationFn: async ({did}) => {
       await agent.unmute(did)
@@ -316,6 +319,9 @@ function useProfileUnmuteMutation() {
         })
       }
     },
+    onSuccess() {
+      queryClient.invalidateQueries({queryKey: RQKEY_MY_MUTED()})
+    },
     onError(error, variables) {
       if (!variables.skipOptimistic) {
         // revert the optimistic update
diff --git a/src/state/queries/resolve-uri.ts b/src/state/queries/resolve-uri.ts
index 83bccdce7..370f2a5b6 100644
--- a/src/state/queries/resolve-uri.ts
+++ b/src/state/queries/resolve-uri.ts
@@ -1,12 +1,15 @@
 import {useQuery} from '@tanstack/react-query'
 import {AtUri} from '@atproto/api'
-import {useSession} from '../session'
+
+import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 export const RQKEY = (uri: string) => ['resolved-uri', uri]
 
 export function useResolveUriQuery(uri: string | undefined) {
   const {agent} = useSession()
   return useQuery<{uri: string; did: string}, Error>({
+    staleTime: STALE.INFINITY,
     queryKey: RQKEY(uri || ''),
     async queryFn() {
       const urip = new AtUri(uri || '')
diff --git a/src/state/queries/service.ts b/src/state/queries/service.ts
index 5f7e10778..c7df89960 100644
--- a/src/state/queries/service.ts
+++ b/src/state/queries/service.ts
@@ -1,10 +1,13 @@
 import {BskyAgent} from '@atproto/api'
 import {useQuery} from '@tanstack/react-query'
 
+import {STALE} from '#/state/queries'
+
 export const RQKEY = (serviceUrl: string) => ['service', serviceUrl]
 
 export function useServiceQuery(serviceUrl: string) {
   return useQuery({
+    staleTime: STALE.HOURS.ONE,
     queryKey: RQKEY(serviceUrl),
     queryFn: async () => {
       const agent = new BskyAgent({service: serviceUrl})
diff --git a/src/state/queries/suggested-feeds.ts b/src/state/queries/suggested-feeds.ts
index e148c97c3..a2418a0c4 100644
--- a/src/state/queries/suggested-feeds.ts
+++ b/src/state/queries/suggested-feeds.ts
@@ -2,6 +2,7 @@ import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
 import {AppBskyFeedGetSuggestedFeeds} from '@atproto/api'
 
 import {useSession} from '#/state/session'
+import {STALE} from '#/state/queries'
 
 export const suggestedFeedsQueryKey = ['suggestedFeeds']
 
@@ -15,6 +16,7 @@ export function useSuggestedFeedsQuery() {
     QueryKey,
     string | undefined
   >({
+    staleTime: STALE.INFINITY,
     queryKey: suggestedFeedsQueryKey,
     queryFn: async ({pageParam}) => {
       const res = await agent.app.bsky.feed.getSuggestedFeeds({
diff --git a/src/state/queries/suggested-follows.ts b/src/state/queries/suggested-follows.ts
index e16088d6a..275450b97 100644
--- a/src/state/queries/suggested-follows.ts
+++ b/src/state/queries/suggested-follows.ts
@@ -14,6 +14,7 @@ import {
 
 import {useSession} from '#/state/session'
 import {useModerationOpts} from '#/state/queries/preferences'
+import {STALE} from '#/state/queries'
 
 const suggestedFollowsQueryKey = ['suggested-follows']
 const suggestedFollowsByActorQueryKey = (did: string) => [
@@ -33,6 +34,7 @@ export function useSuggestedFollowsQuery() {
     string | undefined
   >({
     enabled: !!moderationOpts,
+    staleTime: STALE.INFINITY,
     queryKey: suggestedFollowsQueryKey,
     queryFn: async ({pageParam}) => {
       const res = await agent.app.bsky.actor.getSuggestions({