Bluesky app fork with some witchin' additions 💫

fix age assurance and geolocation breaking things

xan.lol 62334b9f 70b84206

verified
+33 -85
+17 -28
src/ageAssurance/data.tsx
··· 3 type AppBskyAgeassuranceDefs, 4 type AppBskyAgeassuranceGetConfig, 5 type AppBskyAgeassuranceGetState, 6 - AtpAgent, 7 getAgeAssuranceRegionConfig, 8 } from '@atproto/api' 9 import AsyncStorage from '@react-native-async-storage/async-storage' ··· 13 import debounce from 'lodash.debounce' 14 15 import {networkRetry} from '#/lib/async/retry' 16 - import {PUBLIC_BSKY_SERVICE} from '#/lib/constants' 17 import {getAge} from '#/lib/strings/time' 18 import { 19 hasSnoozedBirthdateUpdateForDid, ··· 91 export const configQueryKey = ['config'] 92 export 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 } 100 export function getConfigFromCache(): 101 | AppBskyAgeassuranceGetConfig.OutputSchema ··· 179 export 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 189 } 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 } 204 export function getServerStateFromCache({ 205 did, ··· 226 227 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' 9 import AsyncStorage from '@react-native-async-storage/async-storage' ··· 13 import debounce from 'lodash.debounce' 14 15 import {networkRetry} from '#/lib/async/retry' 16 import {getAge} from '#/lib/strings/time' 17 import { 18 hasSnoozedBirthdateUpdateForDid, ··· 90 export const configQueryKey = ['config'] 91 export async function getConfig() { 92 if (debug.enabled) return debug.resolve(debug.config) 93 + return { 94 + regions: [], 95 + } 96 } 97 export function getConfigFromCache(): 98 | AppBskyAgeassuranceGetConfig.OutputSchema ··· 176 export 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 } 192 } 193 export function getServerStateFromCache({ 194 did, ··· 215 216 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) 124 125 /** 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 */
··· 123 : Number(process.env.EXPO_PUBLIC_GCP_PROJECT_ID) 124 125 /** 126 * 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' 2 import {type Geolocation} from '#/geolocation/types' 3 4 - export const GEOLOCATION_SERVICE_URL = `${GEOLOCATION_URL}/geolocation` 5 6 /** 7 * Default geolocation config.
··· 1 import {type Geolocation} from '#/geolocation/types' 2 3 + export const GEOLOCATION_SERVICE_URL = '' // No longer needed 4 5 /** 6 * Default geolocation config.
+10 -45
src/geolocation/service.ts
··· 1 import {useEffect, useState} from 'react' 2 import EventEmitter from 'eventemitter3' 3 4 - import {networkRetry} from '#/lib/async/retry' 5 - import { 6 - FALLBACK_GEOLOCATION_SERVICE_RESPONSE, 7 - GEOLOCATION_SERVICE_URL, 8 - } from '#/geolocation/const' 9 import * as debug from '#/geolocation/debug' 10 import {logger} from '#/geolocation/logger' 11 import {type Geolocation} from '#/geolocation/types' 12 import {device} from '#/storage' 13 14 const events = new EventEmitter() 15 const EVENT = 'geolocation-service-response-updated' 16 const emitGeolocationServiceResponseUpdate = (data: Geolocation) => { ··· 25 } 26 } 27 28 - 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 } 38 39 /** ··· 63 } else { 64 logger.debug(`resolve(): initiating`) 65 66 - /** 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 } 82 83 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 }
··· 1 import {useEffect, useState} from 'react' 2 import EventEmitter from 'eventemitter3' 3 4 + import {FALLBACK_GEOLOCATION_SERVICE_RESPONSE} from '#/geolocation/const' 5 import * as debug from '#/geolocation/debug' 6 import {logger} from '#/geolocation/logger' 7 import {type Geolocation} from '#/geolocation/types' 8 import {device} from '#/storage' 9 10 + const geolocationData = FALLBACK_GEOLOCATION_SERVICE_RESPONSE 11 const events = new EventEmitter() 12 const EVENT = 'geolocation-service-response-updated' 13 const emitGeolocationServiceResponseUpdate = (data: Geolocation) => { ··· 22 } 23 } 24 25 + async function fetchGeolocationServiceData(): Promise<Geolocation | undefined> { 26 if (debug.enabled) return debug.resolve(debug.geolocation) 27 + // Return local geolocation data instead of making HTTP request 28 + return geolocationData as Geolocation 29 } 30 31 /** ··· 55 } else { 56 logger.debug(`resolve(): initiating`) 57 58 geolocationServicePromise = new Promise(async resolvePromise => { 59 let success = false 60 ··· 63 device.set(['geolocationServiceResponse'], response) 64 emitGeolocationServiceResponseUpdate(response) 65 } else { 66 throw new Error(`fetchGeolocationServiceData returned no data`) 67 } 68 } 69 70 try { 71 + // Use local data - no need to retry or handle network errors 72 + const config = await fetchGeolocationServiceData() 73 cacheResponseOrThrow(config) 74 success = true 75 } catch (e: any) { 76 + logger.debug(`resolve(): fetchGeolocationServiceData failed`, { 77 + safeMessage: e.message, 78 + }) 79 } finally { 80 resolvePromise({success}) 81 }