An ATproto social media client -- with an independent Appview.

fix: use a seperate agent instead for alternative atproto-proxy header; avoids what I assume was a race condition

Changed files
+83 -101
src
state
queries
notifications
preferences
session
+8 -11
src/state/queries/notifications/settings.ts
··· 7 7 useQueryClient, 8 8 } from '@tanstack/react-query' 9 9 10 - import {BLUESKY_PROXY_HEADER, PBLLC_BLUESKY_PROXY_HEADER} from '#/lib/constants' 11 10 import {logger} from '#/logger' 12 - import {useAgent} from '#/state/session' 11 + import {getBskyAppviewAgent as getBskyAppviewAgent} from '#/state/session/appview-agent' 13 12 import * as Toast from '#/view/com/util/Toast' 14 13 15 14 const RQKEY_ROOT = 'notification-settings' ··· 18 17 export function useNotificationSettingsQuery({ 19 18 enabled, 20 19 }: {enabled?: boolean} = {}) { 21 - const agent = useAgent() 22 - 23 20 return useQuery({ 24 21 queryKey: RQKEY, 25 22 queryFn: async () => { 26 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 27 - const response = await agent.app.bsky.notification.getPreferences() 28 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 23 + // Use appview agent which already has PBLLC proxy configured 24 + const bskyAppviewAgent = getBskyAppviewAgent() 25 + const response = 26 + await bskyAppviewAgent.app.bsky.notification.getPreferences() 29 27 return response.data.preferences 30 28 }, 31 29 enabled, 32 30 }) 33 31 } 34 32 export function useNotificationSettingsUpdateMutation() { 35 - const agent = useAgent() 36 33 const queryClient = useQueryClient() 37 34 38 35 return useMutation({ 39 36 mutationFn: async ( 40 37 update: Partial<AppBskyNotificationDefs.Preferences>, 41 38 ) => { 42 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 39 + // Use appview agent which already has PBLLC proxy configured 40 + const appviewAgent = getBskyAppviewAgent() 43 41 const response = 44 - await agent.app.bsky.notification.putPreferencesV2(update) 45 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 42 + await appviewAgent.app.bsky.notification.putPreferencesV2(update) 46 43 return response.data.preferences 47 44 }, 48 45 onMutate: update => {
+48 -90
src/state/queries/preferences/index.ts
··· 6 6 } from '@atproto/api' 7 7 import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' 8 8 9 - import { 10 - BLUESKY_PROXY_HEADER, 11 - PBLLC_BLUESKY_PROXY_HEADER, 12 - PROD_DEFAULT_FEED, 13 - } from '#/lib/constants' 9 + import {PROD_DEFAULT_FEED} from '#/lib/constants' 14 10 import {replaceEqualDeep} from '#/lib/functions' 15 11 import {getAge} from '#/lib/strings/time' 16 12 import {logger} from '#/logger' ··· 28 24 } from '#/state/queries/preferences/types' 29 25 import {useAgent} from '#/state/session' 30 26 import {saveLabelers} from '#/state/session/agent-config' 27 + import {getBskyAppviewAgent} from '#/state/session/appview-agent' 31 28 32 29 export * from '#/state/queries/preferences/const' 33 30 export * from '#/state/queries/preferences/moderation' ··· 49 46 if (!agent.did) { 50 47 return DEFAULT_LOGGED_OUT_PREFERENCES 51 48 } else { 52 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 53 - const res = await agent.getPreferences() 54 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 49 + const appviewAgent = getBskyAppviewAgent() 50 + const res = await appviewAgent.getPreferences() 55 51 56 52 // save to local storage to ensure there are labels on initial requests 57 53 saveLabelers( ··· 99 95 100 96 export function useClearPreferencesMutation() { 101 97 const queryClient = useQueryClient() 102 - const agent = useAgent() 103 98 104 99 return useMutation({ 105 100 mutationFn: async () => { 106 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 107 - await agent.app.bsky.actor.putPreferences({preferences: []}) 108 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 101 + const appviewAgent = getBskyAppviewAgent() 102 + await appviewAgent.app.bsky.actor.putPreferences({preferences: []}) 109 103 // triggers a refetch 110 104 await queryClient.invalidateQueries({ 111 105 queryKey: preferencesQueryKey, ··· 115 109 } 116 110 117 111 export function usePreferencesSetContentLabelMutation() { 118 - const agent = useAgent() 119 112 const queryClient = useQueryClient() 120 113 121 114 return useMutation< ··· 124 117 {label: string; visibility: LabelPreference; labelerDid: string | undefined} 125 118 >({ 126 119 mutationFn: async ({label, visibility, labelerDid}) => { 127 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 128 - await agent.setContentLabelPref(label, visibility, labelerDid) 129 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 120 + const appviewAgent = getBskyAppviewAgent() 121 + await appviewAgent.setContentLabelPref(label, visibility, labelerDid) 130 122 logger.metric( 131 123 'moderation:changeLabelPreference', 132 124 {preference: visibility}, ··· 142 134 143 135 export function useSetContentLabelMutation() { 144 136 const queryClient = useQueryClient() 145 - const agent = useAgent() 146 137 147 138 return useMutation({ 148 139 mutationFn: async ({ ··· 154 145 visibility: LabelPreference 155 146 labelerDid?: string 156 147 }) => { 157 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 158 - await agent.setContentLabelPref(label, visibility, labelerDid) 159 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 148 + const appviewAgent = getBskyAppviewAgent() 149 + await appviewAgent.setContentLabelPref(label, visibility, labelerDid) 160 150 // triggers a refetch 161 151 await queryClient.invalidateQueries({ 162 152 queryKey: preferencesQueryKey, ··· 167 157 168 158 export function usePreferencesSetAdultContentMutation() { 169 159 const queryClient = useQueryClient() 170 - const agent = useAgent() 171 160 172 161 return useMutation<void, unknown, {enabled: boolean}>({ 173 162 mutationFn: async ({enabled}) => { 174 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 175 - await agent.setAdultContentEnabled(enabled) 176 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 163 + const appviewAgent = getBskyAppviewAgent() 164 + await appviewAgent.setAdultContentEnabled(enabled) 177 165 // triggers a refetch 178 166 await queryClient.invalidateQueries({ 179 167 queryKey: preferencesQueryKey, ··· 184 172 185 173 export function usePreferencesSetBirthDateMutation() { 186 174 const queryClient = useQueryClient() 187 - const agent = useAgent() 188 175 189 176 return useMutation<void, unknown, {birthDate: Date}>({ 190 177 mutationFn: async ({birthDate}: {birthDate: Date}) => { 191 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 192 - await agent.setPersonalDetails({birthDate: birthDate.toISOString()}) 193 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 178 + const appviewAgent = getBskyAppviewAgent() 179 + await appviewAgent.setPersonalDetails({ 180 + birthDate: birthDate.toISOString(), 181 + }) 194 182 // triggers a refetch 195 183 await queryClient.invalidateQueries({ 196 184 queryKey: preferencesQueryKey, ··· 201 189 202 190 export function useSetFeedViewPreferencesMutation() { 203 191 const queryClient = useQueryClient() 204 - const agent = useAgent() 205 192 206 193 return useMutation<void, unknown, Partial<BskyFeedViewPreference>>({ 207 194 mutationFn: async prefs => { ··· 209 196 * special handling here, merged into `feedViewPrefs` above, since 210 197 * following was previously called `home` 211 198 */ 212 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 213 - await agent.setFeedViewPrefs('home', prefs) 214 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 199 + const appviewAgent = getBskyAppviewAgent() 200 + await appviewAgent.setFeedViewPrefs('home', prefs) 215 201 // triggers a refetch 216 202 await queryClient.invalidateQueries({ 217 203 queryKey: preferencesQueryKey, ··· 222 208 223 209 export function useSetThreadViewPreferencesMutation() { 224 210 const queryClient = useQueryClient() 225 - const agent = useAgent() 226 211 227 212 return useMutation<void, unknown, Partial<ThreadViewPreferences>>({ 228 213 mutationFn: async prefs => { 229 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 230 - await agent.setThreadViewPrefs(prefs) 231 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 214 + const appviewAgent = getBskyAppviewAgent() 215 + await appviewAgent.setThreadViewPrefs(prefs) 232 216 // triggers a refetch 233 217 await queryClient.invalidateQueries({ 234 218 queryKey: preferencesQueryKey, ··· 239 223 240 224 export function useOverwriteSavedFeedsMutation() { 241 225 const queryClient = useQueryClient() 242 - const agent = useAgent() 243 226 244 227 return useMutation<void, unknown, AppBskyActorDefs.SavedFeed[]>({ 245 228 mutationFn: async savedFeeds => { 246 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 247 - await agent.overwriteSavedFeeds(savedFeeds) 248 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 229 + const appviewAgent = getBskyAppviewAgent() 230 + await appviewAgent.overwriteSavedFeeds(savedFeeds) 249 231 // triggers a refetch 250 232 await queryClient.invalidateQueries({ 251 233 queryKey: preferencesQueryKey, ··· 256 238 257 239 export function useAddSavedFeedsMutation() { 258 240 const queryClient = useQueryClient() 259 - const agent = useAgent() 260 241 261 242 return useMutation< 262 243 void, ··· 264 245 Pick<AppBskyActorDefs.SavedFeed, 'type' | 'value' | 'pinned'>[] 265 246 >({ 266 247 mutationFn: async savedFeeds => { 267 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 268 - await agent.addSavedFeeds(savedFeeds) 269 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 248 + const appviewAgent = getBskyAppviewAgent() 249 + await appviewAgent.addSavedFeeds(savedFeeds) 270 250 // triggers a refetch 271 251 await queryClient.invalidateQueries({ 272 252 queryKey: preferencesQueryKey, ··· 277 257 278 258 export function useRemoveFeedMutation() { 279 259 const queryClient = useQueryClient() 280 - const agent = useAgent() 281 260 282 261 return useMutation<void, unknown, Pick<AppBskyActorDefs.SavedFeed, 'id'>>({ 283 262 mutationFn: async savedFeed => { 284 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 285 - await agent.removeSavedFeeds([savedFeed.id]) 286 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 263 + const appviewAgent = getBskyAppviewAgent() 264 + await appviewAgent.removeSavedFeeds([savedFeed.id]) 287 265 // triggers a refetch 288 266 await queryClient.invalidateQueries({ 289 267 queryKey: preferencesQueryKey, ··· 294 272 295 273 export function useReplaceForYouWithDiscoverFeedMutation() { 296 274 const queryClient = useQueryClient() 297 - const agent = useAgent() 298 275 299 276 return useMutation({ 300 277 mutationFn: async ({ ··· 304 281 forYouFeedConfig: AppBskyActorDefs.SavedFeed | undefined 305 282 discoverFeedConfig: AppBskyActorDefs.SavedFeed | undefined 306 283 }) => { 307 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 284 + const appviewAgent = getBskyAppviewAgent() 308 285 if (forYouFeedConfig) { 309 - await agent.removeSavedFeeds([forYouFeedConfig.id]) 286 + await appviewAgent.removeSavedFeeds([forYouFeedConfig.id]) 310 287 } 311 288 if (!discoverFeedConfig) { 312 - await agent.addSavedFeeds([ 289 + await appviewAgent.addSavedFeeds([ 313 290 { 314 291 type: 'feed', 315 292 value: PROD_DEFAULT_FEED('Discover'), ··· 317 294 }, 318 295 ]) 319 296 } else { 320 - await agent.updateSavedFeeds([ 297 + await appviewAgent.updateSavedFeeds([ 321 298 { 322 299 ...discoverFeedConfig, 323 300 pinned: true, 324 301 }, 325 302 ]) 326 303 } 327 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 328 304 // triggers a refetch 329 305 await queryClient.invalidateQueries({ 330 306 queryKey: preferencesQueryKey, ··· 335 311 336 312 export function useUpdateSavedFeedsMutation() { 337 313 const queryClient = useQueryClient() 338 - const agent = useAgent() 339 314 340 315 return useMutation<void, unknown, AppBskyActorDefs.SavedFeed[]>({ 341 316 mutationFn: async feeds => { 342 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 343 - await agent.updateSavedFeeds(feeds) 344 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 317 + const appviewAgent = getBskyAppviewAgent() 318 + await appviewAgent.updateSavedFeeds(feeds) 345 319 346 320 // triggers a refetch 347 321 await queryClient.invalidateQueries({ ··· 353 327 354 328 export function useUpsertMutedWordsMutation() { 355 329 const queryClient = useQueryClient() 356 - const agent = useAgent() 357 330 358 331 return useMutation({ 359 332 mutationFn: async (mutedWords: AppBskyActorDefs.MutedWord[]) => { 360 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 361 - await agent.upsertMutedWords(mutedWords) 362 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 333 + const appviewAgent = getBskyAppviewAgent() 334 + await appviewAgent.upsertMutedWords(mutedWords) 363 335 // triggers a refetch 364 336 await queryClient.invalidateQueries({ 365 337 queryKey: preferencesQueryKey, ··· 370 342 371 343 export function useUpdateMutedWordMutation() { 372 344 const queryClient = useQueryClient() 373 - const agent = useAgent() 374 345 375 346 return useMutation({ 376 347 mutationFn: async (mutedWord: AppBskyActorDefs.MutedWord) => { 377 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 378 - await agent.updateMutedWord(mutedWord) 379 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 348 + const appviewAgent = getBskyAppviewAgent() 349 + await appviewAgent.updateMutedWord(mutedWord) 380 350 // triggers a refetch 381 351 await queryClient.invalidateQueries({ 382 352 queryKey: preferencesQueryKey, ··· 387 357 388 358 export function useRemoveMutedWordMutation() { 389 359 const queryClient = useQueryClient() 390 - const agent = useAgent() 391 360 392 361 return useMutation({ 393 362 mutationFn: async (mutedWord: AppBskyActorDefs.MutedWord) => { 394 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 395 - await agent.removeMutedWord(mutedWord) 396 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 363 + const appviewAgent = getBskyAppviewAgent() 364 + await appviewAgent.removeMutedWord(mutedWord) 397 365 // triggers a refetch 398 366 await queryClient.invalidateQueries({ 399 367 queryKey: preferencesQueryKey, ··· 404 372 405 373 export function useRemoveMutedWordsMutation() { 406 374 const queryClient = useQueryClient() 407 - const agent = useAgent() 408 375 409 376 return useMutation({ 410 377 mutationFn: async (mutedWords: AppBskyActorDefs.MutedWord[]) => { 411 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 412 - await agent.removeMutedWords(mutedWords) 413 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 378 + const appviewAgent = getBskyAppviewAgent() 379 + await appviewAgent.removeMutedWords(mutedWords) 414 380 // triggers a refetch 415 381 await queryClient.invalidateQueries({ 416 382 queryKey: preferencesQueryKey, ··· 421 387 422 388 export function useQueueNudgesMutation() { 423 389 const queryClient = useQueryClient() 424 - const agent = useAgent() 425 390 426 391 return useMutation({ 427 392 mutationFn: async (nudges: string | string[]) => { 428 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 429 - await agent.bskyAppQueueNudges(nudges) 430 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 393 + const appviewAgent = getBskyAppviewAgent() 394 + await appviewAgent.bskyAppQueueNudges(nudges) 431 395 // triggers a refetch 432 396 await queryClient.invalidateQueries({ 433 397 queryKey: preferencesQueryKey, ··· 438 402 439 403 export function useDismissNudgesMutation() { 440 404 const queryClient = useQueryClient() 441 - const agent = useAgent() 442 405 443 406 return useMutation({ 444 407 mutationFn: async (nudges: string | string[]) => { 445 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 446 - await agent.bskyAppDismissNudges(nudges) 447 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 408 + const appviewAgent = getBskyAppviewAgent() 409 + await appviewAgent.bskyAppDismissNudges(nudges) 448 410 // triggers a refetch 449 411 await queryClient.invalidateQueries({ 450 412 queryKey: preferencesQueryKey, ··· 455 417 456 418 export function useSetActiveProgressGuideMutation() { 457 419 const queryClient = useQueryClient() 458 - const agent = useAgent() 459 420 460 421 return useMutation({ 461 422 mutationFn: async ( 462 423 guide: AppBskyActorDefs.BskyAppProgressGuide | undefined, 463 424 ) => { 464 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 465 - await agent.bskyAppSetActiveProgressGuide(guide) 466 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 425 + const appviewAgent = getBskyAppviewAgent() 426 + await appviewAgent.bskyAppSetActiveProgressGuide(guide) 467 427 // triggers a refetch 468 428 await queryClient.invalidateQueries({ 469 429 queryKey: preferencesQueryKey, ··· 474 434 475 435 export function useSetVerificationPrefsMutation() { 476 436 const queryClient = useQueryClient() 477 - const agent = useAgent() 478 437 479 438 return useMutation<void, unknown, AppBskyActorDefs.VerificationPrefs>({ 480 439 mutationFn: async prefs => { 481 - agent.configureProxy(PBLLC_BLUESKY_PROXY_HEADER.get()) 482 - await agent.setVerificationPrefs(prefs) 483 - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) 440 + const appviewAgent = getBskyAppviewAgent() 441 + await appviewAgent.setVerificationPrefs(prefs) 484 442 if (prefs.hideBadges) { 485 443 logger.metric('verification:settings:hideBadges', {}, {statsig: true}) 486 444 } else {
+27
src/state/session/index.tsx
··· 14 14 createAgentAndResume, 15 15 sessionAccountToSession, 16 16 } from './agent' 17 + import {getBskyAppviewAgent} from './appview-agent' 17 18 import {type Action, getInitialState, reducer, type State} from './reducer' 18 19 19 20 export {isSignupQueued} from './util' ··· 122 123 if (signal.aborted) { 123 124 return 124 125 } 126 + 127 + // Initialize the appview agent with the account 128 + getBskyAppviewAgent().initializeWithAccount(account) 129 + 125 130 store.dispatch({ 126 131 type: 'switched-to-account', 127 132 newAgent: agent, ··· 145 150 if (signal.aborted) { 146 151 return 147 152 } 153 + 154 + // Initialize the appview agent with the account 155 + getBskyAppviewAgent().initializeWithAccount(account) 156 + 148 157 store.dispatch({ 149 158 type: 'switched-to-account', 150 159 newAgent: agent, ··· 166 175 logContext => { 167 176 addSessionDebugLog({type: 'method:start', method: 'logout'}) 168 177 cancelPendingTask() 178 + 179 + // Clear the appview agent session 180 + getBskyAppviewAgent().clearSession() 181 + 169 182 store.dispatch({ 170 183 type: 'logged-out-current-account', 171 184 }) ··· 185 198 logContext => { 186 199 addSessionDebugLog({type: 'method:start', method: 'logout'}) 187 200 cancelPendingTask() 201 + 202 + // Clear the appview agent session 203 + getBskyAppviewAgent().clearSession() 204 + 188 205 store.dispatch({ 189 206 type: 'logged-out-every-account', 190 207 }) ··· 214 231 if (signal.aborted) { 215 232 return 216 233 } 234 + 235 + // Initialize the appview agent with the account 236 + getBskyAppviewAgent().initializeWithAccount(account) 237 + 217 238 store.dispatch({ 218 239 type: 'switched-to-account', 219 240 newAgent: agent, ··· 276 297 const agent = state.currentAgentState.agent as BskyAgent 277 298 const prevSession = agent.session 278 299 agent.sessionManager.session = sessionAccountToSession(syncedAccount) 300 + 301 + // Also update the appview agent with the current session 302 + getBskyAppviewAgent().initializeWithAccount(syncedAccount) 303 + 279 304 addSessionDebugLog({ 280 305 type: 'agent:patch', 281 306 agent, ··· 390 415 } 391 416 return agent 392 417 } 418 + 419 + // No need to export anything, getAppviewAgent is already imported directly where needed