Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cb6e65e3135d9c92b953839de79fe9d9ad90b5b8 150 lines 4.5 kB view raw
1import {useCallback, useEffect, useRef} from 'react' 2import * as Location from 'expo-location' 3import {createPermissionHook} from 'expo-modules-core' 4 5import {isNative} from '#/platform/detection' 6import * as debug from '#/geolocation/debug' 7import {logger} from '#/geolocation/logger' 8import {type Geolocation} from '#/geolocation/types' 9import {normalizeDeviceLocation} from '#/geolocation/util' 10import {device} from '#/storage' 11 12/** 13 * Location.useForegroundPermissions on web just errors if the 14 * navigator.permissions API is not available. We need to catch and ignore it, 15 * since it's effectively denied. 16 * 17 * @see https://github.com/expo/expo/blob/72f1562ed9cce5ff6dfe04aa415b71632a3d4b87/packages/expo-location/src/Location.ts#L290-L293 18 */ 19const useForegroundPermissions = createPermissionHook({ 20 getMethod: () => 21 Location.getForegroundPermissionsAsync().catch(error => { 22 logger.debug( 23 'useForegroundPermission: error getting location permissions', 24 {safeMessage: error}, 25 ) 26 return { 27 status: Location.PermissionStatus.DENIED, 28 granted: false, 29 canAskAgain: false, 30 expires: 0, 31 } 32 }), 33 requestMethod: () => 34 Location.requestForegroundPermissionsAsync().catch(error => { 35 logger.debug( 36 'useForegroundPermission: error requesting location permissions', 37 {safeMessage: error}, 38 ) 39 return { 40 status: Location.PermissionStatus.DENIED, 41 granted: false, 42 canAskAgain: false, 43 expires: 0, 44 } 45 }), 46}) 47 48export async function getDeviceGeolocation(): Promise<Geolocation> { 49 if (debug.enabled && debug.deviceGeolocation) 50 return debug.resolve(debug.deviceGeolocation) 51 52 try { 53 const geocode = await Location.getCurrentPositionAsync() 54 const locations = await Location.reverseGeocodeAsync({ 55 latitude: geocode.coords.latitude, 56 longitude: geocode.coords.longitude, 57 }) 58 const location = locations.at(0) 59 const normalized = location ? normalizeDeviceLocation(location) : undefined 60 return { 61 countryCode: normalized?.countryCode ?? undefined, 62 regionCode: normalized?.regionCode ?? undefined, 63 } 64 } catch (e) { 65 logger.error('getDeviceGeolocation: failed', {safeMessage: e}) 66 return { 67 countryCode: undefined, 68 regionCode: undefined, 69 } 70 } 71} 72 73export function useRequestDeviceGeolocation(): () => Promise< 74 | { 75 granted: true 76 location: Geolocation | undefined 77 } 78 | { 79 granted: false 80 } 81> { 82 return useCallback(async () => { 83 const status = await Location.requestForegroundPermissionsAsync() 84 if (status.granted) { 85 return { 86 granted: true, 87 location: await getDeviceGeolocation(), 88 } 89 } else { 90 return { 91 granted: false, 92 } 93 } 94 }, []) 95} 96 97/** 98 * Hook to get and sync the device geolocation from the device GPS and store it 99 * using device storage. If permissions are not granted, it will clear any cached 100 * storage value. 101 */ 102export function useSyncDeviceGeolocationOnStartup( 103 sync: (location: Geolocation | undefined) => void, 104) { 105 const synced = useRef(false) 106 const [status] = useForegroundPermissions() 107 useEffect(() => { 108 if (!isNative) return 109 110 async function get() { 111 // no need to set this more than once per session 112 if (synced.current) return 113 logger.debug('useSyncDeviceGeolocationOnStartup: checking perms') 114 if (status?.granted) { 115 const location = await getDeviceGeolocation() 116 if (location) { 117 logger.debug('useSyncDeviceGeolocationOnStartup: got location') 118 sync(location) 119 synced.current = true 120 } 121 } else { 122 const hasCachedValue = device.get(['deviceGeolocation']) !== undefined 123 /** 124 * If we have a cached value, but user has revoked permissions, 125 * quietly (will take effect lazily) clear this out. 126 */ 127 if (hasCachedValue) { 128 logger.debug( 129 'useSyncDeviceGeolocationOnStartup: clearing cached location, perms revoked', 130 ) 131 device.set(['deviceGeolocation'], undefined) 132 } 133 } 134 } 135 136 get().catch(e => { 137 logger.error( 138 'useSyncDeviceGeolocationOnStartup: failed to get location', 139 { 140 safeMessage: e, 141 }, 142 ) 143 }) 144 }, [status, sync]) 145} 146 147export function useIsDeviceGeolocationGranted() { 148 const [status] = useForegroundPermissions() 149 return status?.granted === true 150}