From 0e3b2633b9876ac3aea39897cdb8831da8c3c1bd Mon Sep 17 00:00:00 2001 From: Alexander K Date: Sun, 17 Mar 2024 21:34:52 +0100 Subject: Add 'Liberation Sans' font for FireFox on Linux; lightbox buttons adjustments --- .../ImageViewing/components/ImageDefaultHeader.tsx | 49 +++++++++++++------- src/view/com/lightbox/Lightbox.web.tsx | 54 ++++++++++++++++++---- src/view/com/util/forms/NativeDropdown.web.tsx | 2 +- src/view/com/util/text/Text.tsx | 15 +++++- 4 files changed, 90 insertions(+), 30 deletions(-) (limited to 'src/view') diff --git a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx index 3401adaff..b23dfedc8 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx @@ -7,8 +7,15 @@ */ import React from 'react' import {createHitslop} from 'lib/constants' -import {SafeAreaView, Text, TouchableOpacity, StyleSheet} from 'react-native' +import { + SafeAreaView, + TouchableOpacity, + StyleSheet, + ViewStyle, +} from 'react-native' import {t} from '@lingui/macro' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {usePalette} from '#/lib/hooks/usePalette' type Props = { onRequestClose: () => void @@ -16,20 +23,24 @@ type Props = { const HIT_SLOP = createHitslop(16) -const ImageDefaultHeader = ({onRequestClose}: Props) => ( - - - - - -) +const ImageDefaultHeader = ({onRequestClose}: Props) => { + const pal = usePalette('default') + + return ( + + + + + + ) +} const styles = StyleSheet.create({ root: { @@ -37,8 +48,8 @@ const styles = StyleSheet.create({ pointerEvents: 'box-none', }, closeButton: { - marginRight: 8, - marginTop: 8, + marginRight: 10, + marginTop: 10, width: 44, height: 44, alignItems: 'center', @@ -53,6 +64,10 @@ const styles = StyleSheet.create({ color: '#FFF', includeFontPadding: false, }, + blurredBackground: { + backdropFilter: 'blur(10px)', + WebkitBackdropFilter: 'blur(10px)', + } as ViewStyle, }) export default ImageDefaultHeader diff --git a/src/view/com/lightbox/Lightbox.web.tsx b/src/view/com/lightbox/Lightbox.web.tsx index fb97c30a4..942c9a686 100644 --- a/src/view/com/lightbox/Lightbox.web.tsx +++ b/src/view/com/lightbox/Lightbox.web.tsx @@ -7,6 +7,7 @@ import { StyleSheet, View, Pressable, + ViewStyle, } from 'react-native' import { FontAwesomeIcon, @@ -24,6 +25,7 @@ import { ProfileImageLightbox, } from '#/state/lightbox' import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock' +import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' interface Img { uri: string @@ -111,6 +113,14 @@ function LightboxInner({ return () => window.removeEventListener('keydown', onKeyDown) }, [onKeyDown]) + const {isTabletOrDesktop} = useWebMediaQueries() + const btnStyle = React.useMemo(() => { + return isTabletOrDesktop ? styles.btnTablet : styles.btnMobile + }, [isTabletOrDesktop]) + const iconSize = React.useMemo(() => { + return isTabletOrDesktop ? 32 : 24 + }, [isTabletOrDesktop]) + return ( )} {canGoRight && ( )} @@ -213,20 +233,30 @@ const styles = StyleSheet.create({ }, btn: { position: 'absolute', - backgroundColor: '#000', - width: 50, - height: 50, + backgroundColor: '#00000077', justifyContent: 'center', alignItems: 'center', + }, + btnTablet: { + width: 50, + height: 50, borderRadius: 25, + left: 30, + right: 30, + }, + btnMobile: { + width: 44, + height: 44, + borderRadius: 22, + left: 20, + right: 20, }, leftBtn: { - left: 30, + right: 'auto', top: '50%', }, rightBtn: { - position: 'absolute', - right: 30, + left: 'auto', top: '50%', }, footer: { @@ -234,4 +264,8 @@ const styles = StyleSheet.create({ paddingVertical: 24, backgroundColor: colors.black, }, + blurredBackground: { + backdropFilter: 'blur(10px)', + WebkitBackdropFilter: 'blur(10px)', + } as ViewStyle, }) diff --git a/src/view/com/util/forms/NativeDropdown.web.tsx b/src/view/com/util/forms/NativeDropdown.web.tsx index 6abeb16cc..94591d393 100644 --- a/src/view/com/util/forms/NativeDropdown.web.tsx +++ b/src/view/com/util/forms/NativeDropdown.web.tsx @@ -237,7 +237,7 @@ const styles = StyleSheet.create({ paddingRight: 12, borderRadius: 8, fontFamily: - '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif', outline: 0, border: 0, }, diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx index ccb51bfca..37d665581 100644 --- a/src/view/com/util/text/Text.tsx +++ b/src/view/com/util/text/Text.tsx @@ -2,7 +2,7 @@ import React from 'react' import {Text as RNText, TextProps} from 'react-native' import {s, lh} from 'lib/styles' import {useTheme, TypographyVariant} from 'lib/ThemeContext' -import {isIOS} from 'platform/detection' +import {isIOS, isWeb} from 'platform/detection' import {UITextView} from 'react-native-ui-text-view' export type CustomTextProps = TextProps & { @@ -13,6 +13,11 @@ export type CustomTextProps = TextProps & { selectable?: boolean } +const fontFamilyStyle = { + fontFamily: + '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Liberation Sans", Helvetica, Arial, sans-serif', +} + export function Text({ type = 'md', children, @@ -39,7 +44,13 @@ export function Text({ return ( Date: Tue, 19 Mar 2024 20:11:48 +0000 Subject: Fix typos and clarify two `accessibilityHint`s (#2923) * Clarify accessibilityHint in Settings * Fix typo in ModerationMutedAccounts.tsx * Fix typo in ModerationBlockedAccounts.tsx * fix lint error in ModerationMutedAccounts.tsx * try again * fix lint error in index.tsx * try again * Update index.tsx * Update index.tsx --------- Co-authored-by: Paul Frazee --- src/view/screens/ModerationBlockedAccounts.tsx | 2 +- src/view/screens/ModerationMutedAccounts.tsx | 4 ++-- src/view/screens/Settings/index.tsx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/view') diff --git a/src/view/screens/ModerationBlockedAccounts.tsx b/src/view/screens/ModerationBlockedAccounts.tsx index 09d77987f..eb3b27048 100644 --- a/src/view/screens/ModerationBlockedAccounts.tsx +++ b/src/view/screens/ModerationBlockedAccounts.tsx @@ -131,7 +131,7 @@ export function ModerationBlockedAccounts({}: Props) { You have not blocked any accounts yet. To block an account, go - to their profile and selected "Block account" from the menu on + to their profile and select "Block account" from the menu on their account. diff --git a/src/view/screens/ModerationMutedAccounts.tsx b/src/view/screens/ModerationMutedAccounts.tsx index 1aff19dd3..911ace778 100644 --- a/src/view/screens/ModerationMutedAccounts.tsx +++ b/src/view/screens/ModerationMutedAccounts.tsx @@ -130,8 +130,8 @@ export function ModerationMutedAccounts({}: Props) { You have not muted any accounts yet. To mute an account, go to - their profile and selected "Mute account" from the menu on - their account. + their profile and select "Mute account" from the menu on their + account. diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 465007777..7e808f910 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -711,7 +711,7 @@ export function SettingsScreen({}: Props) { accessibilityRole="button" accessibilityLabel={_(msg`Change handle`)} accessibilityHint={_( - msg`Opens modal for choosing or creating a new Bluesky username`, + msg`Opens modal for choosing a new Bluesky handle`, )}> Date: Tue, 19 Mar 2024 13:26:25 -0700 Subject: Prevent linking to post from an embed in composer (#3275) --- src/view/com/composer/Composer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/view') diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index ab7551b60..ddb01a8fa 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -447,7 +447,7 @@ export const ComposePost = observer(function ComposePost({ /> )} {quote ? ( - + ) : undefined} -- cgit 1.4.1 From e433a1c968af548327c15bcc2e7fcb5a80760d36 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 19 Mar 2024 14:06:04 -0700 Subject: Remove dead style --- .../com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/view') diff --git a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx index 3a040b5d0..88476c8e1 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx @@ -57,13 +57,6 @@ const styles = StyleSheet.create({ borderRadius: 22, backgroundColor: '#00000077', }, - closeText: { - lineHeight: 22, - fontSize: 19, - textAlign: 'center', - color: '#FFF', - includeFontPadding: false, - }, blurredBackground: { backdropFilter: 'blur(10px)', WebkitBackdropFilter: 'blur(10px)', -- cgit 1.4.1 From bdf77f8548dc62571ecb6c70b320b866360fbec1 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 19 Mar 2024 14:43:00 -0700 Subject: Fix detection of !no-unauthenticated (#3279) --- src/components/moderation/ScreenHider.tsx | 3 ++- src/view/com/post-thread/PostThread.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/view') diff --git a/src/components/moderation/ScreenHider.tsx b/src/components/moderation/ScreenHider.tsx index 71ca85a92..4e3a9680f 100644 --- a/src/components/moderation/ScreenHider.tsx +++ b/src/components/moderation/ScreenHider.tsx @@ -56,7 +56,8 @@ export function ScreenHider({ const isNoPwi = !!modui.blurs.find( cause => - cause.type === 'label' && cause.labelDef.id === '!no-unauthenticated', + cause.type === 'label' && + cause.labelDef.identifier === '!no-unauthenticated', ) return ( - cause.type === 'label' && cause.labelDef.id === '!no-unauthenticated', + cause.type === 'label' && + cause.labelDef.identifier === '!no-unauthenticated', ) }, [rootPost, moderationOpts]) -- cgit 1.4.1 From a90566d86439a96509967f522069a4be729dfc1b Mon Sep 17 00:00:00 2001 From: Hailey Date: Tue, 19 Mar 2024 17:47:10 -0700 Subject: use different labels for `porn` and `sexual` (#3282) * use different labels for `porn` and `sexual` * newline --- src/lib/moderation/useModerationCauseDescription.ts | 6 +++++- src/view/com/posts/FeedErrorMessage.tsx | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'src/view') diff --git a/src/lib/moderation/useModerationCauseDescription.ts b/src/lib/moderation/useModerationCauseDescription.ts index 46771e958..57b50d777 100644 --- a/src/lib/moderation/useModerationCauseDescription.ts +++ b/src/lib/moderation/useModerationCauseDescription.ts @@ -118,11 +118,15 @@ export function useModerationCauseDescription( (labeler?.creator.handle ? '@' + labeler?.creator.handle : undefined) if (!source) { if (cause.label.src === BSKY_LABELER_DID) { - source = 'Bluesky Moderation' + source = 'Bluesky Moderation Service' } else { source = cause.label.src } } + if (def.identifier === 'porn' || def.identifier === 'sexual') { + strings.name = 'Adult Content' + } + return { icon: def.identifier === '!no-unauthenticated' diff --git a/src/view/com/posts/FeedErrorMessage.tsx b/src/view/com/posts/FeedErrorMessage.tsx index c52090f97..d4ca38d07 100644 --- a/src/view/com/posts/FeedErrorMessage.tsx +++ b/src/view/com/posts/FeedErrorMessage.tsx @@ -46,7 +46,7 @@ export function FeedErrorMessage({ if ( typeof knownError !== 'undefined' && knownError !== KnownError.Unknown && - feedDesc.startsWith('feedgen') + (feedDesc.startsWith('feedgen') || knownError === KnownError.FeedNSFPublic) ) { return ( Date: Tue, 19 Mar 2024 17:54:40 -0700 Subject: Various e2e test fixes (#3284) * Just use the first picture every time * Add missing testIDs * Various test fixes * Use simplified link fetcher for e2e * Disable tests for now-n * Update test-env creation --- __e2e__/tests/curate-lists.test.ts | 4 +- __e2e__/tests/home-screen.test.ts | 2 +- .../invites-and-text-verification.test.skip.ts | 52 ++++++++++++++ .../tests/invites-and-text-verification.test.ts | 52 -------------- __e2e__/tests/profile-screen.test.ts | 8 +-- __e2e__/tests/text-verification.test.skip.ts | 83 ++++++++++++++++++++++ __e2e__/tests/text-verification.test.ts | 81 --------------------- jest/test-pds.ts | 36 +++++++++- src/lib/media/picker.e2e.tsx | 3 +- src/view/com/composer/useExternalLinkFetch.e2e.ts | 45 ++++++++++++ src/view/com/util/UserAvatar.tsx | 5 +- src/view/com/util/UserBanner.tsx | 5 +- 12 files changed, 229 insertions(+), 147 deletions(-) create mode 100644 __e2e__/tests/invites-and-text-verification.test.skip.ts delete mode 100644 __e2e__/tests/invites-and-text-verification.test.ts create mode 100644 __e2e__/tests/text-verification.test.skip.ts delete mode 100644 __e2e__/tests/text-verification.test.ts create mode 100644 src/view/com/composer/useExternalLinkFetch.e2e.ts (limited to 'src/view') diff --git a/__e2e__/tests/curate-lists.test.ts b/__e2e__/tests/curate-lists.test.ts index f188b154b..2c44fa5df 100644 --- a/__e2e__/tests/curate-lists.test.ts +++ b/__e2e__/tests/curate-lists.test.ts @@ -64,7 +64,7 @@ describe('Curate lists', () => { await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('changeAvatarBtn')).tap() - await element(by.text('Library')).tap() + await element(by.text('Upload from Library')).tap() await sleep(3e3) await element(by.id('saveBtn')).tap() await expect(element(by.id('createOrEditListModal'))).not.toBeVisible() @@ -81,7 +81,7 @@ describe('Curate lists', () => { await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('changeAvatarBtn')).tap() - await element(by.text('Remove')).tap() + await element(by.text('Remove Avatar')).tap() await element(by.id('saveBtn')).tap() await expect(element(by.id('createOrEditListModal'))).not.toBeVisible() await expect(element(by.id('userAvatarFallback'))).toExist() diff --git a/__e2e__/tests/home-screen.test.ts b/__e2e__/tests/home-screen.test.ts index ce7f1643b..a83a34edc 100644 --- a/__e2e__/tests/home-screen.test.ts +++ b/__e2e__/tests/home-screen.test.ts @@ -17,7 +17,7 @@ describe('Home screen', () => { it('Can go to feeds page using feeds button in tab bar', async () => { await element(by.id('homeScreenFeedTabs-Feeds ✨')).tap() - await expect(element(by.text('Discover new feeds'))).toBeVisible() + await expect(element(by.text('Discover New Feeds'))).toBeVisible() }) it('Feeds button disappears after pinning a feed', async () => { diff --git a/__e2e__/tests/invites-and-text-verification.test.skip.ts b/__e2e__/tests/invites-and-text-verification.test.skip.ts new file mode 100644 index 000000000..863b31107 --- /dev/null +++ b/__e2e__/tests/invites-and-text-verification.test.skip.ts @@ -0,0 +1,52 @@ +/* eslint-env detox/detox */ + +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' +import {openApp, loginAsAlice, createServer} from '../util' + +describe('invite-codes', () => { + let service: string + let inviteCode = '' + beforeAll(async () => { + service = await createServer('?users&invite&phone') + await openApp({permissions: {notifications: 'YES'}}) + }) + + it('I can fetch invite codes', async () => { + await loginAsAlice() + await element(by.id('e2eOpenInviteCodesModal')).tap() + await expect(element(by.id('inviteCodesModal'))).toBeVisible() + const attrs = await element(by.id('inviteCode-0-code')).getAttributes() + inviteCode = attrs.text + await element(by.id('closeBtn')).tap() + await element(by.id('e2eSignOut')).tap() + }) + + it('I can create a new account with the invite code', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() + await element(by.id('createAccountButton')).tap() + await device.takeScreenshot('1- opened create account screen') + await element(by.id('selectServiceButton')).tap() + await device.takeScreenshot('2- selected other server') + await element(by.id('customSelectBtn')).tap() + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('doneBtn')).tap() + await device.takeScreenshot('3- input test server URL') + await element(by.id('inviteCodeInput')).typeText(inviteCode) + await element(by.id('emailInput')).typeText('example@test.com') + await element(by.id('passwordInput')).typeText('hunter2') + await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + await element(by.id('phoneInput')).typeText('2345551234') + await element(by.id('requestCodeBtn')).tap() + await device.takeScreenshot('5- requested code') + await element(by.id('codeInput')).typeText('000000') + await device.takeScreenshot('6- entered code') + await element(by.id('nextBtn')).tap() + await element(by.id('handleInput')).typeText('e2e-test') + await device.takeScreenshot('7- entered handle') + await element(by.id('nextBtn')).tap() + await expect(element(by.id('onboardingInterests'))).toBeVisible() + }) +}) diff --git a/__e2e__/tests/invites-and-text-verification.test.ts b/__e2e__/tests/invites-and-text-verification.test.ts deleted file mode 100644 index 863b31107..000000000 --- a/__e2e__/tests/invites-and-text-verification.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* eslint-env detox/detox */ - -import {describe, beforeAll, it} from '@jest/globals' -import {expect} from 'detox' -import {openApp, loginAsAlice, createServer} from '../util' - -describe('invite-codes', () => { - let service: string - let inviteCode = '' - beforeAll(async () => { - service = await createServer('?users&invite&phone') - await openApp({permissions: {notifications: 'YES'}}) - }) - - it('I can fetch invite codes', async () => { - await loginAsAlice() - await element(by.id('e2eOpenInviteCodesModal')).tap() - await expect(element(by.id('inviteCodesModal'))).toBeVisible() - const attrs = await element(by.id('inviteCode-0-code')).getAttributes() - inviteCode = attrs.text - await element(by.id('closeBtn')).tap() - await element(by.id('e2eSignOut')).tap() - }) - - it('I can create a new account with the invite code', async () => { - await element(by.id('e2eOpenLoggedOutView')).tap() - await element(by.id('createAccountButton')).tap() - await device.takeScreenshot('1- opened create account screen') - await element(by.id('selectServiceButton')).tap() - await device.takeScreenshot('2- selected other server') - await element(by.id('customSelectBtn')).tap() - await element(by.id('customServerTextInput')).typeText(service) - await element(by.id('customServerTextInput')).tapReturnKey() - await element(by.id('doneBtn')).tap() - await device.takeScreenshot('3- input test server URL') - await element(by.id('inviteCodeInput')).typeText(inviteCode) - await element(by.id('emailInput')).typeText('example@test.com') - await element(by.id('passwordInput')).typeText('hunter2') - await device.takeScreenshot('4- entered account details') - await element(by.id('nextBtn')).tap() - await element(by.id('phoneInput')).typeText('2345551234') - await element(by.id('requestCodeBtn')).tap() - await device.takeScreenshot('5- requested code') - await element(by.id('codeInput')).typeText('000000') - await device.takeScreenshot('6- entered code') - await element(by.id('nextBtn')).tap() - await element(by.id('handleInput')).typeText('e2e-test') - await device.takeScreenshot('7- entered handle') - await element(by.id('nextBtn')).tap() - await expect(element(by.id('onboardingInterests'))).toBeVisible() - }) -}) diff --git a/__e2e__/tests/profile-screen.test.ts b/__e2e__/tests/profile-screen.test.ts index 7ff43642f..13d0fa8ef 100644 --- a/__e2e__/tests/profile-screen.test.ts +++ b/__e2e__/tests/profile-screen.test.ts @@ -70,10 +70,10 @@ describe('Profile screen', () => { await element(by.id('profileHeaderEditProfileButton')).tap() await expect(element(by.id('editProfileModal'))).toBeVisible() await element(by.id('changeBannerBtn')).tap() - await element(by.text('Library')).tap() + await element(by.text('Upload from Library')).tap() await sleep(3e3) await element(by.id('changeAvatarBtn')).tap() - await element(by.text('Library')).tap() + await element(by.text('Upload from Library')).tap() await sleep(3e3) await element(by.id('editProfileSaveBtn')).tap() await expect(element(by.id('editProfileModal'))).not.toBeVisible() @@ -87,9 +87,9 @@ describe('Profile screen', () => { await element(by.id('profileHeaderEditProfileButton')).tap() await expect(element(by.id('editProfileModal'))).toBeVisible() await element(by.id('changeBannerBtn')).tap() - await element(by.text('Remove')).tap() + await element(by.text('Remove Banner')).tap() await element(by.id('changeAvatarBtn')).tap() - await element(by.text('Remove')).tap() + await element(by.text('Remove Avatar')).tap() await element(by.id('editProfileSaveBtn')).tap() await expect(element(by.id('editProfileModal'))).not.toBeVisible() await expect(element(by.id('userBannerFallback'))).toExist() diff --git a/__e2e__/tests/text-verification.test.skip.ts b/__e2e__/tests/text-verification.test.skip.ts new file mode 100644 index 000000000..bd19e66b2 --- /dev/null +++ b/__e2e__/tests/text-verification.test.skip.ts @@ -0,0 +1,83 @@ +/* eslint-env detox/detox */ + +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' +import {openApp, createServer} from '../util' + +describe('Create account', () => { + let service: string + beforeAll(async () => { + service = await createServer('?phone') + await openApp({permissions: {notifications: 'YES'}}) + }) + + it('I can create a new account with text verification', async () => { + console.log('SERVICE IS', service) + await element(by.id('e2eOpenLoggedOutView')).tap() + + await element(by.id('createAccountButton')).tap() + await device.takeScreenshot('1- opened create account screen') + await element(by.id('selectServiceButton')).tap() + await device.takeScreenshot('2- selected other server') + await element(by.id('customSelectBtn')).tap() + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('doneBtn')).tap() + await device.takeScreenshot('3- input test server URL') + await element(by.id('emailInput')).typeText('text-verification@test.com') + await element(by.id('passwordInput')).typeText('hunter2') + await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + + await element(by.id('handleInput')).typeText('text-verification-test') + await device.takeScreenshot('5- entered handle') + await element(by.id('nextBtn')).tap() + + await element(by.id('phoneInput')).typeText('8042221111') + await element(by.id('requestCodeBtn')).tap() + await device.takeScreenshot('6- requested code') + + await element(by.id('codeInput')).typeText('000000') + await device.takeScreenshot('7- entered code') + await element(by.id('nextBtn')).tap() + + await element(by.id('nextBtn')).tap() + + await expect(element(by.id('onboardingInterests'))).toBeVisible() + }) + + it('failed text verification correctly goes back to the code input screen', async () => { + await element(by.id('e2eSignOut')).tap() + await element(by.id('e2eOpenLoggedOutView')).tap() + + await element(by.id('createAccountButton')).tap() + await device.takeScreenshot('1- opened create account screen') + await element(by.id('selectServiceButton')).tap() + await device.takeScreenshot('2- selected other server') + await element(by.id('customSelectBtn')).tap() + await element(by.id('customServerTextInput')).typeText(service) + await element(by.id('customServerTextInput')).tapReturnKey() + await element(by.id('doneBtn')).tap() + await device.takeScreenshot('3- input test server URL') + await element(by.id('emailInput')).typeText('text-verification2@test.com') + await element(by.id('passwordInput')).typeText('hunter2') + await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + + await element(by.id('phoneInput')).typeText('8042221111') + await element(by.id('requestCodeBtn')).tap() + await device.takeScreenshot('5- requested code') + + await element(by.id('codeInput')).typeText('111111') + await device.takeScreenshot('6- entered code') + await element(by.id('nextBtn')).tap() + + await element(by.id('handleInput')).typeText('text-verification-test2') + await device.takeScreenshot('7- entered handle') + + await element(by.id('nextBtn')).tap() + + await expect(element(by.id('codeInput'))).toBeVisible() + await device.takeScreenshot('8- got error') + }) +}) diff --git a/__e2e__/tests/text-verification.test.ts b/__e2e__/tests/text-verification.test.ts deleted file mode 100644 index 79b14aeca..000000000 --- a/__e2e__/tests/text-verification.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-env detox/detox */ - -import {describe, beforeAll, it} from '@jest/globals' -import {expect} from 'detox' -import {openApp, createServer} from '../util' - -describe('Create account', () => { - let service: string - beforeAll(async () => { - service = await createServer('?phone') - await openApp({permissions: {notifications: 'YES'}}) - }) - - it('I can create a new account with text verification', async () => { - await element(by.id('e2eOpenLoggedOutView')).tap() - - await element(by.id('createAccountButton')).tap() - await device.takeScreenshot('1- opened create account screen') - await element(by.id('selectServiceButton')).tap() - await device.takeScreenshot('2- selected other server') - await element(by.id('customSelectBtn')).tap() - await element(by.id('customServerTextInput')).typeText(service) - await element(by.id('customServerTextInput')).tapReturnKey() - await element(by.id('doneBtn')).tap() - await device.takeScreenshot('3- input test server URL') - await element(by.id('emailInput')).typeText('text-verification@test.com') - await element(by.id('passwordInput')).typeText('hunter2') - await device.takeScreenshot('4- entered account details') - await element(by.id('nextBtn')).tap() - - await element(by.id('phoneInput')).typeText('8042221111') - await element(by.id('requestCodeBtn')).tap() - await device.takeScreenshot('5- requested code') - - await element(by.id('codeInput')).typeText('000000') - await device.takeScreenshot('6- entered code') - await element(by.id('nextBtn')).tap() - - await element(by.id('handleInput')).typeText('text-verification-test') - await device.takeScreenshot('7- entered handle') - - await element(by.id('nextBtn')).tap() - - await expect(element(by.id('onboardingInterests'))).toBeVisible() - }) - - it('failed text verification correctly goes back to the code input screen', async () => { - await element(by.id('e2eSignOut')).tap() - await element(by.id('e2eOpenLoggedOutView')).tap() - - await element(by.id('createAccountButton')).tap() - await device.takeScreenshot('1- opened create account screen') - await element(by.id('selectServiceButton')).tap() - await device.takeScreenshot('2- selected other server') - await element(by.id('customSelectBtn')).tap() - await element(by.id('customServerTextInput')).typeText(service) - await element(by.id('customServerTextInput')).tapReturnKey() - await element(by.id('doneBtn')).tap() - await device.takeScreenshot('3- input test server URL') - await element(by.id('emailInput')).typeText('text-verification2@test.com') - await element(by.id('passwordInput')).typeText('hunter2') - await device.takeScreenshot('4- entered account details') - await element(by.id('nextBtn')).tap() - - await element(by.id('phoneInput')).typeText('8042221111') - await element(by.id('requestCodeBtn')).tap() - await device.takeScreenshot('5- requested code') - - await element(by.id('codeInput')).typeText('111111') - await device.takeScreenshot('6- entered code') - await element(by.id('nextBtn')).tap() - - await element(by.id('handleInput')).typeText('text-verification-test2') - await device.takeScreenshot('7- entered handle') - - await element(by.id('nextBtn')).tap() - - await expect(element(by.id('codeInput'))).toBeVisible() - await device.takeScreenshot('8- got error') - }) -}) diff --git a/jest/test-pds.ts b/jest/test-pds.ts index 51d964394..b70a9abf0 100644 --- a/jest/test-pds.ts +++ b/jest/test-pds.ts @@ -72,6 +72,7 @@ export async function createServer( const phoneParams = phoneRequired ? { phoneVerificationRequired: true, + phoneVerificationProvider: 'twilio', twilioAccountSid: 'ACXXXXXXX', twilioAuthToken: 'AUTH', twilioServiceSid: 'VAXXXXXXXX', @@ -95,6 +96,35 @@ export async function createServer( }) mockTwilio(testNet.pds) + // add the test mod authority + if (!phoneRequired) { + const agent = new BskyAgent({service: pdsUrl}) + const res = await agent.api.com.atproto.server.createAccount({ + email: 'mod-authority@test.com', + handle: 'mod-authority.test', + password: 'hunter2', + }) + agent.api.setHeader('Authorization', `Bearer ${res.data.accessJwt}`) + await agent.api.app.bsky.actor.profile.create( + {repo: res.data.did}, + { + displayName: 'Dev-env Moderation', + description: `The pretend version of mod.bsky.app`, + }, + ) + + await agent.api.app.bsky.labeler.service.create( + {repo: res.data.did, rkey: 'self'}, + { + policies: { + labelValues: ['!hide', '!warn'], + labelValueDefinitions: [], + }, + createdAt: new Date().toISOString(), + }, + ) + } + const pic = fs.readFileSync( path.join(__dirname, '..', 'assets', 'default-avatar.png'), ) @@ -455,13 +485,13 @@ async function getPort(start = 3000) { } export const mockTwilio = (pds: TestPds) => { - if (!pds.ctx.twilio) return + if (!pds.ctx.phoneVerifier) return - pds.ctx.twilio.sendCode = async (_number: string) => { + pds.ctx.phoneVerifier.sendCode = async (_number: string) => { // do nothing } - pds.ctx.twilio.verifyCode = async (_number: string, code: string) => { + pds.ctx.phoneVerifier.verifyCode = async (_number: string, code: string) => { return code === '000000' } } diff --git a/src/lib/media/picker.e2e.tsx b/src/lib/media/picker.e2e.tsx index d7b608041..31702ab22 100644 --- a/src/lib/media/picker.e2e.tsx +++ b/src/lib/media/picker.e2e.tsx @@ -3,7 +3,6 @@ import RNFS from 'react-native-fs' import {CropperOptions} from './types' import {compressIfNeeded} from './manip' -let _imageCounter = 0 async function getFile() { let files = await RNFS.readDir( RNFS.LibraryDirectoryPath.split('/') @@ -12,7 +11,7 @@ async function getFile() { .join('/'), ) files = files.filter(file => file.path.endsWith('.JPG')) - const file = files[_imageCounter++ % files.length] + const file = files[0] return await compressIfNeeded({ path: file.path, mime: 'image/jpeg', diff --git a/src/view/com/composer/useExternalLinkFetch.e2e.ts b/src/view/com/composer/useExternalLinkFetch.e2e.ts new file mode 100644 index 000000000..ccf619db3 --- /dev/null +++ b/src/view/com/composer/useExternalLinkFetch.e2e.ts @@ -0,0 +1,45 @@ +import {useState, useEffect} from 'react' +import * as apilib from 'lib/api/index' +import {getLinkMeta} from 'lib/link-meta/link-meta' +import {ComposerOpts} from 'state/shell/composer' +import {getAgent} from '#/state/session' + +export function useExternalLinkFetch({}: { + setQuote: (opts: ComposerOpts['quote']) => void +}) { + const [extLink, setExtLink] = useState( + undefined, + ) + + useEffect(() => { + let aborted = false + const cleanup = () => { + aborted = true + } + if (!extLink) { + return cleanup + } + if (!extLink.meta) { + getLinkMeta(getAgent(), extLink.uri).then(meta => { + if (aborted) { + return + } + setExtLink({ + uri: extLink.uri, + isLoading: !!meta.image, + meta, + }) + }) + return cleanup + } + if (extLink.isLoading) { + setExtLink({ + ...extLink, + isLoading: false, // done + }) + } + return cleanup + }, [extLink]) + + return {extLink, setExtLink} +} diff --git a/src/view/com/util/UserAvatar.tsx b/src/view/com/util/UserAvatar.tsx index 8656c3f51..4beedbd5b 100644 --- a/src/view/com/util/UserAvatar.tsx +++ b/src/view/com/util/UserAvatar.tsx @@ -298,7 +298,10 @@ let EditableUserAvatar = ({ {({props}) => ( - + {avatar ? ( {({props}) => ( - + {banner ? ( Date: Wed, 20 Mar 2024 00:56:31 +0000 Subject: [Statsig] Track feed refresh (#3283) --- src/lib/statsig/events.ts | 4 ++++ src/view/com/feeds/FeedPage.tsx | 9 +++++++++ src/view/com/posts/Feed.tsx | 8 ++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src/view') diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts index b91a15ecb..420c58ed2 100644 --- a/src/lib/statsig/events.ts +++ b/src/lib/statsig/events.ts @@ -9,6 +9,10 @@ export type LogEvents = { feedType: string itemCount: number } + 'feed:refresh': { + feedType: string + reason: 'pull-to-refresh' | 'soft-reset' | 'load-latest' + } 'post:create': { imageCount: number isReply: boolean diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index e6b5d1fb6..2d0736b09 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -22,6 +22,7 @@ import {listenSoftReset} from '#/state/events' import {truncateAndInvalidate} from '#/state/queries/util' import {TabState, getTabState, getRootNavigation} from '#/lib/routes/helpers' import {isNative} from '#/platform/detection' +import {logEvent} from '#/lib/statsig/statsig' const POLL_FREQ = 60e3 // 60sec @@ -68,6 +69,10 @@ export function FeedPage({ scrollToTop() truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) setHasNew(false) + logEvent('feed:refresh', { + feedType: feed.split('|')[0], + reason: 'soft-reset', + }) } }, [navigation, isPageFocused, scrollToTop, queryClient, feed, setHasNew]) @@ -89,6 +94,10 @@ export function FeedPage({ scrollToTop() truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) setHasNew(false) + logEvent('feed:refresh', { + feedType: feed.split('|')[0], + reason: 'load-latest', + }) }, [scrollToTop, feed, queryClient, setHasNew]) return ( diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index b86646a4d..8afcce94f 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -90,6 +90,7 @@ let Feed = ({ const [isPTRing, setIsPTRing] = React.useState(false) const checkForNewRef = React.useRef<(() => void) | null>(null) const lastFetchRef = React.useRef(Date.now()) + const feedType = feed.split('|')[0] const opts = React.useMemo( () => ({enabled, ignoreFilterFor}), @@ -214,6 +215,10 @@ let Feed = ({ const onRefresh = React.useCallback(async () => { track('Feed:onRefresh') + logEvent('feed:refresh', { + feedType: feedType, + reason: 'pull-to-refresh', + }) setIsPTRing(true) try { await refetch() @@ -222,9 +227,8 @@ let Feed = ({ logger.error('Failed to refresh posts feed', {message: err}) } setIsPTRing(false) - }, [refetch, track, setIsPTRing, onHasNew]) + }, [refetch, track, setIsPTRing, onHasNew, feedType]) - const feedType = feed.split('|')[0] const onEndReached = React.useCallback(async () => { if (isFetching || !hasNextPage || isError) return -- cgit 1.4.1 From 3d8d1dd1737c11d924bec7d51ae4deb6cb6336b0 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 20 Mar 2024 03:24:05 +0000 Subject: [Statsig] Track login/logout (#3286) * [Statsig] Track login/logout * Fix missing attribution --- src/Navigation.tsx | 6 ++- src/lib/hooks/useAccountSwitcher.ts | 8 +++- src/lib/statsig/events.ts | 7 +++ src/screens/Deactivated.tsx | 4 +- src/state/session/index.tsx | 61 +++++++++++++++++---------- src/view/com/auth/login/ChooseAccountForm.tsx | 5 +++ src/view/com/auth/login/LoginForm.tsx | 13 +++--- src/view/com/modals/SwitchAccount.tsx | 6 ++- src/view/com/testing/TestCtrls.e2e.tsx | 28 +++++++----- src/view/screens/Settings/index.tsx | 8 +++- 10 files changed, 98 insertions(+), 48 deletions(-) (limited to 'src/view') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 3d6a15c4e..83aede722 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -565,7 +565,11 @@ function RoutesContainer({children}: React.PropsWithChildren<{}>) { } function getCurrentRouteName() { - return navigationRef.getCurrentRoute()?.name + if (navigationRef.isReady()) { + return navigationRef.getCurrentRoute()?.name + } else { + return undefined + } } /** diff --git a/src/lib/hooks/useAccountSwitcher.ts b/src/lib/hooks/useAccountSwitcher.ts index 74b5674d5..eb1685a0a 100644 --- a/src/lib/hooks/useAccountSwitcher.ts +++ b/src/lib/hooks/useAccountSwitcher.ts @@ -6,6 +6,7 @@ import {useSessionApi, SessionAccount} from '#/state/session' import * as Toast from '#/view/com/util/Toast' import {useCloseAllActiveElements} from '#/state/util' import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import {LogEvents} from '../statsig/statsig' export function useAccountSwitcher() { const {track} = useAnalytics() @@ -14,7 +15,10 @@ export function useAccountSwitcher() { const {requestSwitchToAccount} = useLoggedOutViewControls() const onPressSwitchAccount = useCallback( - async (account: SessionAccount) => { + async ( + account: SessionAccount, + logContext: LogEvents['account:loggedIn']['logContext'], + ) => { track('Settings:SwitchAccountButtonClicked') try { @@ -28,7 +32,7 @@ export function useAccountSwitcher() { // So we change the URL ourselves. The navigator will pick it up on remount. history.pushState(null, '', '/') } - await selectAccount(account) + await selectAccount(account, logContext) setTimeout(() => { Toast.show(`Signed in as @${account.handle}`) }, 100) diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts index 420c58ed2..f57c8d416 100644 --- a/src/lib/statsig/events.ts +++ b/src/lib/statsig/events.ts @@ -2,6 +2,13 @@ export type LogEvents = { init: { initMs: number } + 'account:loggedIn': { + logContext: 'LoginForm' | 'SwitchAccount' | 'ChooseAccountForm' | 'Settings' + withPassword: boolean + } + 'account:loggedOut': { + logContext: 'SwitchAccount' | 'Settings' | 'Deactivated' + } 'notifications:openApp': {} 'state:background': {} 'state:foreground': {} diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx index f4c201475..7e87973cb 100644 --- a/src/screens/Deactivated.tsx +++ b/src/screens/Deactivated.tsx @@ -147,7 +147,7 @@ export function Deactivated() { variant="ghost" size="large" label={_(msg`Log out`)} - onPress={logout}> + onPress={() => logout('Deactivated')}> Log out @@ -176,7 +176,7 @@ export function Deactivated() { variant="ghost" size="large" label={_(msg`Log out`)} - onPress={logout}> + onPress={() => logout('Deactivated')}> Log out diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index 6b1474839..b6748bfad 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -20,6 +20,7 @@ import {useCloseAllActiveElements} from '#/state/util' import {track} from '#/lib/analytics/analytics' import {hasProp} from '#/lib/type-guards' import {readLabelers} from './agent-config' +import {logEvent, LogEvents} from '#/lib/statsig/statsig' let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT @@ -54,17 +55,22 @@ export type ApiContext = { verificationPhone?: string verificationCode?: string }) => Promise - login: (props: { - service: string - identifier: string - password: string - }) => Promise + login: ( + props: { + service: string + identifier: string + password: string + }, + logContext: LogEvents['account:loggedIn']['logContext'], + ) => Promise /** * A full logout. Clears the `currentAccount` from session, AND removes * access tokens from all accounts, so that returning as any user will * require a full login. */ - logout: () => Promise + logout: ( + logContext: LogEvents['account:loggedOut']['logContext'], + ) => Promise /** * A partial logout. Clears the `currentAccount` from session, but DOES NOT * clear access tokens from accounts, allowing the user to return to their @@ -76,7 +82,10 @@ export type ApiContext = { initSession: (account: SessionAccount) => Promise resumeSession: (account?: SessionAccount) => Promise removeAccount: (account: SessionAccount) => void - selectAccount: (account: SessionAccount) => Promise + selectAccount: ( + account: SessionAccount, + logContext: LogEvents['account:loggedIn']['logContext'], + ) => Promise updateCurrentAccount: ( account: Partial< Pick @@ -286,7 +295,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { ) const login = React.useCallback( - async ({service, identifier, password}) => { + async ({service, identifier, password}, logContext) => { logger.debug(`session: login`, {}, logger.DebugContext.session) const agent = new BskyAgent({service}) @@ -329,24 +338,29 @@ export function Provider({children}: React.PropsWithChildren<{}>) { logger.debug(`session: logged in`, {}, logger.DebugContext.session) track('Sign In', {resumedSession: false}) + logEvent('account:loggedIn', {logContext, withPassword: true}) }, [upsertAccount, queryClient, clearCurrentAccount], ) - const logout = React.useCallback(async () => { - logger.debug(`session: logout`) - clearCurrentAccount() - setStateAndPersist(s => { - return { - ...s, - accounts: s.accounts.map(a => ({ - ...a, - refreshJwt: undefined, - accessJwt: undefined, - })), - } - }) - }, [clearCurrentAccount, setStateAndPersist]) + const logout = React.useCallback( + async logContext => { + logger.debug(`session: logout`) + clearCurrentAccount() + setStateAndPersist(s => { + return { + ...s, + accounts: s.accounts.map(a => ({ + ...a, + refreshJwt: undefined, + accessJwt: undefined, + })), + } + }) + logEvent('account:loggedOut', {logContext}) + }, + [clearCurrentAccount, setStateAndPersist], + ) const initSession = React.useCallback( async account => { @@ -540,11 +554,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) { ) const selectAccount = React.useCallback( - async account => { + async (account, logContext) => { setState(s => ({...s, isSwitchingAccounts: true})) try { await initSession(account) setState(s => ({...s, isSwitchingAccounts: false})) + logEvent('account:loggedIn', {logContext, withPassword: false}) } catch (e) { // reset this in case of error setState(s => ({...s, isSwitchingAccounts: false})) diff --git a/src/view/com/auth/login/ChooseAccountForm.tsx b/src/view/com/auth/login/ChooseAccountForm.tsx index d3b075fdb..e754c8483 100644 --- a/src/view/com/auth/login/ChooseAccountForm.tsx +++ b/src/view/com/auth/login/ChooseAccountForm.tsx @@ -16,6 +16,7 @@ import {useSession, useSessionApi, SessionAccount} from '#/state/session' import {useProfileQuery} from '#/state/queries/profile' import {useLoggedOutViewControls} from '#/state/shell/logged-out' import * as Toast from '#/view/com/util/Toast' +import {logEvent} from '#/lib/statsig/statsig' function AccountItem({ account, @@ -102,6 +103,10 @@ export const ChooseAccountForm = ({ Toast.show(_(msg`Already signed in as @${account.handle}`)) } else { await initSession(account) + logEvent('account:loggedIn', { + logContext: 'ChooseAccountForm', + withPassword: false, + }) track('Sign In', {resumedSession: true}) setTimeout(() => { Toast.show(_(msg`Signed in as @${account.handle}`)) diff --git a/src/view/com/auth/login/LoginForm.tsx b/src/view/com/auth/login/LoginForm.tsx index 3202d69c5..92f495575 100644 --- a/src/view/com/auth/login/LoginForm.tsx +++ b/src/view/com/auth/login/LoginForm.tsx @@ -98,11 +98,14 @@ export const LoginForm = ({ } // TODO remove double login - await login({ - service: serviceUrl, - identifier: fullIdent, - password, - }) + await login( + { + service: serviceUrl, + identifier: fullIdent, + password, + }, + 'LoginForm', + ) } catch (e: any) { const errMsg = e.toString() setIsProcessing(false) diff --git a/src/view/com/modals/SwitchAccount.tsx b/src/view/com/modals/SwitchAccount.tsx index 0658805bd..892b07c9a 100644 --- a/src/view/com/modals/SwitchAccount.tsx +++ b/src/view/com/modals/SwitchAccount.tsx @@ -39,7 +39,7 @@ function SwitchAccountCard({account}: {account: SessionAccount}) { track('Settings:SignOutButtonClicked') closeAllActiveElements() // needs to be in timeout or the modal re-opens - setTimeout(() => logout(), 0) + setTimeout(() => logout('SwitchAccount'), 0) }, [track, logout, closeAllActiveElements]) const contents = ( @@ -95,7 +95,9 @@ function SwitchAccountCard({account}: {account: SessionAccount}) { key={account.did} style={[isSwitchingAccounts && styles.dimmed]} onPress={ - isSwitchingAccounts ? undefined : () => onPressSwitchAccount(account) + isSwitchingAccounts + ? undefined + : () => onPressSwitchAccount(account, 'SwitchAccount') } accessibilityRole="button" accessibilityLabel={_(msg`Switch to ${account.handle}`)} diff --git a/src/view/com/testing/TestCtrls.e2e.tsx b/src/view/com/testing/TestCtrls.e2e.tsx index e1e899488..1eb99c4f5 100644 --- a/src/view/com/testing/TestCtrls.e2e.tsx +++ b/src/view/com/testing/TestCtrls.e2e.tsx @@ -22,18 +22,24 @@ export function TestCtrls() { const {mutate: setFeedViewPref} = useSetFeedViewPreferencesMutation() const {setShowLoggedOut} = useLoggedOutViewControls() const onPressSignInAlice = async () => { - await login({ - service: 'http://localhost:3000', - identifier: 'alice.test', - password: 'hunter2', - }) + await login( + { + service: 'http://localhost:3000', + identifier: 'alice.test', + password: 'hunter2', + }, + 'LoginForm', + ) } const onPressSignInBob = async () => { - await login({ - service: 'http://localhost:3000', - identifier: 'bob.test', - password: 'hunter2', - }) + await login( + { + service: 'http://localhost:3000', + identifier: 'bob.test', + password: 'hunter2', + }, + 'LoginForm', + ) } return ( @@ -51,7 +57,7 @@ export function TestCtrls() { /> logout()} + onPress={() => logout('Settings')} accessibilityRole="button" style={BTN} /> diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx index 7e808f910..3967678b4 100644 --- a/src/view/screens/Settings/index.tsx +++ b/src/view/screens/Settings/index.tsx @@ -100,7 +100,9 @@ function SettingsAccountCard({account}: {account: SessionAccount}) { {isCurrentAccount ? ( { + logout('Settings') + }} accessibilityRole="button" accessibilityLabel={_(msg`Sign out`)} accessibilityHint={`Signs ${profile?.displayName} out of Bluesky`}> @@ -129,7 +131,9 @@ function SettingsAccountCard({account}: {account: SessionAccount}) { testID={`switchToAccountBtn-${account.handle}`} key={account.did} onPress={ - isSwitchingAccounts ? undefined : () => onPressSwitchAccount(account) + isSwitchingAccounts + ? undefined + : () => onPressSwitchAccount(account, 'Settings') } accessibilityRole="button" accessibilityLabel={_(msg`Switch to ${account.handle}`)} -- cgit 1.4.1