diff --git a/src/state/models/ui/shell.ts b/src/state/models/ui/shell.ts
index 47cc0aa825..5eb7177972 100644
--- a/src/state/models/ui/shell.ts
+++ b/src/state/models/ui/shell.ts
@@ -57,6 +57,11 @@ export interface ChangeHandleModal {
onChanged: () => void
}
+export interface ChangePasswordModal {
+ name: 'change-password'
+ onChanged: () => void
+}
+
export interface WaitlistModal {
name: 'waitlist'
}
@@ -70,18 +75,30 @@ export interface ContentFilteringSettingsModal {
}
export type Modal =
- | ConfirmModal
- | EditProfileModal
- | ServerInputModal
- | ReportPostModal
- | ReportAccountModal
- | CropImageModal
- | DeleteAccountModal
- | RepostModal
+ // Account
| ChangeHandleModal
+ | ChangePasswordModal
+ | DeleteAccountModal
+ | EditProfileModal
+
+ // Curation
+ | ContentFilteringSettingsModal
+
+ // Reporting
+ | ReportAccountModal
+ | ReportPostModal
+
+ // Posting
+ | CropImageModal
+ | ServerInputModal
+ | RepostModal
+
+ // Bluesky access
| WaitlistModal
| InviteCodesModal
- | ContentFilteringSettingsModal
+
+ // Generic
+ | ConfirmModal
interface LightboxModel {}
diff --git a/src/state/models/ui/update-account.ts b/src/state/models/ui/update-account.ts
new file mode 100644
index 0000000000..1fe531717c
--- /dev/null
+++ b/src/state/models/ui/update-account.ts
@@ -0,0 +1,20 @@
+import {BskyAgent} from '@atproto/api'
+import {makeAutoObservable} from 'mobx'
+import {RootStoreModel} from '../root-store'
+
+export class AccountModel {
+ serviceUrl = ''
+ constructor(public rootStore: RootStoreModel) {
+ makeAutoObservable(
+ this,
+ {},
+ {
+ autoBind: true,
+ },
+ )
+ }
+
+ updatePassword() {
+ const agent = new BskyAgent({service: this.serviceUrl})
+ }
+}
diff --git a/src/view/com/modals/ChangePassword.tsx b/src/view/com/modals/ChangePassword.tsx
new file mode 100644
index 0000000000..f8660dd1e8
--- /dev/null
+++ b/src/view/com/modals/ChangePassword.tsx
@@ -0,0 +1,84 @@
+import React, {useCallback} from 'react'
+import {StyleSheet, TouchableOpacity, View} from 'react-native'
+import {Text} from '../util/text/Text'
+import {useStores} from 'state/index'
+import {s} from 'lib/styles'
+import {usePalette} from 'lib/hooks/usePalette'
+
+export const snapPoints = ['100%']
+
+interface Props {
+ onChanged: () => void
+}
+
+export function Component({onChanged}: Props) {
+ const pal = usePalette('default')
+ const store = useStores()
+
+ const onCancel = useCallback(() => {
+ store.shell.closeModal()
+ }, [store.shell])
+
+ return (
+
+
+
+
+
+ Cancel
+
+
+
+
+ Change password
+
+
+ {/* {isProcessing ? (
+
+ ) : error && !serviceDescription ? (
+
+
+ Retry
+
+
+ ) : canSave ? ( */}
+
+
+ Save
+
+
+ {/* ) : undefined} */}
+
+
+
+ Enter the email you used to create your account. We'll send you a "reset
+ code" so you can set a new password.
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ instructions: {
+ paddingHorizontal: 18,
+ paddingVertical: 16,
+ },
+ title: {
+ flexDirection: 'row',
+ alignItems: 'baseline',
+ justifyContent: 'space-between',
+ paddingTop: 25,
+ paddingHorizontal: 20,
+ paddingBottom: 15,
+ borderBottomWidth: 1,
+ },
+ titleLeft: {},
+ titleMiddle: {
+ fontSize: 21,
+ },
+ titleRight: {},
+})
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index 3f10ec8365..8101f4dba2 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -14,6 +14,7 @@ import * as RepostModal from './Repost'
import * as ReportAccountModal from './ReportAccount'
import * as DeleteAccountModal from './DeleteAccount'
import * as ChangeHandleModal from './ChangeHandle'
+import * as ChangePasswordModal from './ChangePassword'
import * as WaitlistModal from './Waitlist'
import * as InviteCodesModal from './InviteCodes'
import * as ContentFilteringSettingsModal from './ContentFilteringSettings'
@@ -71,6 +72,8 @@ export const ModalsContainer = observer(function ModalsContainer() {
} else if (activeModal?.name === 'change-handle') {
snapPoints = ChangeHandleModal.snapPoints
element =
+ } else if (activeModal?.name === 'change-password') {
+ element =
} else if (activeModal?.name === 'waitlist') {
snapPoints = WaitlistModal.snapPoints
element =
@@ -96,7 +99,8 @@ export const ModalsContainer = observer(function ModalsContainer() {
}
handleIndicatorStyle={{backgroundColor: pal.text.color}}
handleStyle={[styles.handle, pal.view]}
- onChange={onBottomSheetChange}>
+ onChange={onBottomSheetChange}
+ onClose={onClose}>
{element}
)
diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx
index 25fed69a49..74a79b1f78 100644
--- a/src/view/com/modals/Modal.web.tsx
+++ b/src/view/com/modals/Modal.web.tsx
@@ -15,6 +15,7 @@ import * as DeleteAccountModal from './DeleteAccount'
import * as RepostModal from './Repost'
import * as CropImageModal from './crop-image/CropImage.web'
import * as ChangeHandleModal from './ChangeHandle'
+import * as ChangePasswordModal from './ChangePassword'
import * as WaitlistModal from './Waitlist'
import * as InviteCodesModal from './InviteCodes'
import * as ContentFilteringSettingsModal from './ContentFilteringSettings'
@@ -72,6 +73,8 @@ function Modal({modal}: {modal: ModalIface}) {
element =
} else if (modal.name === 'change-handle') {
element =
+ } else if (modal.name === 'change-password') {
+ element =
} else if (modal.name === 'waitlist') {
element =
} else if (modal.name === 'invite-codes') {
diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx
index 76a3efa602..e13e3ca71b 100644
--- a/src/view/screens/Settings.tsx
+++ b/src/view/screens/Settings.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React, {useCallback, useState} from 'react'
import {
ActivityIndicator,
StyleSheet,
@@ -34,7 +34,6 @@ import {useCustomPalette} from 'lib/hooks/useCustomPalette'
import {AccountData} from 'state/models/session'
import {useAnalytics} from 'lib/analytics'
import {NavigationProp} from 'lib/routes/types'
-import {pluralize} from 'lib/strings/helpers'
type Props = NativeStackScreenProps
export const SettingsScreen = withAuthRequired(
@@ -43,16 +42,8 @@ export const SettingsScreen = withAuthRequired(
const store = useStores()
const navigation = useNavigation()
const {screen, track} = useAnalytics()
- const [isSwitching, setIsSwitching] = React.useState(false)
+ const [isSwitching, setIsSwitching] = 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},
@@ -63,13 +54,13 @@ export const SettingsScreen = withAuthRequired(
})
useFocusEffect(
- React.useCallback(() => {
+ useCallback(() => {
screen('Settings')
store.shell.setMinimalShellMode(false)
}, [screen, store]),
)
- const onPressSwitchAccount = React.useCallback(
+ const onPressSwitchAccount = useCallback(
async (acct: AccountData) => {
track('Settings:SwitchAccountButtonClicked')
setIsSwitching(true)
@@ -89,14 +80,14 @@ export const SettingsScreen = withAuthRequired(
[track, setIsSwitching, navigation, store],
)
- const onPressAddAccount = React.useCallback(() => {
+ const onPressAddAccount = useCallback(() => {
track('Settings:AddAccountButtonClicked')
navigation.navigate('HomeTab')
navigation.dispatch(StackActions.popToTop())
store.session.clear()
}, [track, navigation, store])
- const onPressChangeHandle = React.useCallback(() => {
+ const onPressChangeHandle = useCallback(() => {
track('Settings:ChangeHandleButtonClicked')
store.shell.openModal({
name: 'change-handle',
@@ -119,29 +110,53 @@ export const SettingsScreen = withAuthRequired(
})
}, [track, store, setIsSwitching])
- const onPressInviteCodes = React.useCallback(() => {
- track('Settings:InvitecodesButtonClicked')
- store.shell.openModal({name: 'invite-codes'})
- }, [track, store])
+ const onPressChangePassword = useCallback(() => {
+ track('Settings:ChangePasswordButtonClicked')
+ store.shell.openModal({
+ name: 'change-password',
+ onChanged() {
+ setIsSwitching(true)
+ store.session.reloadFromServer().then(
+ () => {
+ setIsSwitching(false)
+ Toast.show('Your password has been updated')
+ },
+ err => {
+ store.log.error(
+ 'Failed to reload from server after handle update',
+ {err},
+ )
+ setIsSwitching(false)
+ },
+ )
+ },
+ })
+ }, [track, store, setIsSwitching])
- const onPressContentFiltering = React.useCallback(() => {
+ React.useEffect(() => {
+ onPressChangePassword()
+ }, [onPressChangePassword])
+
+ const onPressContentFiltering = useCallback(() => {
track('Settings:ContentfilteringButtonClicked')
store.shell.openModal({name: 'content-filtering-settings'})
}, [track, store])
- const onPressSignout = React.useCallback(() => {
+ const onPressSignout = useCallback(() => {
track('Settings:SignOutButtonClicked')
store.session.logout()
}, [track, store])
- const onPressDeleteAccount = React.useCallback(() => {
+ const onPressDeleteAccount = useCallback(() => {
store.shell.openModal({name: 'delete-account'})
}, [store])
return (
-
+
-
+
@@ -180,6 +195,7 @@ export const SettingsScreen = withAuthRequired(
)}
+
{store.session.switchableAccounts.map(account => (
-
- Invite a friend
-
-
- 0 ? primaryBg : pal.btn,
- ]}>
- 0
- ? primaryText
- : pal.text) as FontAwesomeIconStyle
- }
- />
-
- 0 ? pal.link : pal.text}>
- {store.me.invitesAvailable} invite{' '}
- {pluralize(store.me.invitesAvailable, 'code')} available
-
-
-
-
-
Advanced
@@ -278,7 +263,21 @@ export const SettingsScreen = withAuthRequired(
/>
- Change my handle
+ Change handle
+
+
+
+
+
+
+
+ Change password
@@ -308,7 +307,7 @@ export const SettingsScreen = withAuthRequired(
Developer tools
@@ -316,7 +315,7 @@ export const SettingsScreen = withAuthRequired(
@@ -354,6 +353,10 @@ function AccountDropdownBtn({handle}: {handle: string}) {
}
const styles = StyleSheet.create({
+ contentContainer: {
+ paddingLeft: '18px',
+ paddingRight: '18px',
+ },
dimmed: {
opacity: 0.5,
},
@@ -361,7 +364,6 @@ const styles = StyleSheet.create({
height: 20,
},
heading: {
- paddingHorizontal: 18,
paddingBottom: 6,
},
profile: {
@@ -369,20 +371,11 @@ const styles = StyleSheet.create({
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: {
@@ -398,6 +391,5 @@ const styles = StyleSheet.create({
},
buildInfo: {
paddingVertical: 8,
- paddingHorizontal: 18,
},
})