diff options
Diffstat (limited to 'src/view/com')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 2 | ||||
-rw-r--r-- | src/view/com/composer/useExternalLinkFetch.e2e.ts | 45 | ||||
-rw-r--r-- | src/view/com/feeds/FeedPage.tsx | 9 | ||||
-rw-r--r-- | src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx | 27 | ||||
-rw-r--r-- | src/view/com/lightbox/Lightbox.web.tsx | 54 | ||||
-rw-r--r-- | src/view/com/modals/SwitchAccount.tsx | 6 | ||||
-rw-r--r-- | src/view/com/post-thread/PostThread.tsx | 3 | ||||
-rw-r--r-- | src/view/com/posts/Feed.tsx | 8 | ||||
-rw-r--r-- | src/view/com/posts/FeedErrorMessage.tsx | 8 | ||||
-rw-r--r-- | src/view/com/testing/TestCtrls.e2e.tsx | 28 | ||||
-rw-r--r-- | src/view/com/util/UserAvatar.tsx | 5 | ||||
-rw-r--r-- | src/view/com/util/UserBanner.tsx | 5 | ||||
-rw-r--r-- | src/view/com/util/forms/NativeDropdown.web.tsx | 2 | ||||
-rw-r--r-- | src/view/com/util/text/Text.tsx | 15 |
14 files changed, 169 insertions, 48 deletions
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 ? ( - <View style={[s.mt5, isWeb && s.mb10]}> + <View style={[s.mt5, isWeb && s.mb10, {pointerEvents: 'none'}]}> <QuoteEmbed quote={quote} /> </View> ) : undefined} 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<apilib.ExternalEmbedDraft | undefined>( + 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/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/lightbox/ImageViewing/components/ImageDefaultHeader.tsx b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx index 3872919de..88476c8e1 100644 --- a/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx +++ b/src/view/com/lightbox/ImageViewing/components/ImageDefaultHeader.tsx @@ -6,7 +6,13 @@ * */ import React from 'react' -import {SafeAreaView, Text, TouchableOpacity, StyleSheet} from 'react-native' +import { + SafeAreaView, + TouchableOpacity, + StyleSheet, + ViewStyle, +} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -23,14 +29,14 @@ const ImageDefaultHeader = ({onRequestClose}: Props) => { return ( <SafeAreaView style={styles.root}> <TouchableOpacity - style={styles.closeButton} + style={[styles.closeButton, styles.blurredBackground]} onPress={onRequestClose} hitSlop={HIT_SLOP} accessibilityRole="button" accessibilityLabel={_(msg`Close image`)} accessibilityHint={_(msg`Closes viewer for header image`)} onAccessibilityEscape={onRequestClose}> - <Text style={styles.closeText}>✕</Text> + <FontAwesomeIcon icon="close" color={'#fff'} size={22} /> </TouchableOpacity> </SafeAreaView> ) @@ -42,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', @@ -51,13 +57,10 @@ 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)', + } 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 ( <View style={styles.mask}> <TouchableWithoutFeedback @@ -130,28 +140,38 @@ function LightboxInner({ {canGoLeft && ( <TouchableOpacity onPress={onPressLeft} - style={[styles.btn, styles.leftBtn]} + style={[ + styles.btn, + btnStyle, + styles.leftBtn, + styles.blurredBackground, + ]} accessibilityRole="button" accessibilityLabel={_(msg`Previous image`)} accessibilityHint=""> <FontAwesomeIcon icon="angle-left" style={styles.icon as FontAwesomeIconStyle} - size={40} + size={iconSize} /> </TouchableOpacity> )} {canGoRight && ( <TouchableOpacity onPress={onPressRight} - style={[styles.btn, styles.rightBtn]} + style={[ + styles.btn, + btnStyle, + styles.rightBtn, + styles.blurredBackground, + ]} accessibilityRole="button" accessibilityLabel={_(msg`Next image`)} accessibilityHint=""> <FontAwesomeIcon icon="angle-right" style={styles.icon as FontAwesomeIconStyle} - size={40} + size={iconSize} /> </TouchableOpacity> )} @@ -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/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/post-thread/PostThread.tsx b/src/view/com/post-thread/PostThread.tsx index 8042e7bd5..ba74ba6d8 100644 --- a/src/view/com/post-thread/PostThread.tsx +++ b/src/view/com/post-thread/PostThread.tsx @@ -108,7 +108,8 @@ export function PostThread({ ?.ui('contentList') .blurs.find( cause => - cause.type === 'label' && cause.labelDef.id === '!no-unauthenticated', + cause.type === 'label' && + cause.labelDef.identifier === '!no-unauthenticated', ) }, [rootPost, moderationOpts]) 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<number>(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 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 ( <FeedgenErrorMessage @@ -240,6 +240,9 @@ function detectKnownError( if (typeof error !== 'string') { error = error.toString() } + if (error.includes(KnownError.FeedNSFPublic)) { + return KnownError.FeedNSFPublic + } if (!feedDesc.startsWith('feedgen')) { return KnownError.Unknown } @@ -263,8 +266,5 @@ function detectKnownError( if (error.includes('feed provided an invalid response')) { return KnownError.FeedgenBadResponse } - if (error.includes(KnownError.FeedNSFPublic)) { - return KnownError.FeedNSFPublic - } return KnownError.FeedgenUnknown } 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 ( <View style={{position: 'absolute', top: 100, right: 0, zIndex: 100}}> @@ -51,7 +57,7 @@ export function TestCtrls() { /> <Pressable testID="e2eSignOut" - onPress={() => logout()} + onPress={() => logout('Settings')} accessibilityRole="button" style={BTN} /> 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 = ({ <Menu.Root> <Menu.Trigger label={_(msg`Edit avatar`)}> {({props}) => ( - <TouchableOpacity {...props} activeOpacity={0.8}> + <TouchableOpacity + {...props} + activeOpacity={0.8} + testID="changeAvatarBtn"> {avatar ? ( <HighPriorityImage testID="userAvatarImage" diff --git a/src/view/com/util/UserBanner.tsx b/src/view/com/util/UserBanner.tsx index 4fb3726cd..4d73b853b 100644 --- a/src/view/com/util/UserBanner.tsx +++ b/src/view/com/util/UserBanner.tsx @@ -84,7 +84,10 @@ export function UserBanner({ <Menu.Root> <Menu.Trigger label={_(msg`Edit avatar`)}> {({props}) => ( - <TouchableOpacity {...props} activeOpacity={0.8}> + <TouchableOpacity + {...props} + activeOpacity={0.8} + testID="changeBannerBtn"> {banner ? ( <Image testID="userBannerImage" 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 ( <RNText - style={[s.black, typography, lineHeightStyle, style]} + style={[ + s.black, + typography, + isWeb && fontFamilyStyle, + lineHeightStyle, + style, + ]} // @ts-ignore web only -esb dataSet={Object.assign({tooltip: title}, dataSet || {})} selectable={selectable} |