Refactor notification settings as dialogs (#10646)
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -308,16 +308,6 @@ func serve(cctx *cli.Context) error {
|
||||
e.GET("/settings/interests", server.WebGeneric)
|
||||
e.GET("/settings/about", server.WebGeneric)
|
||||
e.GET("/settings/notifications", server.WebGeneric)
|
||||
e.GET("/settings/notifications/replies", server.WebGeneric)
|
||||
e.GET("/settings/notifications/mentions", server.WebGeneric)
|
||||
e.GET("/settings/notifications/quotes", server.WebGeneric)
|
||||
e.GET("/settings/notifications/likes", server.WebGeneric)
|
||||
e.GET("/settings/notifications/reposts", server.WebGeneric)
|
||||
e.GET("/settings/notifications/new-followers", server.WebGeneric)
|
||||
e.GET("/settings/notifications/likes-on-reposts", server.WebGeneric)
|
||||
e.GET("/settings/notifications/reposts-on-reposts", server.WebGeneric)
|
||||
e.GET("/settings/notifications/activity", server.WebGeneric)
|
||||
e.GET("/settings/notifications/miscellaneous", server.WebGeneric)
|
||||
e.GET("/sys/debug", server.WebGeneric)
|
||||
e.GET("/sys/debug-mod", server.WebGeneric)
|
||||
e.GET("/sys/log", server.WebGeneric)
|
||||
|
||||
+6
-95
@@ -114,16 +114,6 @@ 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'
|
||||
@@ -444,86 +434,6 @@ function commonScreens(Stack: typeof Flat, unreadCountLabel?: string) {
|
||||
getComponent={() => NotificationSettingsScreen}
|
||||
options={{title: title(msg`Notification settings`), requireAuth: true}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ReplyNotificationSettings"
|
||||
getComponent={() => ReplyNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Reply notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="MentionNotificationSettings"
|
||||
getComponent={() => MentionNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Mention notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="QuoteNotificationSettings"
|
||||
getComponent={() => QuoteNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Quote notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="LikeNotificationSettings"
|
||||
getComponent={() => LikeNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Like notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="RepostNotificationSettings"
|
||||
getComponent={() => RepostNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Repost notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="NewFollowerNotificationSettings"
|
||||
getComponent={() => NewFollowerNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`New follower notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="LikesOnRepostsNotificationSettings"
|
||||
getComponent={() => LikesOnRepostsNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Likes of your reposts notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="RepostsOnRepostsNotificationSettings"
|
||||
getComponent={() => RepostsOnRepostsNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Reposts of your reposts notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ActivityNotificationSettings"
|
||||
getComponent={() => ActivityNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Activity notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="MiscellaneousNotificationSettings"
|
||||
getComponent={() => MiscellaneousNotificationSettingsScreen}
|
||||
options={{
|
||||
title: title(msg`Miscellaneous notifications`),
|
||||
requireAuth: true,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="ContentAndMediaSettings"
|
||||
getComponent={() => ContentAndMediaSettingsScreen}
|
||||
@@ -916,6 +826,7 @@ const LINKING = {
|
||||
|
||||
function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
const ax = useAnalytics()
|
||||
// eslint-disable-next-line react-compiler/react-compiler
|
||||
const notyLogger = ax.logger.useChild(ax.logger.Context.Notifications)
|
||||
const theme = useColorSchemeStyle(DefaultTheme, DarkTheme)
|
||||
const {currentAccount, accounts} = useSession()
|
||||
@@ -943,7 +854,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
|
||||
const account = accounts.find(a => a.did === payload.recipientDid)
|
||||
if (account) {
|
||||
onPressSwitchAccount(account, 'Notification')
|
||||
void onPressSwitchAccount(account, 'Notification')
|
||||
} else {
|
||||
setShowLoggedOut(true)
|
||||
}
|
||||
@@ -955,7 +866,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
// chat-added-to-group routes to the convo because the recipient was
|
||||
// just added and now has access.
|
||||
// @ts-expect-error nested navigators aren't typed -sfn
|
||||
navigate('MessagesTab', {
|
||||
void navigate('MessagesTab', {
|
||||
screen: 'Messages',
|
||||
params: {
|
||||
pushToConversation: payload.convoId,
|
||||
@@ -1012,7 +923,7 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) {
|
||||
} else if (path) {
|
||||
const [screen, params] = router.matchPath(path)
|
||||
// @ts-expect-error nested navigators aren't typed -sfn
|
||||
navigate('HomeTab', {screen, params})
|
||||
void navigate('HomeTab', {screen, params})
|
||||
notyLogger.debug(`handlePushNotificationEntry: navigate`, {
|
||||
screen,
|
||||
params,
|
||||
@@ -1118,7 +1029,7 @@ function navigate<K extends keyof AllNavigatorParams>(
|
||||
}
|
||||
navigationRef.addListener('state', handler)
|
||||
|
||||
// @ts-ignore I dont know what would make typescript happy but I have a life -prf
|
||||
// @ts-ignore I don't know what would make typescript happy but I have a life -prf
|
||||
navigationRef.navigate(name, params)
|
||||
}),
|
||||
timeout(1e3),
|
||||
@@ -1131,7 +1042,7 @@ function resetToTab(
|
||||
tabName: 'HomeTab' | 'SearchTab' | 'MessagesTab' | 'NotificationsTab',
|
||||
) {
|
||||
if (navigationRef.isReady()) {
|
||||
navigate(tabName)
|
||||
void navigate(tabName)
|
||||
if (navigationRef.canGoBack()) {
|
||||
navigationRef.dispatch(StackActions.popToTop()) //we need to check .canGoBack() before calling it
|
||||
}
|
||||
|
||||
@@ -1017,7 +1017,7 @@ export type Events = {
|
||||
'activityPreference:changeChannels': {
|
||||
name: string
|
||||
push: boolean
|
||||
list: boolean
|
||||
list?: boolean
|
||||
}
|
||||
'activityPreference:changeFilter': {
|
||||
name: string
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyNotificationDefs} from '@atproto/api'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import * as SettingsList from '#/screens/Settings/components/SettingsList'
|
||||
import {PreferenceControls} from '#/screens/Settings/NotificationSettings/components/PreferenceControls'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {type Props as SVGIconProps} from '#/components/icons/common'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
|
||||
type NotificationSettingsDialogProps = {
|
||||
control: Dialog.DialogControlProps
|
||||
name: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>
|
||||
syncOthers?: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>[]
|
||||
icon: React.ComponentType<SVGIconProps>
|
||||
titleText: React.ReactNode
|
||||
subtitleText: React.ReactNode
|
||||
allowDisableInApp?: boolean
|
||||
}
|
||||
|
||||
export function NotificationSettingsDialog({
|
||||
control,
|
||||
name,
|
||||
syncOthers,
|
||||
titleText,
|
||||
subtitleText,
|
||||
allowDisableInApp = true,
|
||||
}: NotificationSettingsDialogProps) {
|
||||
return (
|
||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
||||
<NotificationSettingsDialogInner
|
||||
control={control}
|
||||
name={name}
|
||||
syncOthers={syncOthers}
|
||||
titleText={titleText}
|
||||
subtitleText={subtitleText}
|
||||
allowDisableInApp={allowDisableInApp}
|
||||
/>
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
function NotificationSettingsDialogInner({
|
||||
control,
|
||||
name,
|
||||
syncOthers,
|
||||
titleText,
|
||||
subtitleText,
|
||||
allowDisableInApp,
|
||||
}: Omit<NotificationSettingsDialogProps, 'icon'>) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog.Handle />
|
||||
<Dialog.ScrollableInner
|
||||
label={l`Notification settings`}
|
||||
style={web({maxWidth: 400})}>
|
||||
<SettingsList.Container>
|
||||
<View style={[a.flex_1, a.gap_xs, a.mb_md]}>
|
||||
<Text style={[a.font_semi_bold, a.text_lg]}>{titleText}</Text>
|
||||
<Text
|
||||
style={[a.text_sm, t.atoms.text_contrast_medium, a.leading_snug]}>
|
||||
{subtitleText}
|
||||
</Text>
|
||||
</View>
|
||||
{isError ? (
|
||||
<View style={[a.mt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name={name}
|
||||
syncOthers={syncOthers}
|
||||
preference={preferences?.[name]}
|
||||
allowDisableInApp={allowDisableInApp}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
<Dialog.Close />
|
||||
{IS_NATIVE && (
|
||||
<Button
|
||||
color="secondary"
|
||||
size="large"
|
||||
label={l`Close dialog`}
|
||||
onPress={() => control.close()}
|
||||
style={[a.mt_md]}>
|
||||
<ButtonText>
|
||||
<Trans>Done</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
)}
|
||||
</Dialog.ScrollableInner>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -55,16 +55,6 @@ export type CommonNavigatorParams = {
|
||||
ActivityPrivacySettings: undefined
|
||||
ContentAndMediaSettings: undefined
|
||||
NotificationSettings: undefined
|
||||
ReplyNotificationSettings: undefined
|
||||
MentionNotificationSettings: undefined
|
||||
QuoteNotificationSettings: undefined
|
||||
LikeNotificationSettings: undefined
|
||||
RepostNotificationSettings: undefined
|
||||
NewFollowerNotificationSettings: undefined
|
||||
LikesOnRepostsNotificationSettings: undefined
|
||||
RepostsOnRepostsNotificationSettings: undefined
|
||||
ActivityNotificationSettings: undefined
|
||||
MiscellaneousNotificationSettings: undefined
|
||||
InterestsSettings: undefined
|
||||
AboutSettings: undefined
|
||||
AppIconSettings: undefined
|
||||
|
||||
@@ -58,18 +58,6 @@ export const router = new Router<AllNavigatableRoutes>({
|
||||
AboutSettings: '/settings/about',
|
||||
AppIconSettings: '/settings/app-icon',
|
||||
NotificationSettings: '/settings/notifications',
|
||||
ReplyNotificationSettings: '/settings/notifications/replies',
|
||||
MentionNotificationSettings: '/settings/notifications/mentions',
|
||||
QuoteNotificationSettings: '/settings/notifications/quotes',
|
||||
LikeNotificationSettings: '/settings/notifications/likes',
|
||||
RepostNotificationSettings: '/settings/notifications/reposts',
|
||||
NewFollowerNotificationSettings: '/settings/notifications/new-followers',
|
||||
LikesOnRepostsNotificationSettings:
|
||||
'/settings/notifications/likes-on-reposts',
|
||||
RepostsOnRepostsNotificationSettings:
|
||||
'/settings/notifications/reposts-on-reposts',
|
||||
ActivityNotificationSettings: '/settings/notifications/activity',
|
||||
MiscellaneousNotificationSettings: '/settings/notifications/miscellaneous',
|
||||
FindContactsSettings: '/settings/find-contacts',
|
||||
// support
|
||||
Support: '/support',
|
||||
|
||||
@@ -50,6 +50,7 @@ export function MessagesSettingsScreenInner({}: Props) {
|
||||
did: currentAccount!.did,
|
||||
})
|
||||
const {preferences, setPref} = useBackgroundNotificationPreferences()
|
||||
|
||||
const exportCarControl = Dialog.useDialogControl()
|
||||
|
||||
const isGroupChatEnabled = ax.features.enabled(ax.features.GroupChatsEnable)
|
||||
@@ -233,7 +234,7 @@ export function MessagesSettingsScreenInner({}: Props) {
|
||||
value={preferences.playSoundChat}
|
||||
style={[a.flex_row, a.align_center, a.justify_between]}
|
||||
onChange={onSelectSoundSetting}>
|
||||
<BellIcon style={[a.mr_2xs, t.atoms.text]} size="md" />
|
||||
<BellIcon style={[a.mr_2xs, t.atoms.text]} size="lg" />
|
||||
<Text
|
||||
style={[
|
||||
a.flex_1,
|
||||
@@ -251,19 +252,19 @@ export function MessagesSettingsScreenInner({}: Props) {
|
||||
)}
|
||||
<View style={[a.px_xl]}>
|
||||
<Toggle.Item
|
||||
label={l`Export chat data`}
|
||||
label={l`Export my chat data`}
|
||||
name="playSoundChat"
|
||||
value={preferences.playSoundChat}
|
||||
style={[a.flex_row, a.align_center, a.justify_between]}
|
||||
onChange={() => {
|
||||
exportCarControl.open()
|
||||
}}>
|
||||
<CarIcon style={[a.mr_2xs, t.atoms.text]} size="md" />
|
||||
<CarIcon style={[a.mr_2xs, t.atoms.text]} size="lg" />
|
||||
<Text
|
||||
style={[a.flex_1, a.text_md, a.font_semi_bold, t.atoms.text]}>
|
||||
<Trans>Export chat data</Trans>
|
||||
<Trans>Export my chat data</Trans>
|
||||
</Text>
|
||||
<ChevronRightIcon style={[a.ml_2xs, t.atoms.text]} size="md" />
|
||||
<ChevronRightIcon style={[a.ml_2xs, t.atoms.text]} size="lg" />
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
<Divider style={{marginVertical: 10}} />
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
import {useCallback, useMemo} from 'react'
|
||||
import {type ListRenderItemInfo, Text as RNText, View} from 'react-native'
|
||||
import {type ModerationOpts} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {logger} from '#/logger'
|
||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useActivitySubscriptionsQuery} from '#/state/queries/activity-subscriptions'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {List} from '#/view/com/util/List'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {SubscribeProfileDialog} from '#/components/activity-notifications/SubscribeProfileDialog'
|
||||
import * as Admonition from '#/components/Admonition'
|
||||
import {Button, ButtonText} from '#/components/Button'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
import {
|
||||
BellRinging_Filled_Corner0_Rounded as BellRingingFilledIcon,
|
||||
BellRinging_Stroke2_Corner0_Rounded as BellRingingIcon,
|
||||
} from '#/components/icons/BellRinging'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {InlineLinkText} from '#/components/Link'
|
||||
import {ListFooter} from '#/components/Lists'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import {Text} from '#/components/Typography'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'ActivityNotificationSettings'
|
||||
>
|
||||
export function ActivityNotificationSettingsScreen({}: Props) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
const moderationOpts = useModerationOpts()
|
||||
|
||||
const {
|
||||
data: subscriptions,
|
||||
isPending,
|
||||
error,
|
||||
isFetchingNextPage,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
} = useActivitySubscriptionsQuery()
|
||||
|
||||
const items = useMemo(() => {
|
||||
if (!subscriptions) return []
|
||||
return subscriptions?.pages.flatMap(page => page.subscriptions)
|
||||
}, [subscriptions])
|
||||
|
||||
const renderItem = useCallback(
|
||||
({item}: ListRenderItemInfo<bsky.profile.AnyProfileView>) => {
|
||||
if (!moderationOpts) return null
|
||||
return (
|
||||
<ActivitySubscriptionCard
|
||||
profile={item}
|
||||
moderationOpts={moderationOpts}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[moderationOpts],
|
||||
)
|
||||
|
||||
const onEndReached = useCallback(async () => {
|
||||
if (isFetchingNextPage || !hasNextPage || isError) return
|
||||
try {
|
||||
await fetchNextPage()
|
||||
} catch (err) {
|
||||
logger.error('Failed to load more likes', {message: err})
|
||||
}
|
||||
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<List
|
||||
ListHeaderComponent={
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={BellRingingIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Activity from others</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Get notified about posts and replies from accounts you
|
||||
choose.
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition.Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition.Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="subscribedPost"
|
||||
preference={preferences?.subscribedPost}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
}
|
||||
data={items}
|
||||
keyExtractor={keyExtractor}
|
||||
renderItem={renderItem}
|
||||
onEndReached={onEndReached}
|
||||
onEndReachedThreshold={4}
|
||||
ListEmptyComponent={
|
||||
error ? null : (
|
||||
<View style={[a.px_xl, a.py_md]}>
|
||||
{!isPending ? (
|
||||
<Admonition.Outer type="tip">
|
||||
<Admonition.Row>
|
||||
<Admonition.Icon />
|
||||
<Admonition.Content>
|
||||
<Admonition.Text>
|
||||
<Trans>
|
||||
Enable notifications for an account by visiting their
|
||||
profile and pressing the{' '}
|
||||
<RNText
|
||||
style={[
|
||||
a.font_semi_bold,
|
||||
t.atoms.text_contrast_high,
|
||||
]}>
|
||||
bell icon
|
||||
</RNText>{' '}
|
||||
<BellRingingFilledIcon
|
||||
size="xs"
|
||||
style={t.atoms.text_contrast_high}
|
||||
/>
|
||||
.
|
||||
</Trans>
|
||||
</Admonition.Text>
|
||||
<Admonition.Text>
|
||||
<Trans>
|
||||
If you want to restrict who can receive notifications
|
||||
for your account's activity, you can change this in{' '}
|
||||
<InlineLinkText
|
||||
label={_(msg`Privacy and Security settings`)}
|
||||
to={{screen: 'ActivityPrivacySettings'}}
|
||||
style={[a.font_semi_bold]}>
|
||||
Settings → Privacy and Security
|
||||
</InlineLinkText>
|
||||
.
|
||||
</Trans>
|
||||
</Admonition.Text>
|
||||
</Admonition.Content>
|
||||
</Admonition.Row>
|
||||
</Admonition.Outer>
|
||||
) : (
|
||||
<View style={[a.flex_1, a.align_center, a.pt_xl]}>
|
||||
<Loader size="lg" />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
ListFooterComponent={
|
||||
<ListFooter
|
||||
style={[items.length === 0 && a.border_transparent]}
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
error={cleanError(error)}
|
||||
onRetry={fetchNextPage}
|
||||
hasNextPage={hasNextPage}
|
||||
/>
|
||||
}
|
||||
windowSize={11}
|
||||
/>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
|
||||
function keyExtractor(item: bsky.profile.AnyProfileView) {
|
||||
return item.did
|
||||
}
|
||||
|
||||
function ActivitySubscriptionCard({
|
||||
profile: profileUnshadowed,
|
||||
moderationOpts,
|
||||
}: {
|
||||
profile: bsky.profile.AnyProfileView
|
||||
moderationOpts: ModerationOpts
|
||||
}) {
|
||||
const profile = useProfileShadow(profileUnshadowed)
|
||||
const control = useDialogControl()
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
|
||||
const preview = useMemo(() => {
|
||||
const actSub = profile.viewer?.activitySubscription
|
||||
if (actSub?.post && actSub?.reply) {
|
||||
return _(msg`Posts, Replies`)
|
||||
} else if (actSub?.post) {
|
||||
return _(msg`Posts`)
|
||||
} else if (actSub?.reply) {
|
||||
return _(msg`Replies`)
|
||||
}
|
||||
return _(msg`None`)
|
||||
}, [_, profile.viewer?.activitySubscription])
|
||||
|
||||
return (
|
||||
<View style={[a.py_md, a.px_xl, a.border_t, t.atoms.border_contrast_low]}>
|
||||
<ProfileCard.Outer>
|
||||
<ProfileCard.Header>
|
||||
<ProfileCard.Avatar
|
||||
profile={profile}
|
||||
moderationOpts={moderationOpts}
|
||||
/>
|
||||
<View style={[a.flex_1, a.gap_2xs]}>
|
||||
<ProfileCard.NameAndHandle
|
||||
profile={profile}
|
||||
moderationOpts={moderationOpts}
|
||||
inline
|
||||
/>
|
||||
<Text style={[a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||
{preview}
|
||||
</Text>
|
||||
</View>
|
||||
<Button
|
||||
label={_(
|
||||
msg`Edit notifications from ${createSanitizedDisplayName(
|
||||
profile,
|
||||
)}`,
|
||||
)}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="solid"
|
||||
onPress={control.open}>
|
||||
<ButtonText>
|
||||
<Trans>Edit</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</ProfileCard.Header>
|
||||
</ProfileCard.Outer>
|
||||
|
||||
<SubscribeProfileDialog
|
||||
control={control}
|
||||
profile={profile}
|
||||
moderationOpts={moderationOpts}
|
||||
includeProfile
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Heart2_Stroke2_Corner0_Rounded as HeartIcon} from '#/components/icons/Heart2'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'LikeNotificationSettings'
|
||||
>
|
||||
export function LikeNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={HeartIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Likes</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people like your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls name="like" preference={preferences?.like} />
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {LikeRepost_Stroke2_Corner2_Rounded as LikeRepostIcon} from '#/components/icons/Heart2'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'LikesOnRepostsNotificationSettings'
|
||||
>
|
||||
export function LikesOnRepostsNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={LikeRepostIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Likes of your reposts</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Get notifications when people like posts that you've reposted.
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="likeViaRepost"
|
||||
preference={preferences?.likeViaRepost}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {At_Stroke2_Corner2_Rounded as AtIcon} from '#/components/icons/At'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'MentionNotificationSettings'
|
||||
>
|
||||
export function MentionNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={AtIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Mentions</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people mention you.</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="mention"
|
||||
preference={preferences?.mention}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Shapes_Stroke2_Corner0_Rounded as ShapesIcon} from '#/components/icons/Shapes'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'MiscellaneousNotificationSettings'
|
||||
>
|
||||
export function MiscellaneousNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={ShapesIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Everything else</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Notifications for everything else, such as when someone joins
|
||||
via one of your starter packs.
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="starterpackJoined"
|
||||
preference={preferences?.starterpackJoined}
|
||||
syncOthers={['verified', 'unverified']}
|
||||
allowDisableInApp={false}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {PersonPlus_Stroke2_Corner2_Rounded as PersonPlusIcon} from '#/components/icons/Person'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'NewFollowerNotificationSettings'
|
||||
>
|
||||
export function NewFollowerNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={PersonPlusIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>New followers</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people follow you.</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="follow"
|
||||
preference={preferences?.follow}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {CloseQuote_Stroke2_Corner0_Rounded as CloseQuoteIcon} from '#/components/icons/Quote'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'QuoteNotificationSettings'
|
||||
>
|
||||
export function QuoteNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={CloseQuoteIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Quotes</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people quote your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls name="quote" preference={preferences?.quote} />
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Bubble_Stroke2_Corner2_Rounded as BubbleIcon} from '#/components/icons/Bubble'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'ReplyNotificationSettings'
|
||||
>
|
||||
export function ReplyNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={BubbleIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Replies</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Get notifications when people reply to your posts.
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls name="reply" preference={preferences?.reply} />
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/Repost'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'RepostNotificationSettings'
|
||||
>
|
||||
export function RepostNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={RepostIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Reposts</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people repost your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="repost"
|
||||
preference={preferences?.repost}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {
|
||||
type AllNavigatorParams,
|
||||
type NativeStackScreenProps,
|
||||
} from '#/lib/routes/types'
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {RepostRepost_Stroke2_Corner2_Rounded as RepostRepostIcon} from '#/components/icons/Repost'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as SettingsList from '../components/SettingsList'
|
||||
import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle'
|
||||
import {PreferenceControls} from './components/PreferenceControls'
|
||||
|
||||
type Props = NativeStackScreenProps<
|
||||
AllNavigatorParams,
|
||||
'RepostsOnRepostsNotificationSettings'
|
||||
>
|
||||
export function RepostsOnRepostsNotificationSettingsScreen({}: Props) {
|
||||
const {data: preferences, isError} = useNotificationSettingsQuery()
|
||||
|
||||
return (
|
||||
<Layout.Screen>
|
||||
<Layout.Header.Outer>
|
||||
<Layout.Header.BackButton />
|
||||
<Layout.Header.Content>
|
||||
<Layout.Header.TitleText>
|
||||
<Trans>Notifications</Trans>
|
||||
</Layout.Header.TitleText>
|
||||
</Layout.Header.Content>
|
||||
<Layout.Header.Slot />
|
||||
</Layout.Header.Outer>
|
||||
<Layout.Content>
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item style={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={RepostRepostIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
bold
|
||||
titleText={<Trans>Reposts of your reposts</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Get notifications when people repost posts that you've
|
||||
reposted.
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
</SettingsList.Item>
|
||||
{isError ? (
|
||||
<View style={[a.px_lg, a.pt_md]}>
|
||||
<Admonition type="error">
|
||||
<Trans>Failed to load notification settings.</Trans>
|
||||
</Admonition>
|
||||
</View>
|
||||
) : (
|
||||
<PreferenceControls
|
||||
name="repostViaRepost"
|
||||
preference={preferences?.repostViaRepost}
|
||||
/>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyNotificationDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useNotificationSettingsUpdateMutation} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a, platform, useTheme} from '#/alf'
|
||||
@@ -28,6 +26,7 @@ export function PreferenceControls({
|
||||
preference?:
|
||||
| AppBskyNotificationDefs.Preference
|
||||
| AppBskyNotificationDefs.FilterablePreference
|
||||
| AppBskyNotificationDefs.ChatPreference
|
||||
allowDisableInApp?: boolean
|
||||
}) {
|
||||
if (!preference)
|
||||
@@ -58,16 +57,17 @@ export function Inner({
|
||||
preference:
|
||||
| AppBskyNotificationDefs.Preference
|
||||
| AppBskyNotificationDefs.FilterablePreference
|
||||
| AppBskyNotificationDefs.ChatPreference
|
||||
allowDisableInApp: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const ax = useAnalytics()
|
||||
const {mutate} = useNotificationSettingsUpdateMutation()
|
||||
|
||||
const channels = useMemo(() => {
|
||||
const arr = []
|
||||
if (preference.list) arr.push('list')
|
||||
if ('list' in preference && preference.list) arr.push('list')
|
||||
if (preference.push) arr.push('push')
|
||||
return arr
|
||||
}, [preference])
|
||||
@@ -75,15 +75,18 @@ export function Inner({
|
||||
const onChangeChannels = (change: string[]) => {
|
||||
const newPreference = {
|
||||
...preference,
|
||||
list: change.includes('list'),
|
||||
...('list' in preference ? {list: change.includes('list')} : {}),
|
||||
push: change.includes('push'),
|
||||
} satisfies typeof preference
|
||||
} as typeof preference
|
||||
|
||||
ax.metric('activityPreference:changeChannels', {
|
||||
const metrics: {name: string; push: boolean; list?: boolean} = {
|
||||
name,
|
||||
push: newPreference.push,
|
||||
list: newPreference.list,
|
||||
})
|
||||
}
|
||||
if ('list' in newPreference) {
|
||||
metrics.list = newPreference.list
|
||||
}
|
||||
ax.metric('activityPreference:changeChannels', metrics)
|
||||
|
||||
mutate({
|
||||
[name]: newPreference,
|
||||
@@ -92,7 +95,7 @@ export function Inner({
|
||||
}
|
||||
|
||||
const onChangeFilter = ([change]: string[]) => {
|
||||
if (change !== 'all' && change !== 'follows')
|
||||
if (change !== 'all' && change !== 'follows' && change !== 'accepted')
|
||||
throw new Error('Invalid filter')
|
||||
|
||||
const newPreference = {
|
||||
@@ -109,15 +112,15 @@ export function Inner({
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[a.px_xl, a.pt_md, a.gap_sm]}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<Toggle.Group
|
||||
type="checkbox"
|
||||
label={_(msg`Select your preferred notification channels`)}
|
||||
label={l`Select your preferred notification channels`}
|
||||
values={channels}
|
||||
onChange={onChangeChannels}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<Toggle.Item
|
||||
label={_(msg`Receive push notifications`)}
|
||||
label={l`Receive push notifications`}
|
||||
name="push"
|
||||
style={[
|
||||
a.py_xs,
|
||||
@@ -134,7 +137,7 @@ export function Inner({
|
||||
</Toggle.Item>
|
||||
{allowDisableInApp && (
|
||||
<Toggle.Item
|
||||
label={_(msg`Receive in-app notifications`)}
|
||||
label={l`Receive in-app notifications`}
|
||||
name="list"
|
||||
style={[
|
||||
a.py_xs,
|
||||
@@ -160,39 +163,44 @@ export function Inner({
|
||||
</Text>
|
||||
<Toggle.Group
|
||||
type="radio"
|
||||
label={_(msg`Filter who you receive notifications from`)}
|
||||
label={l`Filter who you receive notifications from`}
|
||||
values={[preference.include]}
|
||||
onChange={onChangeFilter}
|
||||
disabled={channels.length === 0}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<Toggle.Item
|
||||
label={_(msg`Everyone`)}
|
||||
name="all"
|
||||
style={[a.flex_row, a.py_xs, a.gap_sm]}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.LabelText
|
||||
style={[
|
||||
channels.length > 0 && t.atoms.text,
|
||||
a.font_normal,
|
||||
a.text_md,
|
||||
]}>
|
||||
<Trans>Everyone</Trans>
|
||||
</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item
|
||||
label={_(msg`People I follow`)}
|
||||
name="follows"
|
||||
style={[a.flex_row, a.py_xs, a.gap_sm]}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.LabelText
|
||||
style={[
|
||||
channels.length > 0 && t.atoms.text,
|
||||
a.font_normal,
|
||||
a.text_md,
|
||||
]}>
|
||||
<Trans>People I follow</Trans>
|
||||
</Toggle.LabelText>
|
||||
<Toggle.Item highlightRow label={l`Everyone`} name="all">
|
||||
{({selected}) => (
|
||||
<Toggle.RadioWithLabel
|
||||
label={l`Everyone`}
|
||||
selected={selected}
|
||||
/>
|
||||
)}
|
||||
</Toggle.Item>
|
||||
{name === 'chat' ? (
|
||||
<Toggle.Item
|
||||
highlightRow
|
||||
label={l`Accepted conversations`}
|
||||
name="accepted">
|
||||
{({selected}) => (
|
||||
<Toggle.RadioWithLabel
|
||||
label={l`Accepted conversations`}
|
||||
selected={selected}
|
||||
/>
|
||||
)}
|
||||
</Toggle.Item>
|
||||
) : (
|
||||
<Toggle.Item
|
||||
highlightRow
|
||||
label={l`People I follow`}
|
||||
name="follows">
|
||||
{({selected}) => (
|
||||
<Toggle.RadioWithLabel
|
||||
label={l`People I follow`}
|
||||
selected={selected}
|
||||
/>
|
||||
)}
|
||||
</Toggle.Item>
|
||||
)}
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
</>
|
||||
|
||||
@@ -2,9 +2,7 @@ import {useEffect} from 'react'
|
||||
import {Linking, View} from 'react-native'
|
||||
import * as Notification from 'expo-notifications'
|
||||
import {type AppBskyNotificationDefs} from '@atproto/api'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {useAppState} from '#/lib/appState'
|
||||
@@ -15,6 +13,8 @@ import {
|
||||
import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {NotificationSettingsDialog} from '#/components/dialogs/NotificationSettingsDialog'
|
||||
import {At_Stroke2_Corner2_Rounded as AtIcon} from '#/components/icons/At'
|
||||
import {BellRinging_Stroke2_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging'
|
||||
import {Bubble_Stroke2_Corner2_Rounded as BubbleIcon} from '#/components/icons/Bubble'
|
||||
@@ -39,10 +39,21 @@ const RQKEY = ['notification-permissions']
|
||||
|
||||
type Props = NativeStackScreenProps<AllNavigatorParams, 'NotificationSettings'>
|
||||
export function NotificationSettingsScreen({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
const queryClient = useQueryClient()
|
||||
const {data: settings, isError} = useNotificationSettingsQuery()
|
||||
|
||||
const likeDialogControl = Dialog.useDialogControl()
|
||||
const followDialogControl = Dialog.useDialogControl()
|
||||
const replyDialogControl = Dialog.useDialogControl()
|
||||
const mentionDialogControl = Dialog.useDialogControl()
|
||||
const quoteDialogControl = Dialog.useDialogControl()
|
||||
const repostDialogControl = Dialog.useDialogControl()
|
||||
const activityDialogControl = Dialog.useDialogControl()
|
||||
const likeRepostDialogControl = Dialog.useDialogControl()
|
||||
const repostRepostDialogControl = Dialog.useDialogControl()
|
||||
const miscDialogControl = Dialog.useDialogControl()
|
||||
|
||||
const {data: permissions, refetch} = useQuery({
|
||||
queryKey: RQKEY,
|
||||
queryFn: async () => {
|
||||
@@ -54,7 +65,7 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
const appState = useAppState()
|
||||
useEffect(() => {
|
||||
if (appState === 'active') {
|
||||
refetch()
|
||||
void refetch()
|
||||
}
|
||||
}, [appState, refetch])
|
||||
|
||||
@@ -76,10 +87,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
],
|
||||
)
|
||||
} catch {
|
||||
Linking.openSettings()
|
||||
void Linking.openSettings()
|
||||
}
|
||||
} else if (IS_IOS) {
|
||||
Linking.openSettings()
|
||||
void Linking.openSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,8 +111,8 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
{permissions && !permissions.granted && (
|
||||
<>
|
||||
<SettingsList.PressableItem
|
||||
label={_(msg`Enable push notifications`)}
|
||||
onPress={onRequestPermissions}>
|
||||
label={l`Enable push notifications`}
|
||||
onPress={() => void onRequestPermissions()}>
|
||||
<SettingsList.ItemIcon icon={HapticIcon} />
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Enable push notifications</Trans>
|
||||
@@ -118,9 +129,9 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
</View>
|
||||
)}
|
||||
<View style={[a.gap_sm]}>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for like notifications`)}
|
||||
to={{screen: 'LikeNotificationSettings'}}
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for like notifications`}
|
||||
onPress={likeDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={HeartIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -128,10 +139,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
subtitleText={<SettingPreview preference={settings?.like} />}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for new follower notifications`)}
|
||||
to={{screen: 'NewFollowerNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for new follower notifications`}
|
||||
onPress={followDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={PersonPlusIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -139,10 +150,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
subtitleText={<SettingPreview preference={settings?.follow} />}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for reply notifications`)}
|
||||
to={{screen: 'ReplyNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for reply notifications`}
|
||||
onPress={replyDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={BubbleIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -150,10 +161,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
subtitleText={<SettingPreview preference={settings?.reply} />}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for mention notifications`)}
|
||||
to={{screen: 'MentionNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for mention notifications`}
|
||||
onPress={mentionDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={AtIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -161,10 +172,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
subtitleText={<SettingPreview preference={settings?.mention} />}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for quote notifications`)}
|
||||
to={{screen: 'QuoteNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for quote notifications`}
|
||||
onPress={quoteDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={CloseQuoteIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -172,10 +183,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
subtitleText={<SettingPreview preference={settings?.quote} />}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for repost notifications`)}
|
||||
to={{screen: 'RepostNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for repost notifications`}
|
||||
onPress={repostDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={RepostIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -183,10 +194,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
subtitleText={<SettingPreview preference={settings?.repost} />}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for activity from others`)}
|
||||
to={{screen: 'ActivityNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for activity from others`}
|
||||
onPress={activityDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={BellRingingIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -196,12 +207,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(
|
||||
msg`Settings for notifications for likes of your reposts`,
|
||||
)}
|
||||
to={{screen: 'LikesOnRepostsNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for notifications for likes of your reposts`}
|
||||
onPress={likeRepostDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={LikeRepostIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -211,12 +220,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(
|
||||
msg`Settings for notifications for reposts of your reposts`,
|
||||
)}
|
||||
to={{screen: 'RepostsOnRepostsNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for notifications for reposts of your reposts`}
|
||||
onPress={repostRepostDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={RepostRepostIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -226,10 +233,10 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
<SettingsList.LinkItem
|
||||
label={_(msg`Settings for notifications for everything else`)}
|
||||
to={{screen: 'MiscellaneousNotificationSettings'}}
|
||||
</SettingsList.PressableItem>
|
||||
<SettingsList.PressableItem
|
||||
label={l`Settings for notifications for everything else`}
|
||||
onPress={miscDialogControl.open}
|
||||
contentContainerStyle={[a.align_start]}>
|
||||
<SettingsList.ItemIcon icon={ShapesIcon} />
|
||||
<ItemTextWithSubtitle
|
||||
@@ -241,10 +248,105 @@ export function NotificationSettingsScreen({}: Props) {
|
||||
}
|
||||
showSkeleton={!settings}
|
||||
/>
|
||||
</SettingsList.LinkItem>
|
||||
</SettingsList.PressableItem>
|
||||
</View>
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
<NotificationSettingsDialog
|
||||
control={likeDialogControl}
|
||||
name="like"
|
||||
icon={HeartIcon}
|
||||
titleText={<Trans>Likes</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people like your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={followDialogControl}
|
||||
name="follow"
|
||||
icon={PersonPlusIcon}
|
||||
titleText={<Trans>New followers</Trans>}
|
||||
subtitleText={<Trans>Get notifications when people follow you.</Trans>}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={replyDialogControl}
|
||||
name="reply"
|
||||
icon={BubbleIcon}
|
||||
titleText={<Trans>Replies</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people reply to your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={mentionDialogControl}
|
||||
name="mention"
|
||||
icon={AtIcon}
|
||||
titleText={<Trans>Mentions</Trans>}
|
||||
subtitleText={<Trans>Get notifications when people mention you.</Trans>}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={quoteDialogControl}
|
||||
name="quote"
|
||||
icon={CloseQuoteIcon}
|
||||
titleText={<Trans>Quotes</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people quote your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={repostDialogControl}
|
||||
name="repost"
|
||||
icon={RepostIcon}
|
||||
titleText={<Trans>Reposts</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people repost your posts.</Trans>
|
||||
}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={activityDialogControl}
|
||||
name="subscribedPost"
|
||||
icon={BellRingingIcon}
|
||||
titleText={<Trans>Activity from others</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Get notifications when there's activity on posts you're subscribed
|
||||
to.
|
||||
</Trans>
|
||||
}
|
||||
allowDisableInApp={false}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={likeRepostDialogControl}
|
||||
name="likeViaRepost"
|
||||
icon={LikeRepostIcon}
|
||||
titleText={<Trans>Likes of your reposts</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people like your reposts.</Trans>
|
||||
}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={repostRepostDialogControl}
|
||||
name="repostViaRepost"
|
||||
icon={RepostRepostIcon}
|
||||
titleText={<Trans>Reposts of your reposts</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Get notifications when people repost your reposts.</Trans>
|
||||
}
|
||||
/>
|
||||
<NotificationSettingsDialog
|
||||
control={miscDialogControl}
|
||||
name="starterpackJoined"
|
||||
syncOthers={['verified', 'unverified']}
|
||||
icon={ShapesIcon}
|
||||
titleText={<Trans>Everything else</Trans>}
|
||||
subtitleText={
|
||||
<Trans>
|
||||
Get notifications for starter pack joins, verification, and other
|
||||
activity.
|
||||
</Trans>
|
||||
}
|
||||
allowDisableInApp={false}
|
||||
/>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
@@ -256,38 +358,38 @@ function SettingPreview({
|
||||
| AppBskyNotificationDefs.Preference
|
||||
| AppBskyNotificationDefs.FilterablePreference
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {t: l} = useLingui()
|
||||
if (!preference) {
|
||||
return null
|
||||
} else {
|
||||
if ('include' in preference) {
|
||||
if (preference.include === 'all') {
|
||||
if (preference.list && preference.push) {
|
||||
return _(msg`In-app, Push, Everyone`)
|
||||
return l`In-app, push, everyone`
|
||||
} else if (preference.list) {
|
||||
return _(msg`In-app, Everyone`)
|
||||
return l`In-app, everyone`
|
||||
} else if (preference.push) {
|
||||
return _(msg`Push, Everyone`)
|
||||
return l`Push, everyone`
|
||||
}
|
||||
} else if (preference.include === 'follows') {
|
||||
if (preference.list && preference.push) {
|
||||
return _(msg`In-app, Push, People you follow`)
|
||||
return l`In-app, push, people you follow`
|
||||
} else if (preference.list) {
|
||||
return _(msg`In-app, People you follow`)
|
||||
return l`In-app, people you follow`
|
||||
} else if (preference.push) {
|
||||
return _(msg`Push, People you follow`)
|
||||
return l`Push, people you follow`
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (preference.list && preference.push) {
|
||||
return _(msg`In-app, Push`)
|
||||
return l`In-app, push`
|
||||
} else if (preference.list) {
|
||||
return _(msg`In-app`)
|
||||
return l`In-app`
|
||||
} else if (preference.push) {
|
||||
return _(msg`Push`)
|
||||
return l`Push`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _(msg`Off`)
|
||||
return l`Off`
|
||||
}
|
||||
|
||||
@@ -115,12 +115,12 @@ export function ExportCarDialog({
|
||||
<Button
|
||||
color="primary"
|
||||
size="large"
|
||||
label={l`Download CAR file`}
|
||||
label={l`Download profile data`}
|
||||
disabled={!!loading}
|
||||
onPress={() => void download()}>
|
||||
<ButtonIcon icon={loading === 'repo' ? Loader : DownloadIcon} />
|
||||
<ButtonText>
|
||||
<Trans context="button">Download CAR file</Trans>
|
||||
<Trans context="button">Download profile data</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export function useNotificationSettingsUpdateMutation() {
|
||||
},
|
||||
onError: e => {
|
||||
logger.error('Could not update notification settings', {message: e})
|
||||
queryClient.invalidateQueries({queryKey: RQKEY})
|
||||
void queryClient.invalidateQueries({queryKey: RQKEY})
|
||||
Toast.show(t`Could not update notification settings`, {
|
||||
type: 'error',
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user