···3 type AppBskyAgeassuranceDefs,
4 type AppBskyAgeassuranceGetConfig,
5 type AppBskyAgeassuranceGetState,
6- AtpAgent,
7 getAgeAssuranceRegionConfig,
8} from '@atproto/api'
9import AsyncStorage from '@react-native-async-storage/async-storage'
···13import debounce from 'lodash.debounce'
1415import {networkRetry} from '#/lib/async/retry'
16-import {PUBLIC_BSKY_SERVICE} from '#/lib/constants'
17import {getAge} from '#/lib/strings/time'
18import {
19 hasSnoozedBirthdateUpdateForDid,
···91export const configQueryKey = ['config']
92export async function getConfig() {
93 if (debug.enabled) return debug.resolve(debug.config)
94- const agent = new AtpAgent({
95- service: PUBLIC_BSKY_SERVICE,
96- })
97- const res = await agent.app.bsky.ageassurance.getConfig()
98- return res.data
99}
100export function getConfigFromCache():
101 | AppBskyAgeassuranceGetConfig.OutputSchema
···179export function createServerStateQueryKey({did}: {did: string}) {
180 return ['serverState', did]
181}
182-export async function getServerState({agent}: {agent: AtpAgent}) {
183 if (debug.enabled && debug.serverState)
184 return debug.resolve(debug.serverState)
185- const geolocation = device.get(['mergedGeolocation'])
186- if (!geolocation || !geolocation.countryCode) {
187- logger.error(`getServerState: missing geolocation countryCode`)
188- return
00000189 }
190- const {data} = await agent.app.bsky.ageassurance.getState({
191- countryCode: geolocation.countryCode,
192- regionCode: geolocation.regionCode,
193- })
194- const did = getDidFromAgentSession(agent)
195- if (data && did && createdAtCache.has(did)) {
196- /*
197- * If account was just created, just use the local cache if available. On
198- * subsequent reloads, the server should have the correct value.
199- */
200- data.metadata.accountCreatedAt = createdAtCache.get(did)
201- }
202- return data ?? null
203}
204export function getServerStateFromCache({
205 did,
···226227 try {
228 logger.debug(`prefetchServerState: resolving...`)
229- const res = await networkRetry(3, () => getServerState({agent}))
230 qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(qk, res)
231 } catch (e: any) {
232 logger.warn(`prefetchServerState: failed`, {
···238 const did = getDidFromAgentSession(agent)
239 if (!did) return
240 logger.debug(`refetchServerState: fetching...`)
241- const res = await networkRetry(3, () => getServerState({agent}))
242 qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(
243 createServerStateQueryKey({did}),
244 res,
···277 },
278 queryKey: createServerStateQueryKey({did: did!}),
279 async queryFn() {
280- return getServerState({agent})
281 },
282 },
283 qc,
···3 type AppBskyAgeassuranceDefs,
4 type AppBskyAgeassuranceGetConfig,
5 type AppBskyAgeassuranceGetState,
6+ type AtpAgent,
7 getAgeAssuranceRegionConfig,
8} from '@atproto/api'
9import AsyncStorage from '@react-native-async-storage/async-storage'
···13import debounce from 'lodash.debounce'
1415import {networkRetry} from '#/lib/async/retry'
016import {getAge} from '#/lib/strings/time'
17import {
18 hasSnoozedBirthdateUpdateForDid,
···90export const configQueryKey = ['config']
91export async function getConfig() {
92 if (debug.enabled) return debug.resolve(debug.config)
93+ return {
94+ regions: [],
95+ }
0096}
97export function getConfigFromCache():
98 | AppBskyAgeassuranceGetConfig.OutputSchema
···176export function createServerStateQueryKey({did}: {did: string}) {
177 return ['serverState', did]
178}
179+export async function getServerState() {
180 if (debug.enabled && debug.serverState)
181 return debug.resolve(debug.serverState)
182+ return {
183+ state: {
184+ lastInitiatedAt: '2025-07-14T14:22:43.912Z',
185+ status: 'assured' as const,
186+ access: 'full' as const,
187+ },
188+ metadata: {
189+ accountCreatedAt: '2022-11-17T00:35:16.391Z',
190+ },
191 }
0000000000000192}
193export function getServerStateFromCache({
194 did,
···215216 try {
217 logger.debug(`prefetchServerState: resolving...`)
218+ const res = await networkRetry(3, () => getServerState())
219 qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(qk, res)
220 } catch (e: any) {
221 logger.warn(`prefetchServerState: failed`, {
···227 const did = getDidFromAgentSession(agent)
228 if (!did) return
229 logger.debug(`refetchServerState: fetching...`)
230+ const res = await networkRetry(3, () => getServerState())
231 qc.setQueryData<AppBskyAgeassuranceGetState.OutputSchema>(
232 createServerStateQueryKey({did}),
233 res,
···266 },
267 queryKey: createServerStateQueryKey({did: did!}),
268 async queryFn() {
269+ return getServerState()
270 },
271 },
272 qc,
+5-10
src/env/common.ts
···123 : Number(process.env.EXPO_PUBLIC_GCP_PROJECT_ID)
124125/**
126- * URLs for the app config web worker. Can be a
127- * locally running server, see `env.example` for more.
128- */
129-export const GEOLOCATION_DEV_URL = process.env.GEOLOCATION_DEV_URL
130-export const GEOLOCATION_PROD_URL = `https://ip.bsky.app`
131-export const GEOLOCATION_URL = IS_DEV
132- ? (GEOLOCATION_DEV_URL ?? GEOLOCATION_PROD_URL)
133- : GEOLOCATION_PROD_URL
134-135-/**
136 * URLs for the live-event config web worker. Can be a
137 * locally running server, see `env.example` for more.
138 */
00000
···123 : Number(process.env.EXPO_PUBLIC_GCP_PROJECT_ID)
124125/**
0000000000126 * URLs for the live-event config web worker. Can be a
127 * locally running server, see `env.example` for more.
128 */
129+export const LIVE_EVENTS_DEV_URL = process.env.LIVE_EVENTS_DEV_URL
130+export const LIVE_EVENTS_PROD_URL = `https://live-events.workers.bsky.app`
131+export const LIVE_EVENTS_URL = IS_DEV
132+ ? (LIVE_EVENTS_DEV_URL ?? LIVE_EVENTS_PROD_URL)
133+ : LIVE_EVENTS_PROD_URL
+1-2
src/geolocation/const.ts
···1-import {GEOLOCATION_URL} from '#/env'
2import {type Geolocation} from '#/geolocation/types'
34-export const GEOLOCATION_SERVICE_URL = `${GEOLOCATION_URL}/geolocation`
56/**
7 * Default geolocation config.
···01import {type Geolocation} from '#/geolocation/types'
23+export const GEOLOCATION_SERVICE_URL = '' // No longer needed
45/**
6 * Default geolocation config.
+10-45
src/geolocation/service.ts
···1import {useEffect, useState} from 'react'
2import EventEmitter from 'eventemitter3'
34-import {networkRetry} from '#/lib/async/retry'
5-import {
6- FALLBACK_GEOLOCATION_SERVICE_RESPONSE,
7- GEOLOCATION_SERVICE_URL,
8-} from '#/geolocation/const'
9import * as debug from '#/geolocation/debug'
10import {logger} from '#/geolocation/logger'
11import {type Geolocation} from '#/geolocation/types'
12import {device} from '#/storage'
13014const events = new EventEmitter()
15const EVENT = 'geolocation-service-response-updated'
16const emitGeolocationServiceResponseUpdate = (data: Geolocation) => {
···25 }
26}
2728-async function fetchGeolocationServiceData(
29- url: string,
30-): Promise<Geolocation | undefined> {
31 if (debug.enabled) return debug.resolve(debug.geolocation)
32- const res = await fetch(url)
33- if (!res.ok) {
34- throw new Error(`fetchGeolocationServiceData failed ${res.status}`)
35- }
36- return res.json() as Promise<Geolocation>
37}
3839/**
···63 } else {
64 logger.debug(`resolve(): initiating`)
6566- /**
67- * THIS PROMISE SHOULD NEVER `reject()`! We want the app to proceed with
68- * startup, even if geolocation resolution fails.
69- */
70 geolocationServicePromise = new Promise(async resolvePromise => {
71 let success = false
72···75 device.set(['geolocationServiceResponse'], response)
76 emitGeolocationServiceResponseUpdate(response)
77 } else {
78- // endpoint should throw on all failures, this is insurance
79 throw new Error(`fetchGeolocationServiceData returned no data`)
80 }
81 }
8283 try {
84- // Try once, fail fast
85- const config = await fetchGeolocationServiceData(
86- GEOLOCATION_SERVICE_URL,
87- )
88 cacheResponseOrThrow(config)
89 success = true
90 } catch (e: any) {
91- logger.debug(
92- `resolve(): fetchGeolocationServiceData failed initial request`,
93- {
94- safeMessage: e.message,
95- },
96- )
97-98- // retry 3 times, but don't await, proceed with default
99- networkRetry(3, () =>
100- fetchGeolocationServiceData(GEOLOCATION_SERVICE_URL),
101- )
102- .then(config => {
103- cacheResponseOrThrow(config)
104- })
105- .catch((err: any) => {
106- // complete fail closed
107- logger.debug(
108- `resolve(): fetchGeolocationServiceData failed retries`,
109- {
110- safeMessage: err.message,
111- },
112- )
113- })
114 } finally {
115 resolvePromise({success})
116 }
···1import {useEffect, useState} from 'react'
2import EventEmitter from 'eventemitter3'
34+import {FALLBACK_GEOLOCATION_SERVICE_RESPONSE} from '#/geolocation/const'
00005import * as debug from '#/geolocation/debug'
6import {logger} from '#/geolocation/logger'
7import {type Geolocation} from '#/geolocation/types'
8import {device} from '#/storage'
910+const geolocationData = FALLBACK_GEOLOCATION_SERVICE_RESPONSE
11const events = new EventEmitter()
12const EVENT = 'geolocation-service-response-updated'
13const emitGeolocationServiceResponseUpdate = (data: Geolocation) => {
···22 }
23}
2425+async function fetchGeolocationServiceData(): Promise<Geolocation | undefined> {
0026 if (debug.enabled) return debug.resolve(debug.geolocation)
27+ // Return local geolocation data instead of making HTTP request
28+ return geolocationData as Geolocation
00029}
3031/**
···55 } else {
56 logger.debug(`resolve(): initiating`)
57000058 geolocationServicePromise = new Promise(async resolvePromise => {
59 let success = false
60···63 device.set(['geolocationServiceResponse'], response)
64 emitGeolocationServiceResponseUpdate(response)
65 } else {
066 throw new Error(`fetchGeolocationServiceData returned no data`)
67 }
68 }
6970 try {
71+ // Use local data - no need to retry or handle network errors
72+ const config = await fetchGeolocationServiceData()
0073 cacheResponseOrThrow(config)
74 success = true
75 } catch (e: any) {
76+ logger.debug(`resolve(): fetchGeolocationServiceData failed`, {
77+ safeMessage: e.message,
78+ })
0000000000000000000079 } finally {
80 resolvePromise({success})
81 }