An ATproto social media client -- with an independent Appview.

[๐Ÿ™…] Integrate deactivate (#4308)

* Update types

(cherry picked from commit 27deac1f367825771ba76fa098ec1b0a62dcf64a)

* Integrate into deactivate dialog

(cherry picked from commit 84f299a447259cc1fbfc7be607e28197779e4ec1)

* Integrate into Deactivated screen

(cherry picked from commit 29193f34822ecdf11e2a407197fa230285dfe846)

* Bump api sdk

(cherry picked from commit 738c622d3e5a23bfbb0d3bdce3a6bdf01e54ca60)

* Update permalink

(cherry picked from commit c10bf5c071d76c3054bc4ce9d313c10b1820f038)

* Bump sdk pkg

* Update types to match backend

* Loosen types for forwards compat

* Hydrate status from persisted data

* Refresh session when re-activating, clear query cache

* Show app password error

* Refactor dialog to clear state when closed

* Add app password error to Deactivated screen

authored by

Eric Bailey and committed by
GitHub
3ece21cb e64b7cf6

+216 -20
+1 -1
package.json
··· 49 49 "open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web" 50 50 }, 51 51 "dependencies": { 52 - "@atproto/api": "^0.12.14", 52 + "@atproto/api": "^0.12.16", 53 53 "@bam.tech/react-native-image-resizer": "^3.0.4", 54 54 "@braintree/sanitize-url": "^6.0.2", 55 55 "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
+60 -3
src/screens/Deactivated.tsx
··· 4 4 import {msg, Trans} from '@lingui/macro' 5 5 import {useLingui} from '@lingui/react' 6 6 import {useFocusEffect} from '@react-navigation/native' 7 + import {useQueryClient} from '@tanstack/react-query' 7 8 8 9 import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' 10 + import {logger} from '#/logger' 9 11 import {isWeb} from '#/platform/detection' 10 - import {type SessionAccount, useSession, useSessionApi} from '#/state/session' 12 + import { 13 + type SessionAccount, 14 + useAgent, 15 + useSession, 16 + useSessionApi, 17 + } from '#/state/session' 11 18 import {useSetMinimalShellMode} from '#/state/shell' 12 19 import {useLoggedOutViewControls} from '#/state/shell/logged-out' 13 20 import {ScrollView} from '#/view/com/util/Views' 14 21 import {Logo} from '#/view/icons/Logo' 15 22 import {atoms as a, useTheme} from '#/alf' 16 23 import {AccountList} from '#/components/AccountList' 17 - import {Button, ButtonText} from '#/components/Button' 24 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 18 25 import {Divider} from '#/components/Divider' 26 + import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 27 + import {Loader} from '#/components/Loader' 19 28 import {Text} from '#/components/Typography' 20 29 21 30 const COL_WIDTH = 400 ··· 30 39 const hasOtherAccounts = accounts.length > 1 31 40 const setMinimalShellMode = useSetMinimalShellMode() 32 41 const {logout} = useSessionApi() 42 + const agent = useAgent() 43 + const [pending, setPending] = React.useState(false) 44 + const [error, setError] = React.useState<string | undefined>() 45 + const queryClient = useQueryClient() 33 46 34 47 useFocusEffect( 35 48 React.useCallback(() => { ··· 62 75 logout('Deactivated') 63 76 }, [logout]) 64 77 78 + const handleActivate = React.useCallback(async () => { 79 + try { 80 + setPending(true) 81 + await agent.com.atproto.server.activateAccount() 82 + await queryClient.resetQueries() 83 + await agent.resumeSession(agent.session!) 84 + } catch (e: any) { 85 + switch (e.message) { 86 + case 'Bad token scope': 87 + setError( 88 + _( 89 + msg`You're logged in with an App Password. Please log in with your main password to continue deactivating your account.`, 90 + ), 91 + ) 92 + break 93 + default: 94 + setError(_(msg`Something went wrong, please try again`)) 95 + break 96 + } 97 + 98 + logger.error(e, { 99 + context: 'Failed to activate account', 100 + }) 101 + } finally { 102 + setPending(false) 103 + } 104 + }, [_, agent, setPending, setError, queryClient]) 105 + 65 106 return ( 66 107 <View style={[a.h_full_vh, a.flex_1, t.atoms.bg]}> 67 108 <ScrollView ··· 104 145 size="medium" 105 146 variant="solid" 106 147 color="primary" 107 - onPress={() => setShowLoggedOut(true)}> 148 + onPress={handleActivate}> 108 149 <ButtonText> 109 150 <Trans>Yes, reactivate my account</Trans> 110 151 </ButtonText> 152 + {pending && <ButtonIcon icon={Loader} position="right" />} 111 153 </Button> 112 154 <Button 113 155 label={_(msg`Cancel reactivation and log out`)} ··· 120 162 </ButtonText> 121 163 </Button> 122 164 </View> 165 + 166 + {error && ( 167 + <View 168 + style={[ 169 + a.flex_row, 170 + a.gap_sm, 171 + a.mt_md, 172 + a.p_md, 173 + a.rounded_sm, 174 + t.atoms.bg_contrast_25, 175 + ]}> 176 + <CircleInfo size="md" fill={t.palette.negative_400} /> 177 + <Text style={[a.flex_1, a.leading_snug]}>{error}</Text> 178 + </View> 179 + )} 123 180 </View> 124 181 125 182 <View style={[a.pb_3xl]}>
+77 -7
src/screens/Settings/components/DeactivateAccountDialog.tsx
··· 3 3 import {msg, Trans} from '@lingui/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 6 - import {atoms as a, useTheme} from '#/alf' 6 + import {logger} from '#/logger' 7 + import {useAgent, useSessionApi} from '#/state/session' 8 + import {atoms as a, useBreakpoints, useTheme} from '#/alf' 9 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 7 10 import {DialogOuterProps} from '#/components/Dialog' 8 11 import {Divider} from '#/components/Divider' 12 + import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 13 + import {Loader} from '#/components/Loader' 9 14 import * as Prompt from '#/components/Prompt' 10 15 import {Text} from '#/components/Typography' 11 16 ··· 14 19 }: { 15 20 control: DialogOuterProps['control'] 16 21 }) { 22 + return ( 23 + <Prompt.Outer control={control}> 24 + <DeactivateAccountDialogInner control={control} /> 25 + </Prompt.Outer> 26 + ) 27 + } 28 + 29 + function DeactivateAccountDialogInner({ 30 + control, 31 + }: { 32 + control: DialogOuterProps['control'] 33 + }) { 17 34 const t = useTheme() 35 + const {gtMobile} = useBreakpoints() 18 36 const {_} = useLingui() 37 + const agent = useAgent() 38 + const {logout} = useSessionApi() 39 + const [pending, setPending] = React.useState(false) 40 + const [error, setError] = React.useState<string | undefined>() 41 + 42 + const handleDeactivate = React.useCallback(async () => { 43 + try { 44 + setPending(true) 45 + await agent.com.atproto.server.deactivateAccount({}) 46 + control.close(() => { 47 + logout('Deactivated') 48 + }) 49 + } catch (e: any) { 50 + switch (e.message) { 51 + case 'Bad token scope': 52 + setError( 53 + _( 54 + msg`You're logged in with an App Password. Please log in with your main password to continue deactivating your account.`, 55 + ), 56 + ) 57 + break 58 + default: 59 + setError(_(msg`Something went wrong, please try again`)) 60 + break 61 + } 62 + 63 + logger.error(e, { 64 + context: 'Failed to deactivate account', 65 + }) 66 + } finally { 67 + setPending(false) 68 + } 69 + }, [agent, control, logout, _, setPending]) 19 70 20 71 return ( 21 - <Prompt.Outer control={control} testID="confirmModal"> 72 + <> 22 73 <Prompt.TitleText>{_(msg`Deactivate account`)}</Prompt.TitleText> 23 74 <Prompt.DescriptionText> 24 75 <Trans> ··· 48 99 <Divider /> 49 100 </View> 50 101 <Prompt.Actions> 51 - <Prompt.Action 52 - cta={_(msg`Yes, deactivate`)} 53 - onPress={() => {}} 102 + <Button 103 + variant="solid" 54 104 color="negative" 55 - /> 105 + size={gtMobile ? 'small' : 'medium'} 106 + label={_(msg`Yes, deactivate`)} 107 + onPress={handleDeactivate}> 108 + <ButtonText>{_(msg`Yes, deactivate`)}</ButtonText> 109 + {pending && <ButtonIcon icon={Loader} position="right" />} 110 + </Button> 56 111 <Prompt.Cancel /> 57 112 </Prompt.Actions> 58 - </Prompt.Outer> 113 + 114 + {error && ( 115 + <View 116 + style={[ 117 + a.flex_row, 118 + a.gap_sm, 119 + a.mt_md, 120 + a.p_md, 121 + a.rounded_sm, 122 + t.atoms.bg_contrast_25, 123 + ]}> 124 + <CircleInfo size="md" fill={t.palette.negative_400} /> 125 + <Text style={[a.flex_1, a.leading_snug]}>{error}</Text> 126 + </View> 127 + )} 128 + </> 59 129 ) 60 130 }
+6 -3
src/state/persisted/schema.ts
··· 18 18 refreshJwt: z.string().optional(), // optional because it can expire 19 19 accessJwt: z.string().optional(), // optional because it can expire 20 20 signupQueued: z.boolean().optional(), 21 - status: z 22 - .enum(['active', 'takendown', 'suspended', 'deactivated']) 23 - .optional(), 21 + active: z.boolean().optional(), // optional for backwards compat 22 + /** 23 + * Known values: takendown, suspended, deactivated 24 + * @see https://github.com/bluesky-social/atproto/blob/5441fbde9ed3b22463e91481ec80cb095643e141/lexicons/com/atproto/server/getSession.json 25 + */ 26 + status: z.string().optional(), 24 27 pdsUrl: z.string().optional(), 25 28 }) 26 29 export type PersistedAccount = z.infer<typeof accountSchema>
+61
src/state/session/__tests__/session-test.ts
··· 28 28 29 29 const agent = new BskyAgent({service: 'https://alice.com'}) 30 30 agent.session = { 31 + active: true, 31 32 did: 'alice-did', 32 33 handle: 'alice.test', 33 34 accessJwt: 'alice-access-jwt-1', ··· 50 51 "accounts": [ 51 52 { 52 53 "accessJwt": "alice-access-jwt-1", 54 + "active": true, 53 55 "did": "alice-did", 54 56 "email": undefined, 55 57 "emailAuthFactor": false, ··· 88 90 "accounts": [ 89 91 { 90 92 "accessJwt": undefined, 93 + "active": true, 91 94 "did": "alice-did", 92 95 "email": undefined, 93 96 "emailAuthFactor": false, ··· 116 119 117 120 const agent1 = new BskyAgent({service: 'https://alice.com'}) 118 121 agent1.session = { 122 + active: true, 119 123 did: 'alice-did', 120 124 handle: 'alice.test', 121 125 accessJwt: 'alice-access-jwt-1', ··· 138 142 "accounts": [ 139 143 { 140 144 "accessJwt": "alice-access-jwt-1", 145 + "active": true, 141 146 "did": "alice-did", 142 147 "email": undefined, 143 148 "emailAuthFactor": false, ··· 162 167 163 168 const agent2 = new BskyAgent({service: 'https://bob.com'}) 164 169 agent2.session = { 170 + active: true, 165 171 did: 'bob-did', 166 172 handle: 'bob.test', 167 173 accessJwt: 'bob-access-jwt-1', ··· 186 192 "accounts": [ 187 193 { 188 194 "accessJwt": "bob-access-jwt-1", 195 + "active": true, 189 196 "did": "bob-did", 190 197 "email": undefined, 191 198 "emailAuthFactor": false, ··· 199 206 }, 200 207 { 201 208 "accessJwt": "alice-access-jwt-1", 209 + "active": true, 202 210 "did": "alice-did", 203 211 "email": undefined, 204 212 "emailAuthFactor": false, ··· 223 231 224 232 const agent3 = new BskyAgent({service: 'https://alice.com'}) 225 233 agent3.session = { 234 + active: true, 226 235 did: 'alice-did', 227 236 handle: 'alice-updated.test', 228 237 accessJwt: 'alice-access-jwt-2', ··· 247 256 "accounts": [ 248 257 { 249 258 "accessJwt": "alice-access-jwt-2", 259 + "active": true, 250 260 "did": "alice-did", 251 261 "email": undefined, 252 262 "emailAuthFactor": false, ··· 260 270 }, 261 271 { 262 272 "accessJwt": "bob-access-jwt-1", 273 + "active": true, 263 274 "did": "bob-did", 264 275 "email": undefined, 265 276 "emailAuthFactor": false, ··· 284 295 285 296 const agent4 = new BskyAgent({service: 'https://jay.com'}) 286 297 agent4.session = { 298 + active: true, 287 299 did: 'jay-did', 288 300 handle: 'jay.test', 289 301 accessJwt: 'jay-access-jwt-1', ··· 306 318 "accounts": [ 307 319 { 308 320 "accessJwt": "jay-access-jwt-1", 321 + "active": true, 309 322 "did": "jay-did", 310 323 "email": undefined, 311 324 "emailAuthFactor": false, ··· 319 332 }, 320 333 { 321 334 "accessJwt": "alice-access-jwt-2", 335 + "active": true, 322 336 "did": "alice-did", 323 337 "email": undefined, 324 338 "emailAuthFactor": false, ··· 332 346 }, 333 347 { 334 348 "accessJwt": "bob-access-jwt-1", 349 + "active": true, 335 350 "did": "bob-did", 336 351 "email": undefined, 337 352 "emailAuthFactor": false, ··· 374 389 "accounts": [ 375 390 { 376 391 "accessJwt": undefined, 392 + "active": true, 377 393 "did": "jay-did", 378 394 "email": undefined, 379 395 "emailAuthFactor": false, ··· 387 403 }, 388 404 { 389 405 "accessJwt": undefined, 406 + "active": true, 390 407 "did": "alice-did", 391 408 "email": undefined, 392 409 "emailAuthFactor": false, ··· 400 417 }, 401 418 { 402 419 "accessJwt": undefined, 420 + "active": true, 403 421 "did": "bob-did", 404 422 "email": undefined, 405 423 "emailAuthFactor": false, ··· 428 446 429 447 const agent1 = new BskyAgent({service: 'https://alice.com'}) 430 448 agent1.session = { 449 + active: true, 431 450 did: 'alice-did', 432 451 handle: 'alice.test', 433 452 accessJwt: 'alice-access-jwt-1', ··· 459 478 "accounts": [ 460 479 { 461 480 "accessJwt": undefined, 481 + "active": true, 462 482 "did": "alice-did", 463 483 "email": undefined, 464 484 "emailAuthFactor": false, ··· 483 503 484 504 const agent2 = new BskyAgent({service: 'https://alice.com'}) 485 505 agent2.session = { 506 + active: true, 486 507 did: 'alice-did', 487 508 handle: 'alice.test', 488 509 accessJwt: 'alice-access-jwt-2', ··· 504 525 "accounts": [ 505 526 { 506 527 "accessJwt": "alice-access-jwt-2", 528 + "active": true, 507 529 "did": "alice-did", 508 530 "email": undefined, 509 531 "emailAuthFactor": false, ··· 532 554 533 555 const agent1 = new BskyAgent({service: 'https://alice.com'}) 534 556 agent1.session = { 557 + active: true, 535 558 did: 'alice-did', 536 559 handle: 'alice.test', 537 560 accessJwt: 'alice-access-jwt-1', ··· 576 599 577 600 const agent1 = new BskyAgent({service: 'https://alice.com'}) 578 601 agent1.session = { 602 + active: true, 579 603 did: 'alice-did', 580 604 handle: 'alice.test', 581 605 accessJwt: 'alice-access-jwt-1', ··· 583 607 } 584 608 const agent2 = new BskyAgent({service: 'https://bob.com'}) 585 609 agent2.session = { 610 + active: true, 586 611 did: 'bob-did', 587 612 handle: 'bob.test', 588 613 accessJwt: 'bob-access-jwt-1', ··· 616 641 "accounts": [ 617 642 { 618 643 "accessJwt": "bob-access-jwt-1", 644 + "active": true, 619 645 "did": "bob-did", 620 646 "email": undefined, 621 647 "emailAuthFactor": false, ··· 653 679 654 680 const agent1 = new BskyAgent({service: 'https://alice.com'}) 655 681 agent1.session = { 682 + active: true, 656 683 did: 'alice-did', 657 684 handle: 'alice.test', 658 685 accessJwt: 'alice-access-jwt-1', ··· 669 696 expect(state.currentAgentState.did).toBe('alice-did') 670 697 671 698 agent1.session = { 699 + active: true, 672 700 did: 'alice-did', 673 701 handle: 'alice-updated.test', 674 702 accessJwt: 'alice-access-jwt-2', ··· 697 725 "accounts": [ 698 726 { 699 727 "accessJwt": "alice-access-jwt-2", 728 + "active": true, 700 729 "did": "alice-did", 701 730 "email": "alice@foo.bar", 702 731 "emailAuthFactor": false, ··· 720 749 `) 721 750 722 751 agent1.session = { 752 + active: true, 723 753 did: 'alice-did', 724 754 handle: 'alice-updated.test', 725 755 accessJwt: 'alice-access-jwt-3', ··· 748 778 "accounts": [ 749 779 { 750 780 "accessJwt": "alice-access-jwt-3", 781 + "active": true, 751 782 "did": "alice-did", 752 783 "email": "alice@foo.baz", 753 784 "emailAuthFactor": true, ··· 771 802 `) 772 803 773 804 agent1.session = { 805 + active: true, 774 806 did: 'alice-did', 775 807 handle: 'alice-updated.test', 776 808 accessJwt: 'alice-access-jwt-4', ··· 799 831 "accounts": [ 800 832 { 801 833 "accessJwt": "alice-access-jwt-4", 834 + "active": true, 802 835 "did": "alice-did", 803 836 "email": "alice@foo.baz", 804 837 "emailAuthFactor": false, ··· 827 860 828 861 const agent1 = new BskyAgent({service: 'https://alice.com'}) 829 862 agent1.session = { 863 + active: true, 830 864 did: 'alice-did', 831 865 handle: 'alice.test', 832 866 accessJwt: 'alice-access-jwt-1', ··· 843 877 expect(state.currentAgentState.did).toBe('alice-did') 844 878 845 879 agent1.session = { 880 + active: true, 846 881 did: 'alice-did', 847 882 handle: 'alice-updated.test', 848 883 accessJwt: 'alice-access-jwt-2', ··· 873 908 expect(lastState === state).toBe(true) 874 909 875 910 agent1.session = { 911 + active: true, 876 912 did: 'alice-did', 877 913 handle: 'alice-updated.test', 878 914 accessJwt: 'alice-access-jwt-3', ··· 896 932 897 933 const agent1 = new BskyAgent({service: 'https://alice.com'}) 898 934 agent1.session = { 935 + active: true, 899 936 did: 'alice-did', 900 937 handle: 'alice.test', 901 938 accessJwt: 'alice-access-jwt-1', ··· 904 941 905 942 const agent2 = new BskyAgent({service: 'https://bob.com'}) 906 943 agent2.session = { 944 + active: true, 907 945 did: 'bob-did', 908 946 handle: 'bob.test', 909 947 accessJwt: 'bob-access-jwt-1', ··· 928 966 expect(state.currentAgentState.did).toBe('bob-did') 929 967 930 968 agent1.session = { 969 + active: true, 931 970 did: 'alice-did', 932 971 handle: 'alice-updated.test', 933 972 accessJwt: 'alice-access-jwt-2', ··· 956 995 "accounts": [ 957 996 { 958 997 "accessJwt": "bob-access-jwt-1", 998 + "active": true, 959 999 "did": "bob-did", 960 1000 "email": undefined, 961 1001 "emailAuthFactor": false, ··· 969 1009 }, 970 1010 { 971 1011 "accessJwt": "alice-access-jwt-2", 1012 + "active": true, 972 1013 "did": "alice-did", 973 1014 "email": "alice@foo.bar", 974 1015 "emailAuthFactor": false, ··· 992 1033 `) 993 1034 994 1035 agent2.session = { 1036 + active: true, 995 1037 did: 'bob-did', 996 1038 handle: 'bob-updated.test', 997 1039 accessJwt: 'bob-access-jwt-2', ··· 1018 1060 "accounts": [ 1019 1061 { 1020 1062 "accessJwt": "bob-access-jwt-2", 1063 + "active": true, 1021 1064 "did": "bob-did", 1022 1065 "email": undefined, 1023 1066 "emailAuthFactor": false, ··· 1031 1074 }, 1032 1075 { 1033 1076 "accessJwt": "alice-access-jwt-2", 1077 + "active": true, 1034 1078 "did": "alice-did", 1035 1079 "email": "alice@foo.bar", 1036 1080 "emailAuthFactor": false, ··· 1083 1127 1084 1128 const agent1 = new BskyAgent({service: 'https://alice.com'}) 1085 1129 agent1.session = { 1130 + active: true, 1086 1131 did: 'alice-did', 1087 1132 handle: 'alice.test', 1088 1133 accessJwt: 'alice-access-jwt-1', ··· 1091 1136 1092 1137 const agent2 = new BskyAgent({service: 'https://bob.com'}) 1093 1138 agent2.session = { 1139 + active: true, 1094 1140 did: 'bob-did', 1095 1141 handle: 'bob.test', 1096 1142 accessJwt: 'bob-access-jwt-1', ··· 1117 1163 expect(state.currentAgentState.did).toBe('bob-did') 1118 1164 1119 1165 agent1.session = { 1166 + active: true, 1120 1167 did: 'alice-did', 1121 1168 handle: 'alice.test', 1122 1169 accessJwt: 'alice-access-jwt-2', ··· 1142 1189 1143 1190 const agent1 = new BskyAgent({service: 'https://alice.com'}) 1144 1191 agent1.session = { 1192 + active: true, 1145 1193 did: 'alice-did', 1146 1194 handle: 'alice.test', 1147 1195 accessJwt: 'alice-access-jwt-1', ··· 1179 1227 "accounts": [ 1180 1228 { 1181 1229 "accessJwt": "alice-access-jwt-1", 1230 + "active": true, 1182 1231 "did": "alice-did", 1183 1232 "email": undefined, 1184 1233 "emailAuthFactor": false, ··· 1207 1256 1208 1257 const agent1 = new BskyAgent({service: 'https://alice.com'}) 1209 1258 agent1.session = { 1259 + active: true, 1210 1260 did: 'alice-did', 1211 1261 handle: 'alice.test', 1212 1262 accessJwt: 'alice-access-jwt-1', ··· 1242 1292 "accounts": [ 1243 1293 { 1244 1294 "accessJwt": undefined, 1295 + "active": true, 1245 1296 "did": "alice-did", 1246 1297 "email": undefined, 1247 1298 "emailAuthFactor": false, ··· 1270 1321 1271 1322 const agent1 = new BskyAgent({service: 'https://alice.com'}) 1272 1323 agent1.session = { 1324 + active: true, 1273 1325 did: 'alice-did', 1274 1326 handle: 'alice.test', 1275 1327 accessJwt: 'alice-access-jwt-1', ··· 1305 1357 "accounts": [ 1306 1358 { 1307 1359 "accessJwt": undefined, 1360 + "active": true, 1308 1361 "did": "alice-did", 1309 1362 "email": undefined, 1310 1363 "emailAuthFactor": false, ··· 1333 1386 1334 1387 const agent1 = new BskyAgent({service: 'https://alice.com'}) 1335 1388 agent1.session = { 1389 + active: true, 1336 1390 did: 'alice-did', 1337 1391 handle: 'alice.test', 1338 1392 accessJwt: 'alice-access-jwt-1', ··· 1340 1394 } 1341 1395 const agent2 = new BskyAgent({service: 'https://bob.com'}) 1342 1396 agent2.session = { 1397 + active: true, 1343 1398 did: 'bob-did', 1344 1399 handle: 'bob.test', 1345 1400 accessJwt: 'bob-access-jwt-1', ··· 1362 1417 1363 1418 const anotherTabAgent1 = new BskyAgent({service: 'https://jay.com'}) 1364 1419 anotherTabAgent1.session = { 1420 + active: true, 1365 1421 did: 'jay-did', 1366 1422 handle: 'jay.test', 1367 1423 accessJwt: 'jay-access-jwt-1', ··· 1369 1425 } 1370 1426 const anotherTabAgent2 = new BskyAgent({service: 'https://alice.com'}) 1371 1427 anotherTabAgent2.session = { 1428 + active: true, 1372 1429 did: 'bob-did', 1373 1430 handle: 'bob.test', 1374 1431 accessJwt: 'bob-access-jwt-2', ··· 1397 1454 "accounts": [ 1398 1455 { 1399 1456 "accessJwt": "jay-access-jwt-1", 1457 + "active": true, 1400 1458 "did": "jay-did", 1401 1459 "email": undefined, 1402 1460 "emailAuthFactor": false, ··· 1410 1468 }, 1411 1469 { 1412 1470 "accessJwt": "bob-access-jwt-2", 1471 + "active": true, 1413 1472 "did": "bob-did", 1414 1473 "email": undefined, 1415 1474 "emailAuthFactor": false, ··· 1434 1493 1435 1494 const anotherTabAgent3 = new BskyAgent({service: 'https://clarence.com'}) 1436 1495 anotherTabAgent3.session = { 1496 + active: true, 1437 1497 did: 'clarence-did', 1438 1498 handle: 'clarence.test', 1439 1499 accessJwt: 'clarence-access-jwt-2', ··· 1457 1517 "accounts": [ 1458 1518 { 1459 1519 "accessJwt": "clarence-access-jwt-2", 1520 + "active": true, 1460 1521 "did": "clarence-did", 1461 1522 "email": undefined, 1462 1523 "emailAuthFactor": false,
+7 -2
src/state/session/agent.ts
··· 46 46 emailConfirmed: storedAccount.emailConfirmed, 47 47 handle: storedAccount.handle, 48 48 refreshJwt: storedAccount.refreshJwt ?? '', 49 + /** 50 + * @see https://github.com/bluesky-social/atproto/blob/c5d36d5ba2a2c2a5c4f366a5621c06a5608e361e/packages/api/src/agent.ts#L188 51 + */ 52 + active: storedAccount.active ?? true, 53 + status: storedAccount.status, 49 54 } 50 55 if (isSessionExpired(storedAccount)) { 51 56 await networkRetry(1, () => agent.resumeSession(prevSession)) ··· 235 240 refreshJwt: agent.session.refreshJwt, 236 241 accessJwt: agent.session.accessJwt, 237 242 signupQueued: isSignupQueued(agent.session.accessJwt), 238 - // @ts-expect-error TODO remove when backend is ready 239 - status: agent.session.status, 243 + active: agent.session.active, 244 + status: agent.session.status as SessionAccount['status'], 240 245 pdsUrl: agent.pdsUrl?.toString(), 241 246 } 242 247 }
+4 -4
yarn.lock
··· 34 34 jsonpointer "^5.0.0" 35 35 leven "^3.1.0" 36 36 37 - "@atproto/api@^0.12.14": 38 - version "0.12.14" 39 - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.14.tgz#81252fd166ec8fe950056531e690d563437720fa" 40 - integrity sha512-ZPh/afoRjFEQDQgMZW2FQiG5CDUifY7SxBqI0zVJUwed8Zi6fqYzGYM8fcDvD8yJfflRCqRxUE72g5fKiA1zAQ== 37 + "@atproto/api@^0.12.16": 38 + version "0.12.16" 39 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.12.16.tgz#f5b5e06d75d379dafe79521d727ed8ad5516d3fc" 40 + integrity sha512-v3lA/m17nkawDXiqgwXyaUSzJPeXJBMH8QKOoYxcDqN+8yG9LFlGe2ecGarXcbGQjYT0GJTAAW3Y/AaCOEwuLg== 41 41 dependencies: 42 42 "@atproto/common-web" "^0.3.0" 43 43 "@atproto/lexicon" "^0.4.0"