Bluesky app fork with some witchin' additions 💫

Quiet some logs, fix a bug (#8404)

* Composer, 142k

* Log geolocation error at most once per session

* Clean thumb cache, 1.4m

* Quiet generic network errors

* Handle undefined notification payloads

authored by

Eric Bailey and committed by
GitHub
342f820e df2f62e9

+45 -13
+14 -1
src/lib/hooks/useNotificationHandler.ts
··· 26 | 'chat-message' 27 | 'starterpack-joined' 28 29 type NotificationPayload = 30 | { 31 reason: Exclude<NotificationReason, 'chat-message'> 32 uri: string ··· 47 } satisfies Notifications.NotificationBehavior 48 49 // These need to stay outside the hook to persist between account switches 50 - let storedPayload: NotificationPayload | undefined 51 let prevDate = 0 52 53 const logger = Logger.create(Logger.Context.Notifications) ··· 191 logger.debug('Notifications: received', {e}) 192 193 const payload = e.request.trigger.payload as NotificationPayload 194 if ( 195 payload.reason === 'chat-message' && 196 payload.recipientDid === currentAccount?.did ··· 230 ) { 231 const payload = e.notification.request.trigger 232 .payload as NotificationPayload 233 234 logger.debug( 235 'User pressed a notification, opening notifications tab',
··· 26 | 'chat-message' 27 | 'starterpack-joined' 28 29 + /** 30 + * Manually overridden type, but retains the possibility of 31 + * `notification.request.trigger.payload` being `undefined`, as specified in 32 + * the source types. 33 + */ 34 type NotificationPayload = 35 + | undefined 36 | { 37 reason: Exclude<NotificationReason, 'chat-message'> 38 uri: string ··· 53 } satisfies Notifications.NotificationBehavior 54 55 // These need to stay outside the hook to persist between account switches 56 + let storedPayload: NotificationPayload 57 let prevDate = 0 58 59 const logger = Logger.create(Logger.Context.Notifications) ··· 197 logger.debug('Notifications: received', {e}) 198 199 const payload = e.request.trigger.payload as NotificationPayload 200 + 201 + if (!payload) { 202 + return DEFAULT_HANDLER_OPTIONS 203 + } 204 + 205 if ( 206 payload.reason === 'chat-message' && 207 payload.recipientDid === currentAccount?.did ··· 241 ) { 242 const payload = e.notification.request.trigger 243 .payload as NotificationPayload 244 + 245 + if (!payload) return 246 247 logger.debug( 248 'User pressed a notification, opening notifications tab',
+11 -1
src/logger/sentry/setup/index.ts
··· 27 environment: process.env.NODE_ENV, 28 dist, 29 release, 30 - ignoreErrors: [`t is not defined`, `Can't find variable: t`], 31 })
··· 27 environment: process.env.NODE_ENV, 28 dist, 29 release, 30 + ignoreErrors: [ 31 + /* 32 + * Unknown internals errors 33 + */ 34 + `t is not defined`, 35 + `Can't find variable: t`, 36 + /* 37 + * Un-useful errors 38 + */ 39 + `Network request failed`, 40 + ], 41 })
+17 -8
src/state/geolocation.tsx
··· 48 /** 49 * Local promise used within this file only. 50 */ 51 - let geolocationResolution: Promise<void> | undefined 52 53 /** 54 * Begin the process of resolving geolocation. This should be called once at ··· 65 * and fail closed. 66 */ 67 if (__DEV__) { 68 - geolocationResolution = new Promise(y => y()) 69 device.set(['geolocation'], DEFAULT_GEOLOCATION) 70 return 71 } 72 73 geolocationResolution = new Promise(async resolve => { 74 try { 75 // Try once, fail fast 76 const geolocation = await getGeolocation() ··· 83 throw new Error(`geolocation: nothing returned from initial request`) 84 } 85 } catch (e: any) { 86 - logger.error(`geolocation: failed initial request`, { 87 safeMessage: e.message, 88 }) 89 ··· 97 device.set(['geolocation'], geolocation) 98 emitGeolocationUpdate(geolocation) 99 logger.debug(`geolocation: success`, {geolocation}) 100 } else { 101 // endpoint should throw on all failures, this is insurance 102 throw new Error(`geolocation: nothing returned from retries`) ··· 107 logger.debug(`geolocation: failed retries`, {safeMessage: e.message}) 108 }) 109 } finally { 110 - resolve(undefined) 111 } 112 }) 113 } ··· 127 logger.debug(`geolocation: using cache`, {cached}) 128 } else { 129 logger.debug(`geolocation: no cache`) 130 - await geolocationResolution 131 - logger.debug(`geolocation: resolved`, { 132 - resolved: device.get(['geolocation']), 133 - }) 134 } 135 } 136
··· 48 /** 49 * Local promise used within this file only. 50 */ 51 + let geolocationResolution: Promise<{success: boolean}> | undefined 52 53 /** 54 * Begin the process of resolving geolocation. This should be called once at ··· 65 * and fail closed. 66 */ 67 if (__DEV__) { 68 + geolocationResolution = new Promise(y => y({success: true})) 69 device.set(['geolocation'], DEFAULT_GEOLOCATION) 70 return 71 } 72 73 geolocationResolution = new Promise(async resolve => { 74 + let success = true 75 + 76 try { 77 // Try once, fail fast 78 const geolocation = await getGeolocation() ··· 85 throw new Error(`geolocation: nothing returned from initial request`) 86 } 87 } catch (e: any) { 88 + success = false 89 + 90 + logger.debug(`geolocation: failed initial request`, { 91 safeMessage: e.message, 92 }) 93 ··· 101 device.set(['geolocation'], geolocation) 102 emitGeolocationUpdate(geolocation) 103 logger.debug(`geolocation: success`, {geolocation}) 104 + success = true 105 } else { 106 // endpoint should throw on all failures, this is insurance 107 throw new Error(`geolocation: nothing returned from retries`) ··· 112 logger.debug(`geolocation: failed retries`, {safeMessage: e.message}) 113 }) 114 } finally { 115 + resolve({success}) 116 } 117 }) 118 } ··· 132 logger.debug(`geolocation: using cache`, {cached}) 133 } else { 134 logger.debug(`geolocation: no cache`) 135 + const {success} = await geolocationResolution 136 + if (success) { 137 + logger.debug(`geolocation: resolved`, { 138 + resolved: device.get(['geolocation']), 139 + }) 140 + } else { 141 + logger.error(`geolocation: failed to resolve`) 142 + } 143 } 144 } 145
+1 -1
src/view/com/composer/Composer.tsx
··· 400 ).uris[0] 401 try { 402 await whenAppViewReady(agent, postUri, res => { 403 - const postedThread = res.data.thread 404 return AppBskyFeedDefs.isThreadViewPost(postedThread) 405 }) 406 } catch (waitErr: any) {
··· 400 ).uris[0] 401 try { 402 await whenAppViewReady(agent, postUri, res => { 403 + const postedThread = res?.data?.thread 404 return AppBskyFeedDefs.isThreadViewPost(postedThread) 405 }) 406 } catch (waitErr: any) {
+2 -2
src/view/com/composer/videos/VideoTranscodeBackdrop.tsx
··· 1 import {clearCache, createVideoThumbnail} from 'react-native-compressor' 2 import Animated, {FadeIn} from 'react-native-reanimated' 3 import {Image} from 'expo-image' 4 - import {QueryClient, useQuery} from '@tanstack/react-query' 5 6 import {atoms as a} from '#/alf' 7 8 export const RQKEY = 'video-thumbnail' 9 10 export function clearThumbnailCache(queryClient: QueryClient) { 11 - clearCache() 12 queryClient.resetQueries({queryKey: [RQKEY]}) 13 } 14
··· 1 import {clearCache, createVideoThumbnail} from 'react-native-compressor' 2 import Animated, {FadeIn} from 'react-native-reanimated' 3 import {Image} from 'expo-image' 4 + import {type QueryClient, useQuery} from '@tanstack/react-query' 5 6 import {atoms as a} from '#/alf' 7 8 export const RQKEY = 'video-thumbnail' 9 10 export function clearThumbnailCache(queryClient: QueryClient) { 11 + clearCache().catch(() => {}) 12 queryClient.resetQueries({queryKey: [RQKEY]}) 13 } 14