import React from 'react' import { ActivityIndicator, StyleSheet, TextStyle, TouchableOpacity, View, ViewStyle, } from 'react-native' import { useFocusEffect, useNavigation, StackActions, } from '@react-navigation/native' import { FontAwesomeIcon, FontAwesomeIconStyle, } from '@fortawesome/react-native-fontawesome' import {observer} from 'mobx-react-lite' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import * as AppInfo from 'lib/app-info' import {useStores} from 'state/index' import {s, colors} from 'lib/styles' import {ScrollView} from '../com/util/Views' import {ViewHeader} from '../com/util/ViewHeader' import {Link} from '../com/util/Link' import {Text} from '../com/util/text/Text' import * as Toast from '../com/util/Toast' import {UserAvatar} from '../com/util/UserAvatar' import {DropdownButton} from 'view/com/util/forms/DropdownButton' import {usePalette} from 'lib/hooks/usePalette' import {useCustomPalette} from 'lib/hooks/useCustomPalette' import {AccountData} from 'state/models/session' import {useAnalytics} from 'lib/analytics' import {NavigationProp} from 'lib/routes/types' import {isDesktopWeb} from 'platform/detection' import {pluralize} from 'lib/strings/helpers' import {formatCount} from 'view/com/util/numeric/format' type Props = NativeStackScreenProps export const SettingsScreen = withAuthRequired( observer(function Settings({}: Props) { const pal = usePalette('default') const store = useStores() const navigation = useNavigation() const {screen, track} = useAnalytics() const [isSwitching, setIsSwitching] = React.useState(false) const primaryBg = useCustomPalette({ light: {backgroundColor: colors.blue0}, dark: {backgroundColor: colors.blue6}, }) const primaryText = useCustomPalette({ light: {color: colors.blue3}, dark: {color: colors.blue2}, }) const dangerBg = useCustomPalette({ light: {backgroundColor: colors.red1}, dark: {backgroundColor: colors.red7}, }) const dangerText = useCustomPalette({ light: {color: colors.red4}, dark: {color: colors.red2}, }) useFocusEffect( React.useCallback(() => { screen('Settings') store.shell.setMinimalShellMode(false) }, [screen, store]), ) const onPressSwitchAccount = React.useCallback( async (acct: AccountData) => { track('Settings:SwitchAccountButtonClicked') setIsSwitching(true) if (await store.session.resumeSession(acct)) { setIsSwitching(false) navigation.navigate('HomeTab') navigation.dispatch(StackActions.popToTop()) Toast.show(`Signed in as ${acct.displayName || acct.handle}`) return } setIsSwitching(false) Toast.show('Sorry! We need you to enter your password.') navigation.navigate('HomeTab') navigation.dispatch(StackActions.popToTop()) store.session.clear() }, [track, setIsSwitching, navigation, store], ) const onPressAddAccount = React.useCallback(() => { track('Settings:AddAccountButtonClicked') navigation.navigate('HomeTab') navigation.dispatch(StackActions.popToTop()) store.session.clear() }, [track, navigation, store]) const onPressChangeHandle = React.useCallback(() => { track('Settings:ChangeHandleButtonClicked') store.shell.openModal({ name: 'change-handle', onChanged() { setIsSwitching(true) store.session.reloadFromServer().then( () => { setIsSwitching(false) Toast.show('Your handle has been updated') }, err => { store.log.error( 'Failed to reload from server after handle update', {err}, ) setIsSwitching(false) }, ) }, }) }, [track, store, setIsSwitching]) const onPressInviteCodes = React.useCallback(() => { track('Settings:InvitecodesButtonClicked') store.shell.openModal({name: 'invite-codes'}) }, [track, store]) const onPressContentFiltering = React.useCallback(() => { track('Settings:ContentfilteringButtonClicked') store.shell.openModal({name: 'content-filtering-settings'}) }, [track, store]) const onPressContentLanguages = React.useCallback(() => { track('Settings:ContentlanguagesButtonClicked') store.shell.openModal({name: 'content-languages-settings'}) }, [track, store]) const onPressSignout = React.useCallback(() => { track('Settings:SignOutButtonClicked') store.session.logout() }, [track, store]) const onPressDeleteAccount = React.useCallback(() => { store.shell.openModal({name: 'delete-account'}) }, [store]) return ( {store.session.currentSession !== undefined ? ( <> Account Email:{' '} {store.session.currentSession?.email} ) : null} Signed in as {isSwitching ? ( ) : ( {store.me.displayName || store.me.handle} {store.me.handle} Sign out )} {store.session.switchableAccounts.map(account => ( onPressSwitchAccount(account) } accessibilityRole="button" accessibilityLabel={`Switch to ${account.handle}`} accessibilityHint="Switches the account you are logged in to"> {account.displayName || account.handle} {account.handle} ))} Add account Invite a friend 0 ? primaryBg : pal.btn, ]}> 0 ? primaryText : pal.text) as FontAwesomeIconStyle } /> 0 ? pal.link : pal.text}> {formatCount(store.me.invitesAvailable)} invite{' '} {pluralize(store.me.invitesAvailable, 'code')} available Moderation Custom Algorithms Content moderation Muted accounts Blocked accounts Advanced App passwords Content languages Change my handle Danger zone Delete my account Developer tools System log Storybook Build version {AppInfo.appVersion} ({AppInfo.buildVersion}) ) }), ) function AccountDropdownBtn({handle}: {handle: string}) { const store = useStores() const pal = usePalette('default') const items = [ { label: 'Remove account', onPress: () => { store.session.removeAccount(handle) Toast.show('Account removed from quick access') }, }, ] return ( ) } const styles = StyleSheet.create({ dimmed: { opacity: 0.5, }, spacer20: { height: 20, }, heading: { paddingHorizontal: 18, paddingBottom: 6, }, infoLine: { paddingHorizontal: 18, paddingBottom: 6, }, profile: { flexDirection: 'row', marginVertical: 6, borderRadius: 4, paddingVertical: 10, paddingHorizontal: 10, }, linkCard: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, paddingHorizontal: 18, marginBottom: 1, }, linkCardNoIcon: { flexDirection: 'row', alignItems: 'center', paddingVertical: 20, paddingHorizontal: 18, marginBottom: 1, }, avi: { marginRight: 12, }, iconContainer: { alignItems: 'center', justifyContent: 'center', width: 40, height: 40, borderRadius: 30, marginRight: 12, }, buildInfo: { paddingVertical: 8, paddingHorizontal: 18, }, })