forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {useEffect, useState} from 'react'
2import EventEmitter from 'eventemitter3'
3
4import {FALLBACK_GEOLOCATION_SERVICE_RESPONSE} from '#/geolocation/const'
5import * as debug from '#/geolocation/debug'
6import {logger} from '#/geolocation/logger'
7import {type Geolocation} from '#/geolocation/types'
8import {device} from '#/storage'
9
10const geolocationData = FALLBACK_GEOLOCATION_SERVICE_RESPONSE
11const events = new EventEmitter()
12const EVENT = 'geolocation-service-response-updated'
13const emitGeolocationServiceResponseUpdate = (data: Geolocation) => {
14 events.emit(EVENT, data)
15}
16const onGeolocationServiceResponseUpdate = (
17 listener: (data: Geolocation) => void,
18) => {
19 events.on(EVENT, listener)
20 return () => {
21 events.off(EVENT, listener)
22 }
23}
24
25async 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/**
32 * Local promise used within this file only.
33 */
34let geolocationServicePromise: Promise<{success: boolean}> | undefined
35
36/**
37 * Begin the process of resolving geolocation config. This is called right away
38 * at app start, and the promise is awaited later before proceeding with app
39 * startup.
40 */
41export async function resolve() {
42 if (geolocationServicePromise) {
43 const cached = device.get(['geolocationServiceResponse'])
44 if (cached) {
45 logger.debug(`resolve(): using cache`)
46 } else {
47 logger.debug(`resolve(): no cache`)
48 const {success} = await geolocationServicePromise
49 if (success) {
50 logger.debug(`resolve(): resolved`)
51 } else {
52 logger.info(`resolve(): failed`)
53 }
54 }
55 } else {
56 logger.debug(`resolve(): initiating`)
57
58 geolocationServicePromise = new Promise(async resolvePromise => {
59 let success = false
60
61 function cacheResponseOrThrow(response: Geolocation | undefined) {
62 if (response) {
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 }
82 })
83 }
84}
85
86export function useGeolocationServiceResponse() {
87 const [config, setConfig] = useState(() => {
88 const initial =
89 device.get(['geolocationServiceResponse']) ||
90 FALLBACK_GEOLOCATION_SERVICE_RESPONSE
91 return initial
92 })
93
94 useEffect(() => {
95 return onGeolocationServiceResponseUpdate(config => {
96 setConfig(config)
97 })
98 }, [])
99
100 return config
101}