[Settings] Thread prefs revamp (#5772)
* thread preferences screen * minor tweaks * more spacing * replace gate with IS_INTERNAL * [Settings] Following feed prefs revamp (#5773) * gated new settings screen * Following feed prefs * Update src/screens/Settings/FollowingFeedPreferences.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Settings/FollowingFeedPreferences.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * replace pref following feed gate * Update src/screens/Settings/FollowingFeedPreferences.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * use "Experimental" as the header --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * [Settings] External media prefs revamp (#5774) * gated new settings screen * external media prefs revamp * replace gate ext media embeds * Update src/screens/Settings/ExternalMediaPreferences.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * add imports for translation * alternate list style on native --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * [Settings] Languages revamp (partial) (#5775) * language settings (lazy restyle) * replace gate * fix text determining flex space * [Settings] App passwords revamp (#5777) * rework app passwords screen * Apply surfdude's copy changes Thanks @surfdude29! Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * format * replace gate * use admonition for input error and animate --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * [Settings] Change handle dialog (#5781) * new change handle dialog * animations native only * overflow hidden on togglebutton animation * add a low-contrast border * extract out copybutton * finish change handle dialog * invalidate query on success * web fixes * error message for rate limit exceeded * typo * em dash! Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * another em dash Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * set maxwidth of suffixtext * Copy tweak Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * [Settings] Notifs settings revamp (#5884) * rename, move, and restyle notif settings * bold "experimental:" --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import Animated, {
|
||||
FadeIn,
|
||||
FadeOut,
|
||||
LayoutAnimationConfig,
|
||||
LinearTransition,
|
||||
StretchOutY,
|
||||
} from 'react-native-reanimated'
|
||||
import {ComAtprotoServerListAppPasswords} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||
|
||||
import {CommonNavigatorParams} from '#/lib/routes/types'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {isWeb} from '#/platform/detection'
|
||||
import {
|
||||
useAppPasswordDeleteMutation,
|
||||
useAppPasswordsQuery,
|
||||
} from '#/state/queries/app-passwords'
|
||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import {Admonition, colors} from '#/components/Admonition'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
|
||||
import {Trash_Stroke2_Corner0_Rounded as TrashIcon} from '#/components/icons/Trash'
|
||||
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
||||
import * as Layout from '#/components/Layout'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Prompt from '#/components/Prompt'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {AddAppPasswordDialog} from './components/AddAppPasswordDialog'
|
||||
import * as SettingsList from './components/SettingsList'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppPasswords'>
|
||||
export function AppPasswordsScreen({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const {data: appPasswords, error} = useAppPasswordsQuery()
|
||||
const createAppPasswordControl = useDialogControl()
|
||||
|
||||
return (
|
||||
<Layout.Screen testID="AppPasswordsScreen">
|
||||
<Layout.Header title={_(msg`App Passwords`)} />
|
||||
<Layout.Content>
|
||||
{error ? (
|
||||
<ErrorScreen
|
||||
title={_(msg`Oops!`)}
|
||||
message={_(msg`There was an issue fetching your app passwords`)}
|
||||
details={cleanError(error)}
|
||||
/>
|
||||
) : (
|
||||
<SettingsList.Container>
|
||||
<SettingsList.Item>
|
||||
<Admonition type="tip" style={[a.flex_1]}>
|
||||
<Trans>
|
||||
Use app passwords to sign in to other Bluesky clients without
|
||||
giving full access to your account or password.
|
||||
</Trans>
|
||||
</Admonition>
|
||||
</SettingsList.Item>
|
||||
<SettingsList.Item>
|
||||
<Button
|
||||
label={_(msg`Add App Password`)}
|
||||
size="large"
|
||||
color="primary"
|
||||
variant="solid"
|
||||
onPress={() => createAppPasswordControl.open()}
|
||||
style={[a.flex_1]}>
|
||||
<ButtonIcon icon={PlusIcon} />
|
||||
<ButtonText>
|
||||
<Trans>Add App Password</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</SettingsList.Item>
|
||||
<SettingsList.Divider />
|
||||
<LayoutAnimationConfig skipEntering skipExiting>
|
||||
{appPasswords ? (
|
||||
appPasswords.length > 0 ? (
|
||||
<View style={[a.overflow_hidden]}>
|
||||
{appPasswords.map(appPassword => (
|
||||
<Animated.View
|
||||
key={appPassword.name}
|
||||
style={a.w_full}
|
||||
entering={FadeIn}
|
||||
exiting={isWeb ? FadeOut : StretchOutY}
|
||||
layout={LinearTransition.delay(150)}>
|
||||
<SettingsList.Item>
|
||||
<AppPasswordCard appPassword={appPassword} />
|
||||
</SettingsList.Item>
|
||||
</Animated.View>
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<EmptyState
|
||||
icon="growth"
|
||||
message={_(msg`No app passwords yet`)}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.justify_center,
|
||||
a.align_center,
|
||||
a.py_4xl,
|
||||
]}>
|
||||
<Loader size="xl" />
|
||||
</View>
|
||||
)}
|
||||
</LayoutAnimationConfig>
|
||||
</SettingsList.Container>
|
||||
)}
|
||||
</Layout.Content>
|
||||
|
||||
<AddAppPasswordDialog
|
||||
control={createAppPasswordControl}
|
||||
passwords={appPasswords?.map(p => p.name) || []}
|
||||
/>
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
|
||||
function AppPasswordCard({
|
||||
appPassword,
|
||||
}: {
|
||||
appPassword: ComAtprotoServerListAppPasswords.AppPassword
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {i18n, _} = useLingui()
|
||||
const deleteControl = Prompt.usePromptControl()
|
||||
const {mutateAsync: deleteMutation} = useAppPasswordDeleteMutation()
|
||||
|
||||
const onDelete = useCallback(async () => {
|
||||
await deleteMutation({name: appPassword.name})
|
||||
Toast.show(_(msg`App password deleted`))
|
||||
}, [deleteMutation, appPassword.name, _])
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.w_full,
|
||||
a.border,
|
||||
a.rounded_sm,
|
||||
a.px_md,
|
||||
a.py_sm,
|
||||
t.atoms.bg_contrast_25,
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.justify_between,
|
||||
a.align_start,
|
||||
a.w_full,
|
||||
a.gap_sm,
|
||||
]}>
|
||||
<View style={[a.gap_xs]}>
|
||||
<Text style={[t.atoms.text, a.text_md, a.font_bold]}>
|
||||
{appPassword.name}
|
||||
</Text>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
<Trans>
|
||||
Created{' '}
|
||||
{i18n.date(appPassword.createdAt, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})}
|
||||
</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
<Button
|
||||
label={_(msg`Delete app password`)}
|
||||
variant="ghost"
|
||||
color="negative"
|
||||
size="small"
|
||||
style={[a.bg_transparent]}
|
||||
onPress={() => deleteControl.open()}>
|
||||
<ButtonIcon icon={TrashIcon} />
|
||||
</Button>
|
||||
</View>
|
||||
{appPassword.privileged && (
|
||||
<View style={[a.flex_row, a.gap_sm, a.align_center, a.mt_md]}>
|
||||
<WarningIcon style={[{color: colors.warning[t.scheme]}]} />
|
||||
<Text style={t.atoms.text_contrast_high}>
|
||||
<Trans>Allows access to direct messages</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<Prompt.Basic
|
||||
control={deleteControl}
|
||||
title={_(msg`Delete app password?`)}
|
||||
description={_(
|
||||
msg`Are you sure you want to delete the app password "${appPassword.name}"?`,
|
||||
)}
|
||||
onConfirm={onDelete}
|
||||
confirmButtonCta={_(msg`Delete`)}
|
||||
confirmButtonColor="negative"
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user