From c2a5b3efa8c2fa061cc5e86a5d3a3240fb0eba2c Mon Sep 17 00:00:00 2001 From: jcalabro Date: Thu, 10 Jul 2025 12:41:15 -0400 Subject: [PATCH 1/5] Detect Age Restricted Geos --- bskyweb/cmd/bskyweb/server.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 755f2fa3f..ce5910e99 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -603,7 +603,8 @@ type IPCCRequest struct { IP string `json:"ip"` } type IPCCResponse struct { - CC string `json:"countryCode"` + CC string `json:"countryCode"` + AgeRestrictedGeo string `json:"isAgeRestrictedGeo"` } func (srv *Server) WebIpCC(c echo.Context) error { -- 2.51.2 From 377d80e21df668c50585b1131bf1b7f0528bacd0 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 11 Jul 2025 00:36:53 +0300 Subject: [PATCH 2/5] Handle notifications on first load (#8629) * add getInitialURL * get deep linking working * actually handle as push instead * push to route instead of setting initial path * pop to on top of notifications * fix comment * change chat-message handling * add metrics * don't reopen due to notif multiple times * extract data from notification differently on android * sorry sorry annoying naming nits, align logger --------- Co-authored-by: Eric Bailey --- src/App.native.tsx | 2 +- src/Navigation.tsx | 146 +++++++++++--- src/lib/hooks/useNotificationHandler.ts | 250 +++++++++++++----------- src/lib/notifications/notifications.ts | 20 +- src/lib/notifications/util.ts | 3 + src/lib/routes/links.ts | 2 +- src/logger/metrics.ts | 1 + 7 files changed, 276 insertions(+), 148 deletions(-) create mode 100644 src/lib/notifications/util.ts diff --git a/src/App.native.tsx b/src/App.native.tsx index c42b11746..2278b73de 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -17,6 +17,7 @@ import {useLingui} from '@lingui/react' import * as Sentry from '@sentry/react-native' import {KeyboardControllerProvider} from '#/lib/hooks/useEnableKeyboardController' +import {Provider as HideBottomBarBorderProvider} from '#/lib/hooks/useHideBottomBarBorder' import {QueryProvider} from '#/lib/react-query' import {Provider as StatsigProvider, tryFetchGates} from '#/lib/statsig/statsig' import {s} from '#/lib/styles' @@ -73,7 +74,6 @@ import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbe import {Splash} from '#/Splash' import {BottomSheetProvider} from '../modules/bottom-sheet' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' -import {Provider as HideBottomBarBorderProvider} from './lib/hooks/useHideBottomBarBorder' SplashScreen.preventAutoHideAsync() if (isIOS) { diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 26a2b2a2a..e71148a2c 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -1,4 +1,5 @@ -import * as React from 'react' +import {useCallback, useRef} from 'react' +import * as Notifications from 'expo-notifications' import {i18n, type MessageDescriptor} from '@lingui/core' import {msg} from '@lingui/macro' import { @@ -10,13 +11,21 @@ import { createNavigationContainerRef, DarkTheme, DefaultTheme, + type LinkingOptions, NavigationContainer, StackActions, } from '@react-navigation/native' import {timeout} from '#/lib/async/timeout' import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' +import { + getNotificationPayload, + type NotificationPayload, + notificationToURL, + storePayloadForAccountSwitch, +} from '#/lib/hooks/useNotificationHandler' import {useWebScrollRestoration} from '#/lib/hooks/useWebScrollRestoration' +import {logger as notyLogger} from '#/lib/notifications/util' import {buildStateObject} from '#/lib/routes/helpers' import { type AllNavigatorParams, @@ -71,6 +80,7 @@ import {MessagesSettingsScreen} from '#/screens/Messages/Settings' import {ModerationScreen} from '#/screens/Moderation' import {Screen as ModerationVerificationSettings} from '#/screens/Moderation/VerificationSettings' import {Screen as ModerationInteractionSettings} from '#/screens/ModerationInteractionSettings' +import {NotificationsActivityListScreen} from '#/screens/Notifications/ActivityList' import {PostLikedByScreen} from '#/screens/Post/PostLikedBy' import {PostQuotesScreen} from '#/screens/Post/PostQuotes' import {PostRepostedByScreen} from '#/screens/Post/PostRepostedBy' @@ -93,6 +103,18 @@ import {ExternalMediaPreferencesScreen} from '#/screens/Settings/ExternalMediaPr import {FollowingFeedPreferencesScreen} from '#/screens/Settings/FollowingFeedPreferences' import {InterestsSettingsScreen} from '#/screens/Settings/InterestsSettings' import {LanguageSettingsScreen} from '#/screens/Settings/LanguageSettings' +import {LegacyNotificationSettingsScreen} from '#/screens/Settings/LegacyNotificationSettings' +import {NotificationSettingsScreen} from '#/screens/Settings/NotificationSettings' +import {ActivityNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/ActivityNotificationSettings' +import {LikeNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/LikeNotificationSettings' +import {LikesOnRepostsNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/LikesOnRepostsNotificationSettings' +import {MentionNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/MentionNotificationSettings' +import {MiscellaneousNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/MiscellaneousNotificationSettings' +import {NewFollowerNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/NewFollowerNotificationSettings' +import {QuoteNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/QuoteNotificationSettings' +import {ReplyNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/ReplyNotificationSettings' +import {RepostNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/RepostNotificationSettings' +import {RepostsOnRepostsNotificationSettingsScreen} from '#/screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings' import {PrivacyAndSecuritySettingsScreen} from '#/screens/Settings/PrivacyAndSecuritySettings' import {SettingsScreen} from '#/screens/Settings/Settings' import {ThreadPreferencesScreen} from '#/screens/Settings/ThreadPreferences' @@ -110,19 +132,10 @@ import { } from '#/components/dialogs/EmailDialog' import {router} from '#/routes' import {Referrer} from '../modules/expo-bluesky-swiss-army' -import {NotificationsActivityListScreen} from './screens/Notifications/ActivityList' -import {LegacyNotificationSettingsScreen} from './screens/Settings/LegacyNotificationSettings' -import {NotificationSettingsScreen} from './screens/Settings/NotificationSettings' -import {ActivityNotificationSettingsScreen} from './screens/Settings/NotificationSettings/ActivityNotificationSettings' -import {LikeNotificationSettingsScreen} from './screens/Settings/NotificationSettings/LikeNotificationSettings' -import {LikesOnRepostsNotificationSettingsScreen} from './screens/Settings/NotificationSettings/LikesOnRepostsNotificationSettings' -import {MentionNotificationSettingsScreen} from './screens/Settings/NotificationSettings/MentionNotificationSettings' -import {MiscellaneousNotificationSettingsScreen} from './screens/Settings/NotificationSettings/MiscellaneousNotificationSettings' -import {NewFollowerNotificationSettingsScreen} from './screens/Settings/NotificationSettings/NewFollowerNotificationSettings' -import {QuoteNotificationSettingsScreen} from './screens/Settings/NotificationSettings/QuoteNotificationSettings' -import {ReplyNotificationSettingsScreen} from './screens/Settings/NotificationSettings/ReplyNotificationSettings' -import {RepostNotificationSettingsScreen} from './screens/Settings/NotificationSettings/RepostNotificationSettings' -import {RepostsOnRepostsNotificationSettingsScreen} from './screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings' +import {useAccountSwitcher} from './lib/hooks/useAccountSwitcher' +import {useNonReactiveCallback} from './lib/hooks/useNonReactiveCallback' +import {useLoggedOutViewControls} from './state/shell/logged-out' +import {useCloseAllActiveElements} from './state/util' const navigationRef = createNavigationContainerRef() @@ -595,7 +608,7 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) { * in 3 distinct tab-stacks with a different root screen on each. */ function TabsNavigator() { - const tabBar = React.useCallback( + const tabBar = useCallback( (props: JSX.IntrinsicAttributes & BottomTabBarProps) => ( ), @@ -762,6 +775,7 @@ const FlatNavigator = () => { const LINKING = { // TODO figure out what we are going to use + // note: `bluesky://` is what is used in app.config.js prefixes: ['bsky://', 'bluesky://', 'https://bsky.app'], getPathFromState(state: State) { @@ -818,13 +832,100 @@ const LINKING = { return res } }, -} +} satisfies LinkingOptions + +/** + * Used to ensure we don't handle the same notification twice + */ +let lastHandledNotificationDateDedupe: number | undefined function RoutesContainer({children}: React.PropsWithChildren<{}>) { const theme = useColorSchemeStyle(DefaultTheme, DarkTheme) - const {currentAccount} = useSession() - const prevLoggedRouteName = React.useRef(undefined) + const {currentAccount, accounts} = useSession() + const {onPressSwitchAccount} = useAccountSwitcher() + const {setShowLoggedOut} = useLoggedOutViewControls() + const prevLoggedRouteName = useRef(undefined) const emailDialogControl = useEmailDialogControl() + const closeAllActiveElements = useCloseAllActiveElements() + + /** + * Handle navigation to a conversation, or prepares for account switch. + * + * Non-reactive because we need the latest data from some hooks + * after an async call - sfn + */ + const handleChatMessage = useNonReactiveCallback( + (payload: Extract) => { + notyLogger.debug(`handleChatMessage`, {payload}) + + if (payload.recipientDid !== currentAccount?.did) { + // handled in useNotificationHandler after account switch finishes + storePayloadForAccountSwitch(payload) + closeAllActiveElements() + + const account = accounts.find(a => a.did === payload.recipientDid) + if (account) { + onPressSwitchAccount(account, 'Notification') + } else { + setShowLoggedOut(true) + } + } else { + // @ts-expect-error nested navigators aren't typed -sfn + navigate('MessagesTab', { + screen: 'MessagesConversation', + params: { + conversation: payload.convoId, + }, + }) + } + }, + ) + + async function handlePushNotificationEntry() { + if (!isNative) return + + /** + * The notification that caused the app to open, if applicable + */ + const response = await Notifications.getLastNotificationResponseAsync() + + if (response) { + notyLogger.debug(`handlePushNotificationEntry: response`, {response}) + + if (response.notification.date === lastHandledNotificationDateDedupe) + return + lastHandledNotificationDateDedupe = response.notification.date + + const payload = getNotificationPayload(response.notification) + + if (payload) { + notyLogger.metric( + 'notifications:openApp', + {reason: payload.reason, causedBoot: true}, + {statsig: false}, + ) + + if (payload.reason === 'chat-message') { + handleChatMessage(payload) + } else { + const path = notificationToURL(payload) + + if (path === '/notifications') { + resetToTab('NotificationsTab') + notyLogger.debug(`handlePushNotificationEntry: default navigate`) + } else if (path) { + const [screen, params] = router.matchPath(path) + // @ts-expect-error nested navigators aren't typed -sfn + navigate('HomeTab', {screen, params}) + notyLogger.debug(`handlePushNotificationEntry: navigate`, { + screen, + params, + }) + } + } + } + } + } function onReady() { prevLoggedRouteName.current = getCurrentRouteName() @@ -845,9 +946,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { onStateChange={() => { logger.metric( 'router:navigate', - { - from: prevLoggedRouteName.current, - }, + {from: prevLoggedRouteName.current}, {statsig: false}, ) prevLoggedRouteName.current = getCurrentRouteName() @@ -857,6 +956,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { logModuleInitTime() onReady() logger.metric('router:navigate', {}, {statsig: false}) + handlePushNotificationEntry() }} // WARNING: Implicit navigation to nested navigators is depreciated in React Navigation 7.x // However, there's a fair amount of places we do that, especially in when popping to the top of stacks. @@ -906,7 +1006,9 @@ function navigate( return Promise.resolve() } -function resetToTab(tabName: 'HomeTab' | 'SearchTab' | 'NotificationsTab') { +function resetToTab( + tabName: 'HomeTab' | 'SearchTab' | 'MessagesTab' | 'NotificationsTab', +) { if (navigationRef.isReady()) { navigate(tabName) if (navigationRef.canGoBack()) { diff --git a/src/lib/hooks/useNotificationHandler.ts b/src/lib/hooks/useNotificationHandler.ts index 6c3e7deb8..ddee11fb5 100644 --- a/src/lib/hooks/useNotificationHandler.ts +++ b/src/lib/hooks/useNotificationHandler.ts @@ -7,9 +7,9 @@ import {CommonActions, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' +import {logger as notyLogger} from '#/lib/notifications/util' import {type NavigationProp} from '#/lib/routes/types' -import {Logger} from '#/logger' -import {isAndroid} from '#/platform/detection' +import {isAndroid, isIOS} from '#/platform/detection' import {useCurrentConvoId} from '#/state/messages/current-convo-id' import {RQKEY as RQKEY_NOTIFS} from '#/state/queries/notifications/feed' import {invalidateCachedUnreadPage} from '#/state/queries/notifications/unread' @@ -18,6 +18,7 @@ import {useSession} from '#/state/session' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useCloseAllActiveElements} from '#/state/util' import {resetToTab} from '#/Navigation' +import {router} from '#/routes' export type NotificationReason = | 'like' @@ -39,12 +40,13 @@ export type NotificationReason = * `notification.request.trigger.payload` being `undefined`, as specified in * the source types. */ -type NotificationPayload = +export type NotificationPayload = | undefined | { reason: Exclude uri: string subject: string + recipientDid: string } | { reason: 'chat-message' @@ -60,11 +62,17 @@ const DEFAULT_HANDLER_OPTIONS = { shouldSetBadge: true, } satisfies Notifications.NotificationBehavior -// These need to stay outside the hook to persist between account switches -let storedPayload: NotificationPayload -let prevDate = 0 +/** + * Cached notification payload if we handled a notification while the user was + * using a different account. This is consumed after we finish switching + * accounts. + */ +let storedAccountSwitchPayload: NotificationPayload -const logger = Logger.create(Logger.Context.Notifications) +/** + * Used to ensure we don't handle the same notification twice + */ +let lastHandledNotificationDateDedupe = 0 export function useNotificationsHandler() { const queryClient = useQueryClient() @@ -182,8 +190,15 @@ export function useNotificationsHandler() { if (!payload) return if (payload.reason === 'chat-message') { - if (payload.recipientDid !== currentAccount?.did && !storedPayload) { - storedPayload = payload + notyLogger.debug(`useNotificationsHandler: handling chat message`, { + payload, + }) + + if ( + payload.recipientDid !== currentAccount?.did && + !storedAccountSwitchPayload + ) { + storePayloadForAccountSwitch(payload) closeAllActiveElements() const account = accounts.find(a => a.did === payload.recipientDid) @@ -227,86 +242,29 @@ export function useNotificationsHandler() { }) } } else { - switch (payload.reason) { - case 'subscribed-post': - const urip = new AtUri(payload.uri) - if (urip.collection === 'app.bsky.feed.post') { - setTimeout(() => { - // @ts-expect-error types are weird here - navigation.navigate('HomeTab', { - screen: 'PostThread', - params: { - name: urip.host, - rkey: urip.rkey, - }, - }) - }, 500) - } else { - resetToTab('NotificationsTab') - } - break - case 'like': - case 'repost': - case 'follow': - case 'mention': - case 'quote': - case 'reply': - case 'starterpack-joined': - case 'like-via-repost': - case 'repost-via-repost': - case 'verified': - case 'unverified': - default: - resetToTab('NotificationsTab') - break - // TODO implement these after we have an idea of how to handle each individual case - // case 'follow': - // const uri = new AtUri(payload.uri) - // setTimeout(() => { - // // @ts-expect-error types are weird here - // navigation.navigate('HomeTab', { - // screen: 'Profile', - // params: { - // name: uri.host, - // }, - // }) - // }, 500) - // break - // case 'mention': - // case 'reply': - // const urip = new AtUri(payload.uri) - // setTimeout(() => { - // // @ts-expect-error types are weird here - // navigation.navigate('HomeTab', { - // screen: 'PostThread', - // params: { - // name: urip.host, - // rkey: urip.rkey, - // }, - // }) - // }, 500) + const url = notificationToURL(payload) + + if (url === '/notifications') { + resetToTab('NotificationsTab') + } else if (url) { + const [screen, params] = router.matchPath(url) + // @ts-expect-error router is not typed :/ -sfn + navigation.navigate('HomeTab', {screen, params}) + notyLogger.debug(`useNotificationsHandler: navigate`, { + screen, + params, + }) } } } Notifications.setNotificationHandler({ handleNotification: async e => { - if ( - e.request.trigger == null || - typeof e.request.trigger !== 'object' || - !('type' in e.request.trigger) || - e.request.trigger.type !== 'push' - ) { - return DEFAULT_HANDLER_OPTIONS - } + const payload = getNotificationPayload(e) - logger.debug('Notifications: received', {e}) + if (!payload) return DEFAULT_HANDLER_OPTIONS - const payload = e.request.trigger.payload as NotificationPayload - - if (!payload) { - return DEFAULT_HANDLER_OPTIONS - } + notyLogger.debug('useNotificationsHandler: incoming', {e, payload}) if ( payload.reason === 'chat-message' && @@ -329,46 +287,38 @@ export function useNotificationsHandler() { const responseReceivedListener = Notifications.addNotificationResponseReceivedListener(e => { - if (e.notification.date === prevDate) { - return - } - prevDate = e.notification.date + if (e.notification.date === lastHandledNotificationDateDedupe) return + lastHandledNotificationDateDedupe = e.notification.date - logger.debug('Notifications: response received', { + notyLogger.debug('useNotificationsHandler: response received', { actionIdentifier: e.actionIdentifier, }) - if ( - e.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER && - e.notification.request.trigger != null && - typeof e.notification.request.trigger === 'object' && - 'type' in e.notification.request.trigger && - e.notification.request.trigger.type === 'push' - ) { - const payload = e.notification.request.trigger - .payload as NotificationPayload + if (e.actionIdentifier !== Notifications.DEFAULT_ACTION_IDENTIFIER) { + return + } - if (!payload) { - logger.error('useNotificationsHandler: received no payload', { - identifier: e.notification.request.identifier, - }) - return - } + const payload = getNotificationPayload(e.notification) + + if (payload) { if (!payload.reason) { - logger.error('useNotificationsHandler: received unknown payload', { - payload, - identifier: e.notification.request.identifier, - }) + notyLogger.error( + 'useNotificationsHandler: received unknown payload', + { + payload, + identifier: e.notification.request.identifier, + }, + ) return } - logger.debug( + notyLogger.debug( 'User pressed a notification, opening notifications tab', {}, ) - logger.metric( + notyLogger.metric( 'notifications:openApp', - {reason: payload.reason}, + {reason: payload.reason, causedBoot: false}, {statsig: false}, ) @@ -383,24 +333,28 @@ export function useNotificationsHandler() { truncateAndInvalidate(queryClient, RQKEY_NOTIFS('mentions')) } - logger.debug('Notifications: handleNotification', { + notyLogger.debug('Notifications: handleNotification', { content: e.notification.request.content, - payload: e.notification.request.trigger.payload, + payload: payload, }) handleNotification(payload) Notifications.dismissAllNotificationsAsync() + } else { + notyLogger.error('useNotificationsHandler: received no payload', { + identifier: e.notification.request.identifier, + }) } }) // Whenever there's a stored payload, that means we had to switch accounts before handling the notification. // Whenever currentAccount changes, we should try to handle it again. if ( - storedPayload?.reason === 'chat-message' && - currentAccount?.did === storedPayload.recipientDid + storedAccountSwitchPayload?.reason === 'chat-message' && + currentAccount?.did === storedAccountSwitchPayload.recipientDid ) { - handleNotification(storedPayload) - storedPayload = undefined + handleNotification(storedAccountSwitchPayload) + storedAccountSwitchPayload = undefined } return () => { @@ -418,3 +372,71 @@ export function useNotificationsHandler() { setShowLoggedOut, ]) } + +export function storePayloadForAccountSwitch(payload: NotificationPayload) { + storedAccountSwitchPayload = payload +} + +export function getNotificationPayload( + e: Notifications.Notification, +): NotificationPayload | null { + if ( + e.request.trigger == null || + typeof e.request.trigger !== 'object' || + !('type' in e.request.trigger) || + e.request.trigger.type !== 'push' + ) { + return null + } + + const payload = ( + isIOS ? e.request.trigger.payload : e.request.content.data + ) as NotificationPayload + + if (payload) { + return payload + } else { + return null + } +} + +export function notificationToURL( + payload: NotificationPayload, +): string | undefined { + switch (payload?.reason) { + case 'like': + case 'repost': + case 'like-via-repost': + case 'repost-via-repost': { + const urip = new AtUri(payload.subject) + if (urip.collection === 'app.bsky.feed.post') { + return `/profile/${urip.host}/post/${urip.rkey}` + } else { + return '/notifications' + } + } + case 'reply': + case 'quote': + case 'mention': + case 'subscribed-post': { + const urip = new AtUri(payload.uri) + if (urip.collection === 'app.bsky.feed.post') { + return `/profile/${urip.host}/post/${urip.rkey}` + } else { + return '/notifications' + } + } + case 'follow': + case 'starterpack-joined': { + const urip = new AtUri(payload.uri) + return `/profile/${urip.host}` + } + case 'chat-message': + // should be handled separately + return undefined + case 'verified': + case 'unverified': + default: + return '/notifications' + } +} diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts index 2c0487ab7..94b3f6de3 100644 --- a/src/lib/notifications/notifications.ts +++ b/src/lib/notifications/notifications.ts @@ -6,13 +6,11 @@ import {type AtpAgent} from '@atproto/api' import debounce from 'lodash.debounce' import {PUBLIC_APPVIEW_DID, PUBLIC_STAGING_APPVIEW_DID} from '#/lib/constants' -import {Logger} from '#/logger' +import {logger as notyLogger} from '#/lib/notifications/util' import {isNative} from '#/platform/detection' import {type SessionAccount, useAgent, useSession} from '#/state/session' import BackgroundNotificationHandler from '#/../modules/expo-background-notification-handler' -const logger = Logger.create(Logger.Context.Notifications) - /** * @private * Registers the device's push notification token with the Bluesky server. @@ -36,12 +34,12 @@ async function _registerPushToken({ appId: 'xyz.blueskyweb.app', }) - logger.debug(`registerPushToken: success`, { + notyLogger.debug(`registerPushToken: success`, { tokenType: token.type, token: token.data, }) } catch (error) { - logger.error(`registerPushToken: failed`, {safeMessage: error}) + notyLogger.error(`registerPushToken: failed`, {safeMessage: error}) } } @@ -80,7 +78,7 @@ export function useRegisterPushToken() { */ async function getPushToken() { const granted = (await Notifications.getPermissionsAsync()).granted - logger.debug(`getPushToken`, {granted}) + notyLogger.debug(`getPushToken`, {granted}) if (granted) { return Notifications.getDevicePushTokenAsync() } @@ -115,7 +113,9 @@ export function useGetAndRegisterPushToken() { */ const token = await getPushToken() - logger.debug(`useGetAndRegisterPushToken`, {token: token ?? 'undefined'}) + notyLogger.debug(`useGetAndRegisterPushToken`, { + token: token ?? 'undefined', + }) if (token) { /** @@ -147,7 +147,7 @@ export function useNotificationsRegistration() { */ if (!currentAccount) return - logger.debug(`useNotificationsRegistration`) + notyLogger.debug(`useNotificationsRegistration`) /** * Init push token, if permissions are granted already. If they weren't, @@ -168,7 +168,7 @@ export function useNotificationsRegistration() { */ const subscription = Notifications.addPushTokenListener(async token => { registerPushToken({token}) - logger.debug(`addPushTokenListener callback`, {token}) + notyLogger.debug(`addPushTokenListener callback`, {token}) }) return () => { @@ -202,7 +202,7 @@ export function useRequestNotificationsPermission() { const res = await Notifications.requestPermissionsAsync() - logger.metric(`notifications:request`, { + notyLogger.metric(`notifications:request`, { context: context, status: res.status, }) diff --git a/src/lib/notifications/util.ts b/src/lib/notifications/util.ts new file mode 100644 index 000000000..6d00aa0e3 --- /dev/null +++ b/src/lib/notifications/util.ts @@ -0,0 +1,3 @@ +import {Logger} from '#/logger' + +export const logger = Logger.create(Logger.Context.Notifications) diff --git a/src/lib/routes/links.ts b/src/lib/routes/links.ts index 10c99b62d..908db7cba 100644 --- a/src/lib/routes/links.ts +++ b/src/lib/routes/links.ts @@ -1,4 +1,4 @@ -import {AppBskyGraphDefs, AtUri} from '@atproto/api' +import {type AppBskyGraphDefs, AtUri} from '@atproto/api' import {isInvalidHandle} from '#/lib/strings/handles' diff --git a/src/logger/metrics.ts b/src/logger/metrics.ts index d18e69122..3390c4b4b 100644 --- a/src/logger/metrics.ts +++ b/src/logger/metrics.ts @@ -26,6 +26,7 @@ export type MetricEvents = { } 'notifications:openApp': { reason: NotificationReason + causedBoot: boolean } 'notifications:request': { context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' | 'Home' -- 2.51.2 From 8f5a8f37af06775b422d4a53d0f94af71d80c0c2 Mon Sep 17 00:00:00 2001 From: pfrazee <1270099+pfrazee@users.noreply.github.com> Date: Fri, 11 Jul 2025 02:46:37 +0000 Subject: [PATCH 3/5] Nightly source-language update --- src/locale/locales/en/messages.po | 174 +++++++++++++++--------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po index fb7a09283..f2170dcb2 100644 --- a/src/locale/locales/en/messages.po +++ b/src/locale/locales/en/messages.po @@ -509,7 +509,7 @@ msgstr "" msgid "A screenshot of a profile page with a bell icon next to the follow button, indicating the new activity notifications feature." msgstr "" -#: src/Navigation.tsx:509 +#: src/Navigation.tsx:522 #: src/screens/Settings/AboutSettings.tsx:75 #: src/screens/Settings/Settings.tsx:235 #: src/screens/Settings/Settings.tsx:238 @@ -536,11 +536,11 @@ msgstr "" msgid "Accessibility" msgstr "" -#: src/Navigation.tsx:368 +#: src/Navigation.tsx:381 msgid "Accessibility Settings" msgstr "" -#: src/Navigation.tsx:384 +#: src/Navigation.tsx:397 #: src/screens/Login/LoginForm.tsx:194 #: src/screens/Settings/AccountSettings.tsx:48 #: src/screens/Settings/Settings.tsx:165 @@ -604,13 +604,13 @@ msgstr "" msgid "Accounts with a scalloped blue check mark <0><1/> can verify others. These trusted verifiers are selected by Bluesky." msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:174 +#: src/lib/hooks/useNotificationHandler.ts:182 #: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:102 #: src/screens/Settings/NotificationSettings/index.tsx:192 msgid "Activity from others" msgstr "" -#: src/Navigation.tsx:477 +#: src/Navigation.tsx:490 msgid "Activity notifications" msgstr "" @@ -990,7 +990,7 @@ msgstr "" msgid "Anyone who follows me" msgstr "" -#: src/Navigation.tsx:517 +#: src/Navigation.tsx:530 #: src/screens/Settings/AppIconSettings/index.tsx:67 #: src/screens/Settings/AppIconSettings/SettingsListItem.tsx:18 #: src/screens/Settings/AppIconSettings/SettingsListItem.tsx:23 @@ -1027,7 +1027,7 @@ msgstr "" msgid "App passwords" msgstr "" -#: src/Navigation.tsx:336 +#: src/Navigation.tsx:349 #: src/screens/Settings/AppPasswords.tsx:51 msgid "App Passwords" msgstr "" @@ -1063,7 +1063,7 @@ msgstr "" msgid "Appeal this decision" msgstr "" -#: src/Navigation.tsx:376 +#: src/Navigation.tsx:389 #: src/screens/Settings/AppearanceSettings.tsx:88 #: src/screens/Settings/Settings.tsx:203 #: src/screens/Settings/Settings.tsx:206 @@ -1279,7 +1279,7 @@ msgstr "" msgid "Blocked accounts" msgstr "" -#: src/Navigation.tsx:177 +#: src/Navigation.tsx:190 #: src/view/screens/ModerationBlockedAccounts.tsx:104 msgid "Blocked Accounts" msgstr "" @@ -1587,8 +1587,8 @@ msgstr "" msgid "Changes saved" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:91 -#: src/Navigation.tsx:534 +#: src/lib/hooks/useNotificationHandler.ts:99 +#: src/Navigation.tsx:547 #: src/view/shell/bottom-bar/BottomBar.tsx:221 #: src/view/shell/desktop/LeftNav.tsx:553 #: src/view/shell/Drawer.tsx:455 @@ -1601,11 +1601,11 @@ msgctxt "toast" msgid "Chat deleted" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:106 +#: src/lib/hooks/useNotificationHandler.ts:114 msgid "Chat messages - silent" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:97 +#: src/lib/hooks/useNotificationHandler.ts:105 msgid "Chat messages - sound" msgstr "" @@ -1614,7 +1614,7 @@ msgctxt "toast" msgid "Chat muted" msgstr "" -#: src/Navigation.tsx:544 +#: src/Navigation.tsx:557 #: src/screens/Messages/components/InboxPreview.tsx:24 msgid "Chat request inbox" msgstr "" @@ -1625,7 +1625,7 @@ msgid "Chat requests" msgstr "" #: src/components/dms/ConvoMenu.tsx:75 -#: src/Navigation.tsx:539 +#: src/Navigation.tsx:552 #: src/screens/Messages/ChatList.tsx:341 msgid "Chat settings" msgstr "" @@ -1876,7 +1876,7 @@ msgstr "" msgid "Comics" msgstr "" -#: src/Navigation.tsx:326 +#: src/Navigation.tsx:339 #: src/view/screens/CommunityGuidelines.tsx:34 msgid "Community Guidelines" msgstr "" @@ -1963,7 +1963,7 @@ msgstr "" msgid "Content and media" msgstr "" -#: src/Navigation.tsx:493 +#: src/Navigation.tsx:506 msgid "Content and Media" msgstr "" @@ -2154,7 +2154,7 @@ msgstr "" msgid "Copy TXT record value" msgstr "" -#: src/Navigation.tsx:331 +#: src/Navigation.tsx:344 #: src/view/screens/CopyrightPolicy.tsx:31 msgid "Copyright Policy" msgstr "" @@ -2210,7 +2210,7 @@ msgstr "" #: src/components/StarterPack/ProfileStarterPacks.tsx:178 #: src/components/StarterPack/ProfileStarterPacks.tsx:287 -#: src/Navigation.tsx:574 +#: src/Navigation.tsx:587 msgid "Create a starter pack" msgstr "" @@ -2784,7 +2784,7 @@ msgstr "" msgid "Edit Moderation List" msgstr "" -#: src/Navigation.tsx:341 +#: src/Navigation.tsx:354 #: src/view/screens/Feeds.tsx:518 msgid "Edit My Feeds" msgstr "" @@ -2826,7 +2826,7 @@ msgstr "" msgid "Edit who can reply" msgstr "" -#: src/Navigation.tsx:579 +#: src/Navigation.tsx:592 msgid "Edit your starter pack" msgstr "" @@ -3168,7 +3168,7 @@ msgstr "" msgid "Explicit sexual images." msgstr "" -#: src/Navigation.tsx:736 +#: src/Navigation.tsx:749 #: src/screens/Search/Shell.tsx:307 #: src/view/shell/desktop/LeftNav.tsx:635 #: src/view/shell/Drawer.tsx:403 @@ -3199,7 +3199,7 @@ msgstr "" msgid "External media may allow websites to collect information about you and your device. No information is sent or requested until you press the \"play\" button." msgstr "" -#: src/Navigation.tsx:360 +#: src/Navigation.tsx:373 #: src/screens/Settings/ExternalMediaPreferences.tsx:34 msgid "External Media Preferences" msgstr "" @@ -3387,7 +3387,7 @@ msgstr "" msgid "Failed to verify handle. Please try again." msgstr "" -#: src/Navigation.tsx:276 +#: src/Navigation.tsx:289 msgid "Feed" msgstr "" @@ -3428,7 +3428,7 @@ msgctxt "toast" msgid "Feedback sent!" msgstr "" -#: src/Navigation.tsx:559 +#: src/Navigation.tsx:572 #: src/screens/Search/SearchResults.tsx:68 #: src/screens/StarterPack/StarterPackScreen.tsx:190 #: src/view/screens/Feeds.tsx:511 @@ -3597,7 +3597,7 @@ msgstr "" msgid "Followed by <0>{0}, <1>{1}, and {2, plural, one {# other} other {# others}}" msgstr "" -#: src/Navigation.tsx:230 +#: src/Navigation.tsx:243 msgid "Followers of @{0} that you know" msgstr "" @@ -3632,7 +3632,7 @@ msgstr "" msgid "Following feed preferences" msgstr "" -#: src/Navigation.tsx:347 +#: src/Navigation.tsx:360 #: src/screens/Settings/FollowingFeedPreferences.tsx:56 msgid "Following Feed Preferences" msgstr "" @@ -3919,7 +3919,7 @@ msgstr "" msgid "Harassment, trolling, or intolerance" msgstr "" -#: src/Navigation.tsx:524 +#: src/Navigation.tsx:537 msgid "Hashtag" msgstr "" @@ -4076,8 +4076,8 @@ msgstr "" msgid "Hold up! We’re gradually giving access to video, and you’re still waiting in line. Check back soon!" msgstr "" -#: src/Navigation.tsx:731 -#: src/Navigation.tsx:751 +#: src/Navigation.tsx:744 +#: src/Navigation.tsx:764 #: src/view/shell/bottom-bar/BottomBar.tsx:178 #: src/view/shell/desktop/LeftNav.tsx:617 #: src/view/shell/Drawer.tsx:429 @@ -4402,7 +4402,7 @@ msgstr "" msgid "Language selection" msgstr "" -#: src/Navigation.tsx:203 +#: src/Navigation.tsx:216 msgid "Language Settings" msgstr "" @@ -4546,7 +4546,7 @@ msgstr "" msgid "Like 10 posts to train the Discover feed" msgstr "" -#: src/Navigation.tsx:437 +#: src/Navigation.tsx:450 msgid "Like notifications" msgstr "" @@ -4558,8 +4558,8 @@ msgstr "" msgid "Like this labeler" msgstr "" -#: src/Navigation.tsx:281 -#: src/Navigation.tsx:286 +#: src/Navigation.tsx:294 +#: src/Navigation.tsx:299 msgid "Liked by" msgstr "" @@ -4581,20 +4581,20 @@ msgstr "" msgid "Liked by {likeCount, plural, one {# user} other {# users}}" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:118 +#: src/lib/hooks/useNotificationHandler.ts:126 #: src/screens/Settings/NotificationSettings/index.tsx:126 #: src/screens/Settings/NotificationSettings/LikeNotificationSettings.tsx:41 #: src/view/screens/Profile.tsx:229 msgid "Likes" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:160 +#: src/lib/hooks/useNotificationHandler.ts:168 #: src/screens/Settings/NotificationSettings/index.tsx:207 #: src/screens/Settings/NotificationSettings/LikesOnRepostsNotificationSettings.tsx:41 msgid "Likes of your reposts" msgstr "" -#: src/Navigation.tsx:461 +#: src/Navigation.tsx:474 msgid "Likes of your reposts notifications" msgstr "" @@ -4610,7 +4610,7 @@ msgstr "" msgid "Linear" msgstr "" -#: src/Navigation.tsx:236 +#: src/Navigation.tsx:249 msgid "List" msgstr "" @@ -4672,7 +4672,7 @@ msgctxt "toast" msgid "List unmuted" msgstr "" -#: src/Navigation.tsx:157 +#: src/Navigation.tsx:170 #: src/view/screens/Lists.tsx:65 #: src/view/screens/Profile.tsx:224 #: src/view/screens/Profile.tsx:232 @@ -4725,7 +4725,7 @@ msgstr "" msgid "Loading..." msgstr "" -#: src/Navigation.tsx:306 +#: src/Navigation.tsx:319 msgid "Log" msgstr "" @@ -4816,7 +4816,7 @@ msgstr "" msgid "Media that may be disturbing or inappropriate for some audiences." msgstr "" -#: src/Navigation.tsx:421 +#: src/Navigation.tsx:434 msgid "Mention notifications" msgstr "" @@ -4828,7 +4828,7 @@ msgstr "" msgid "Mentioned users" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:139 +#: src/lib/hooks/useNotificationHandler.ts:147 #: src/screens/Settings/NotificationSettings/index.tsx:159 #: src/screens/Settings/NotificationSettings/MentionNotificationSettings.tsx:41 #: src/view/screens/Notifications.tsx:101 @@ -4873,7 +4873,7 @@ msgstr "" msgid "Message options" msgstr "" -#: src/Navigation.tsx:746 +#: src/Navigation.tsx:759 msgid "Messages" msgstr "" @@ -4882,7 +4882,7 @@ msgctxt "Name of app icon variant" msgid "Midnight" msgstr "" -#: src/Navigation.tsx:485 +#: src/Navigation.tsx:498 msgid "Miscellaneous notifications" msgstr "" @@ -4896,7 +4896,7 @@ msgstr "" msgid "Misleading Post" msgstr "" -#: src/Navigation.tsx:162 +#: src/Navigation.tsx:175 #: src/screens/Moderation/index.tsx:93 #: src/screens/Settings/Settings.tsx:179 #: src/screens/Settings/Settings.tsx:182 @@ -4935,7 +4935,7 @@ msgstr "" msgid "Moderation lists" msgstr "" -#: src/Navigation.tsx:167 +#: src/Navigation.tsx:180 #: src/view/screens/ModerationModlists.tsx:65 msgid "Moderation Lists" msgstr "" @@ -4944,7 +4944,7 @@ msgstr "" msgid "moderation settings" msgstr "" -#: src/Navigation.tsx:296 +#: src/Navigation.tsx:309 msgid "Moderation states" msgstr "" @@ -5067,7 +5067,7 @@ msgstr "" msgid "Muted accounts" msgstr "" -#: src/Navigation.tsx:172 +#: src/Navigation.tsx:185 #: src/view/screens/ModerationMutedAccounts.tsx:118 msgid "Muted Accounts" msgstr "" @@ -5186,11 +5186,11 @@ msgstr "" msgid "New Feature" msgstr "" -#: src/Navigation.tsx:453 +#: src/Navigation.tsx:466 msgid "New follower notifications" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:153 +#: src/lib/hooks/useNotificationHandler.ts:161 #: src/screens/Settings/NotificationSettings/index.tsx:137 #: src/screens/Settings/NotificationSettings/NewFollowerNotificationSettings.tsx:41 msgid "New followers" @@ -5447,7 +5447,7 @@ msgstr "" msgid "Not followed by anyone you're following" msgstr "" -#: src/Navigation.tsx:152 +#: src/Navigation.tsx:165 #: src/view/screens/Profile.tsx:125 msgid "Not Found" msgstr "" @@ -5468,8 +5468,8 @@ msgstr "" msgid "Nothing here" msgstr "" -#: src/Navigation.tsx:407 -#: src/Navigation.tsx:554 +#: src/Navigation.tsx:420 +#: src/Navigation.tsx:567 #: src/view/screens/Notifications.tsx:136 msgid "Notification settings" msgstr "" @@ -5482,8 +5482,8 @@ msgstr "" msgid "Notification Sounds" msgstr "" -#: src/Navigation.tsx:549 -#: src/Navigation.tsx:741 +#: src/Navigation.tsx:562 +#: src/Navigation.tsx:754 #: src/screens/Notifications/ActivityList.tsx:29 #: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:90 #: src/screens/Settings/NotificationSettings/index.tsx:92 @@ -5902,11 +5902,11 @@ msgstr "" msgid "People" msgstr "" -#: src/Navigation.tsx:223 +#: src/Navigation.tsx:236 msgid "People followed by @{0}" msgstr "" -#: src/Navigation.tsx:216 +#: src/Navigation.tsx:229 msgid "People following @{0}" msgstr "" @@ -6153,10 +6153,10 @@ msgstr "" msgid "Post by {0}" msgstr "" -#: src/Navigation.tsx:249 -#: src/Navigation.tsx:256 -#: src/Navigation.tsx:263 -#: src/Navigation.tsx:270 +#: src/Navigation.tsx:262 +#: src/Navigation.tsx:269 +#: src/Navigation.tsx:276 +#: src/Navigation.tsx:283 msgid "Post by @{0}" msgstr "" @@ -6194,7 +6194,7 @@ msgstr "" msgid "Post interaction settings" msgstr "" -#: src/Navigation.tsx:183 +#: src/Navigation.tsx:196 #: src/screens/ModerationInteractionSettings/index.tsx:34 msgid "Post Interaction Settings" msgstr "" @@ -6293,8 +6293,8 @@ msgstr "" msgid "Privacy and security" msgstr "" -#: src/Navigation.tsx:392 -#: src/Navigation.tsx:400 +#: src/Navigation.tsx:405 +#: src/Navigation.tsx:413 #: src/screens/Settings/ActivityPrivacySettings.tsx:40 #: src/screens/Settings/PrivacyAndSecuritySettings.tsx:45 msgid "Privacy and Security" @@ -6304,7 +6304,7 @@ msgstr "" msgid "Privacy and Security settings" msgstr "" -#: src/Navigation.tsx:316 +#: src/Navigation.tsx:329 #: src/screens/Settings/AboutSettings.tsx:92 #: src/screens/Settings/AboutSettings.tsx:95 #: src/view/screens/PrivacyPolicy.tsx:31 @@ -6399,7 +6399,7 @@ msgstr "" msgid "QR code saved to your camera roll!" msgstr "" -#: src/Navigation.tsx:429 +#: src/Navigation.tsx:442 msgid "Quote notifications" msgstr "" @@ -6429,7 +6429,7 @@ msgstr "" msgid "Quote settings" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:146 +#: src/lib/hooks/useNotificationHandler.ts:154 #: src/screens/Post/PostQuotes.tsx:38 #: src/screens/Settings/NotificationSettings/index.tsx:170 #: src/screens/Settings/NotificationSettings/QuoteNotificationSettings.tsx:41 @@ -6694,7 +6694,7 @@ msgstr "" #: src/components/activity-notifications/SubscribeProfileDialog.tsx:267 #: src/components/activity-notifications/SubscribeProfileDialog.tsx:279 -#: src/lib/hooks/useNotificationHandler.ts:132 +#: src/lib/hooks/useNotificationHandler.ts:140 #: src/screens/Settings/NotificationSettings/ActivityNotificationSettings.tsx:215 #: src/screens/Settings/NotificationSettings/index.tsx:148 #: src/screens/Settings/NotificationSettings/ReplyNotificationSettings.tsx:41 @@ -6730,7 +6730,7 @@ msgstr "" msgid "Reply Hidden by You" msgstr "" -#: src/Navigation.tsx:413 +#: src/Navigation.tsx:426 msgid "Reply notifications" msgstr "" @@ -6882,7 +6882,7 @@ msgstr "" msgid "Repost ({0, plural, one {# repost} other {# reposts}})" msgstr "" -#: src/Navigation.tsx:445 +#: src/Navigation.tsx:458 msgid "Repost notifications" msgstr "" @@ -6910,7 +6910,7 @@ msgstr "" msgid "Reposted by you" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:125 +#: src/lib/hooks/useNotificationHandler.ts:133 #: src/screens/Settings/NotificationSettings/index.tsx:181 #: src/screens/Settings/NotificationSettings/RepostNotificationSettings.tsx:41 msgid "Reposts" @@ -6921,13 +6921,13 @@ msgstr "" msgid "Reposts of this post" msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:167 +#: src/lib/hooks/useNotificationHandler.ts:175 #: src/screens/Settings/NotificationSettings/index.tsx:222 #: src/screens/Settings/NotificationSettings/RepostsOnRepostsNotificationSettings.tsx:41 msgid "Reposts of your reposts" msgstr "" -#: src/Navigation.tsx:469 +#: src/Navigation.tsx:482 msgid "Reposts of your reposts notifications" msgstr "" @@ -7147,7 +7147,7 @@ msgstr "" msgid "Search" msgstr "" -#: src/Navigation.tsx:242 +#: src/Navigation.tsx:255 #: src/screens/Profile/ProfileSearch.tsx:37 msgid "Search @{0}'s posts" msgstr "" @@ -7463,7 +7463,7 @@ msgstr "" msgid "Sets email for password reset" msgstr "" -#: src/Navigation.tsx:198 +#: src/Navigation.tsx:211 #: src/screens/Settings/Settings.tsx:92 #: src/view/shell/desktop/LeftNav.tsx:727 #: src/view/shell/Drawer.tsx:572 @@ -7603,7 +7603,7 @@ msgstr "" msgid "Share your favorite feed!" msgstr "" -#: src/Navigation.tsx:301 +#: src/Navigation.tsx:314 msgid "Shared Preferences Tester" msgstr "" @@ -7959,8 +7959,8 @@ msgstr "" msgid "Start chat with {displayName}" msgstr "" -#: src/Navigation.tsx:564 -#: src/Navigation.tsx:569 +#: src/Navigation.tsx:577 +#: src/Navigation.tsx:582 #: src/screens/StarterPack/Wizard/index.tsx:186 msgid "Starter Pack" msgstr "" @@ -8004,7 +8004,7 @@ msgstr "" msgid "Storage cleared, you need to restart the app now." msgstr "" -#: src/Navigation.tsx:291 +#: src/Navigation.tsx:304 #: src/screens/Settings/Settings.tsx:405 msgid "Storybook" msgstr "" @@ -8085,7 +8085,7 @@ msgctxt "Name of app icon variant" msgid "Sunset" msgstr "" -#: src/Navigation.tsx:311 +#: src/Navigation.tsx:324 #: src/view/screens/Support.tsx:31 #: src/view/screens/Support.tsx:34 msgid "Support" @@ -8180,7 +8180,7 @@ msgstr "" msgid "Terms" msgstr "" -#: src/Navigation.tsx:321 +#: src/Navigation.tsx:334 #: src/screens/Settings/AboutSettings.tsx:84 #: src/screens/Settings/AboutSettings.tsx:87 #: src/view/screens/TermsOfService.tsx:31 @@ -8711,7 +8711,7 @@ msgstr "" msgid "Threaded mode" msgstr "" -#: src/Navigation.tsx:354 +#: src/Navigation.tsx:367 msgid "Threads Preferences" msgstr "" @@ -8765,7 +8765,7 @@ msgstr "" msgid "Top replies first" msgstr "" -#: src/Navigation.tsx:529 +#: src/Navigation.tsx:542 msgid "Topic" msgstr "" @@ -9232,7 +9232,7 @@ msgstr "" msgid "Verification settings" msgstr "" -#: src/Navigation.tsx:191 +#: src/Navigation.tsx:204 #: src/screens/Moderation/VerificationSettings.tsx:32 msgid "Verification Settings" msgstr "" @@ -9299,7 +9299,7 @@ msgstr "" msgid "Video failed to process" msgstr "" -#: src/Navigation.tsx:585 +#: src/Navigation.tsx:598 msgid "Video Feed" msgstr "" @@ -9823,7 +9823,7 @@ msgstr "" msgid "You can also temporarily deactivate your account instead, and reactivate it at any time." msgstr "" -#: src/lib/hooks/useNotificationHandler.ts:93 +#: src/lib/hooks/useNotificationHandler.ts:101 msgid "You can choose whether chat notifications have sound in the chat settings within the app" msgstr "" @@ -10205,7 +10205,7 @@ msgstr "" msgid "Your full username will be <0>@{0}" msgstr "" -#: src/Navigation.tsx:501 +#: src/Navigation.tsx:514 #: src/screens/Search/modules/ExploreInterestsCard.tsx:67 #: src/screens/Settings/ContentAndMediaSettings.tsx:92 #: src/screens/Settings/ContentAndMediaSettings.tsx:95 -- 2.51.2 From 0e3124a97e96d7f897a7125374b63a98d053598a Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Fri, 11 Jul 2025 18:49:59 +0300 Subject: [PATCH 4/5] Filter activity notifs for muted words (#8637) --- src/state/queries/notifications/util.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts index faccd8087..a47659a84 100644 --- a/src/state/queries/notifications/util.ts +++ b/src/state/queries/notifications/util.ts @@ -7,6 +7,7 @@ import { AppBskyGraphStarterpack, type AppBskyNotificationListNotifications, type BskyAgent, + hasMutedWord, moderateNotification, type ModerationOpts, } from '@atproto/api' @@ -125,6 +126,23 @@ export function shouldFilterNotif( if (!moderationOpts) { return false } + if ( + notif.reason === 'subscribed-post' && + bsky.dangerousIsType( + notif.record, + AppBskyFeedPost.isRecord, + ) && + hasMutedWord({ + mutedWords: moderationOpts.prefs.mutedWords, + text: notif.record.text, + facets: notif.record.facets, + outlineTags: notif.record.tags, + languages: notif.record.langs, + actor: notif.author, + }) + ) { + return true + } if (notif.author.viewer?.following) { return false } -- 2.51.2 From ff57e75b12cef3fc53e9439e3f9cd308e0312cc4 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Sat, 12 Jul 2025 20:50:04 +0300 Subject: [PATCH 5/5] fix mention tab flicker (#8640) --- src/view/com/notifications/NotificationFeed.tsx | 8 +++++++- src/view/com/util/PostMeta.tsx | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/view/com/notifications/NotificationFeed.tsx b/src/view/com/notifications/NotificationFeed.tsx index bad09929a..ca4ba9814 100644 --- a/src/view/com/notifications/NotificationFeed.tsx +++ b/src/view/com/notifications/NotificationFeed.tsx @@ -59,7 +59,13 @@ export function NotificationFeed({ enabled: enabled && !!moderationOpts, filter, }) - const isEmpty = !isFetching && !data?.pages[0]?.items.length + // previously, this was `!isFetching && !data?.pages[0]?.items.length` + // however, if the first page had no items (can happen in the mentions tab!) + // it would flicker the empty state whenever it was loading. + // therefore, we need to find if *any* page has items. in 99.9% of cases, + // the `.find()` won't need to go any further than the first page -sfn + const isEmpty = + !isFetching && !data?.pages.find(page => page.items.length > 0) const items = React.useMemo(() => { let arr: any[] = [] diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx index c1f804203..c0feea5d4 100644 --- a/src/view/com/util/PostMeta.tsx +++ b/src/view/com/util/PostMeta.tsx @@ -122,6 +122,7 @@ let PostMeta = (opts: PostMetaOpts): React.ReactNode => { )}