From 1a5afccdb809d7a8a2e9ebb356b499135a5ff175 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Thu, 29 Feb 2024 10:31:45 -0600 Subject: Add TagMenu controls stub on web (#3028) --- src/components/TagMenu/index.web.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/components/TagMenu/index.web.tsx b/src/components/TagMenu/index.web.tsx index 31187112f..3aebfbba2 100644 --- a/src/components/TagMenu/index.web.tsx +++ b/src/components/TagMenu/index.web.tsx @@ -14,8 +14,21 @@ import { } from '#/state/queries/preferences' import {enforceLen} from '#/lib/strings/helpers' import {web} from '#/alf' +import * as Dialog from '#/components/Dialog' -export function useTagMenuControl() {} +export function useTagMenuControl(): Dialog.DialogControlProps { + return { + id: '', + // @ts-ignore + ref: null, + open: () => { + throw new Error(`TagMenu controls are only available on native platforms`) + }, + close: () => { + throw new Error(`TagMenu controls are only available on native platforms`) + }, + } +} export function TagMenu({ children, -- cgit 1.4.1 From 39d324ab8bd99ca0e19f3e8f4dea5a61d54e6bb4 Mon Sep 17 00:00:00 2001 From: Hailey Date: Thu, 29 Feb 2024 15:23:28 -0800 Subject: Fix link warnings (#3058) * fix problems where www.bsky.app shows as a potential danger * never default to disabling warning * remove more defaults * update storybook cases * oops * reverse --- src/components/Link.tsx | 14 +++++++------- src/components/RichText.tsx | 3 +-- src/lib/strings/url-helpers.ts | 12 +++--------- src/view/com/util/Link.tsx | 10 +++++----- src/view/com/util/text/RichText.tsx | 1 - src/view/screens/Storybook/Links.tsx | 28 ++++++++-------------------- 6 files changed, 24 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 0a654fed2..8c963909b 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -49,7 +49,7 @@ type BaseLinkProps = Pick< * * Note: atm this only works for `InlineLink`s with a string child. */ - warnOnMismatchingTextChild?: boolean + disableMismatchWarning?: boolean /** * Callback for when the link is pressed. Prevent default and return `false` @@ -69,7 +69,7 @@ export function useLink({ to, displayText, action = 'push', - warnOnMismatchingTextChild, + disableMismatchWarning, onPress: outerOnPress, }: BaseLinkProps & { displayText: string @@ -90,7 +90,7 @@ export function useLink({ if (exitEarlyIfFalse === false) return const requiresWarning = Boolean( - warnOnMismatchingTextChild && + !disableMismatchWarning && displayText && isExternal && linkRequiresWarning(href, displayText), @@ -148,7 +148,7 @@ export function useLink({ }, [ outerOnPress, - warnOnMismatchingTextChild, + disableMismatchWarning, displayText, isExternal, href, @@ -167,7 +167,7 @@ export function useLink({ } } -export type LinkProps = Omit & +export type LinkProps = Omit & Omit /** @@ -226,7 +226,7 @@ export function InlineLink({ children, to, action = 'push', - warnOnMismatchingTextChild, + disableMismatchWarning, style, onPress: outerOnPress, download, @@ -239,7 +239,7 @@ export function InlineLink({ to, displayText: stringChildren ? children : '', action, - warnOnMismatchingTextChild, + disableMismatchWarning, onPress: outerOnPress, }) const { diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 3d5f08026..5d82d7e5e 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -105,8 +105,7 @@ export function RichText({ to={link.uri} style={[...styles, {pointerEvents: 'auto'}]} // @ts-ignore TODO - dataSet={WORD_WRAP} - warnOnMismatchingLabel> + dataSet={WORD_WRAP}> {toShortUrl(segment.text)} , ) diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts index ef341154d..ba2cdb39b 100644 --- a/src/lib/strings/url-helpers.ts +++ b/src/lib/strings/url-helpers.ts @@ -157,17 +157,11 @@ export function linkRequiresWarning(uri: string, label: string) { const host = urip.hostname.toLowerCase() - if (host === 'bsky.app') { + // Hosts that end with bsky.app or bsky.social should be trusted by default. + if (host.endsWith('bsky.app') || host.endsWith('bsky.social')) { // if this is a link to internal content, // warn if it represents itself as a URL to another app - if ( - labelDomain && - labelDomain !== 'bsky.app' && - isPossiblyAUrl(labelDomain) - ) { - return true - } - return false + return !!labelDomain && labelDomain !== host && isPossiblyAUrl(labelDomain) } else { // if this is a link to external content, // warn if the label doesnt match the target diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx index d52d3c0e6..e50fb7f09 100644 --- a/src/view/com/util/Link.tsx +++ b/src/view/com/util/Link.tsx @@ -159,7 +159,7 @@ export const TextLink = memo(function TextLink({ dataSet, title, onPress, - warnOnMismatchingLabel, + disableMismatchWarning, navigationAction, ...orgProps }: { @@ -172,7 +172,7 @@ export const TextLink = memo(function TextLink({ lineHeight?: number dataSet?: any title?: string - warnOnMismatchingLabel?: boolean + disableMismatchWarning?: boolean navigationAction?: 'push' | 'replace' | 'navigate' } & TextProps) { const {...props} = useLinkProps({to: sanitizeUrl(href)}) @@ -180,14 +180,14 @@ export const TextLink = memo(function TextLink({ const {openModal, closeModal} = useModalControls() const openLink = useOpenLink() - if (warnOnMismatchingLabel && typeof text !== 'string') { + if (!disableMismatchWarning && typeof text !== 'string') { console.error('Unable to detect mismatching label') } props.onPress = React.useCallback( (e?: Event) => { const requiresWarning = - warnOnMismatchingLabel && + !disableMismatchWarning && linkRequiresWarning(href, typeof text === 'string' ? text : '') if (requiresWarning) { e?.preventDefault?.() @@ -227,7 +227,7 @@ export const TextLink = memo(function TextLink({ navigation, href, text, - warnOnMismatchingLabel, + disableMismatchWarning, navigationAction, openLink, ], diff --git a/src/view/com/util/text/RichText.tsx b/src/view/com/util/text/RichText.tsx index 0ec3f3181..f4ade30e5 100644 --- a/src/view/com/util/text/RichText.tsx +++ b/src/view/com/util/text/RichText.tsx @@ -114,7 +114,6 @@ export function RichText({ href={link.uri} style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]} dataSet={WORD_WRAP} - warnOnMismatchingLabel selectable={selectable} />, ) diff --git a/src/view/screens/Storybook/Links.tsx b/src/view/screens/Storybook/Links.tsx index 3f1806906..f9ecfba55 100644 --- a/src/view/screens/Storybook/Links.tsx +++ b/src/view/screens/Storybook/Links.tsx @@ -4,7 +4,7 @@ import {View} from 'react-native' import {useTheme, atoms as a} from '#/alf' import {ButtonText} from '#/components/Button' import {InlineLink, Link} from '#/components/Link' -import {H1, H3, Text} from '#/components/Typography' +import {H1, Text} from '#/components/Typography' export function Links() { const t = useTheme() @@ -13,31 +13,19 @@ export function Links() {

Links

- - External + + https://google.com - -

External with custom children

+ + External with custom children (google.com) - External with custom children - - - https://bsky.social + Internal (bsky.social) - - Internal + + Internal (bsky.app) Date: Thu, 29 Feb 2024 15:27:00 -0800 Subject: Dismiss keyboard when closing dialog (#3053) --- src/components/Dialog/index.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index 6dfc24f3b..b96d1f835 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -1,5 +1,5 @@ import React, {useImperativeHandle} from 'react' -import {View, Dimensions} from 'react-native' +import {View, Dimensions, Keyboard} from 'react-native' import BottomSheet, { BottomSheetBackdrop, BottomSheetScrollView, @@ -78,6 +78,7 @@ export function Outer({ const onChange = React.useCallback( (index: number) => { if (index === -1) { + Keyboard.dismiss() try { closeCallback.current?.() } catch (e: any) { @@ -190,8 +191,15 @@ export function ScrollableInner({children, style}: DialogInnerProps) { export function Handle() { const t = useTheme() + + const onTouchStart = React.useCallback(() => { + Keyboard.dismiss() + }, []) + return ( - + Date: Thu, 29 Feb 2024 19:30:30 -0600 Subject: Bump API SDK, add validation to MutedWords (#3055) * Bump API SDK, add validation to MutedWords * Tweaks to error state * Comment * Early return --- package.json | 2 +- src/components/TagMenu/index.tsx | 6 ++-- src/components/TagMenu/index.web.tsx | 5 ++- src/components/dialogs/MutedWords.tsx | 60 +++++++++++++++++++++++++++++------ yarn.lock | 40 +++++++++++++++++++---- 5 files changed, 89 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/package.json b/package.json index e9dd9202d..378d520bc 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "update-extensions": "scripts/updateExtensions.sh" }, "dependencies": { - "@atproto/api": "^0.10.0", + "@atproto/api": "^0.10.3", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@emoji-mart/react": "^1.1.1", diff --git a/src/components/TagMenu/index.tsx b/src/components/TagMenu/index.tsx index 2fec7a188..c18c0d6a2 100644 --- a/src/components/TagMenu/index.tsx +++ b/src/components/TagMenu/index.tsx @@ -215,14 +215,12 @@ export function TagMenu({ if (isMuted) { resetUpsert() removeMutedWord({ - value: sanitizedTag, + value: tag, targets: ['tag'], }) } else { resetRemove() - upsertMutedWord([ - {value: sanitizedTag, targets: ['tag']}, - ]) + upsertMutedWord([{value: tag, targets: ['tag']}]) } }) }}> diff --git a/src/components/TagMenu/index.web.tsx b/src/components/TagMenu/index.web.tsx index 3aebfbba2..4fcb4c812 100644 --- a/src/components/TagMenu/index.web.tsx +++ b/src/components/TagMenu/index.web.tsx @@ -104,9 +104,9 @@ export function TagMenu({ : _(msg`Mute ${truncatedTag}`), onPress() { if (isMuted) { - removeMutedWord({value: sanitizedTag, targets: ['tag']}) + removeMutedWord({value: tag, targets: ['tag']}) } else { - upsertMutedWord([{value: sanitizedTag, targets: ['tag']}]) + upsertMutedWord([{value: tag, targets: ['tag']}]) } }, testID: 'tagMenuMute', @@ -127,7 +127,6 @@ export function TagMenu({ preferences, tag, truncatedTag, - sanitizedTag, upsertMutedWord, removeMutedWord, ]) diff --git a/src/components/dialogs/MutedWords.tsx b/src/components/dialogs/MutedWords.tsx index 7c0d4fbca..453b13513 100644 --- a/src/components/dialogs/MutedWords.tsx +++ b/src/components/dialogs/MutedWords.tsx @@ -2,7 +2,7 @@ import React from 'react' import {View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {AppBskyActorDefs} from '@atproto/api' +import {AppBskyActorDefs, sanitizeMutedWordValue} from '@atproto/api' import { usePreferencesQuery, @@ -10,7 +10,14 @@ import { useRemoveMutedWordMutation, } from '#/state/queries/preferences' import {isNative} from '#/platform/detection' -import {atoms as a, useTheme, useBreakpoints, ViewStyleProp, web} from '#/alf' +import { + atoms as a, + useTheme, + useBreakpoints, + ViewStyleProp, + web, + native, +} from '#/alf' import {Text} from '#/components/Typography' import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' @@ -48,24 +55,29 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) { const {isPending, mutateAsync: addMutedWord} = useUpsertMutedWordsMutation() const [field, setField] = React.useState('') const [options, setOptions] = React.useState(['content']) - const [_error, setError] = React.useState('') + const [error, setError] = React.useState('') const submit = React.useCallback(async () => { - const value = field.trim() + const sanitizedValue = sanitizeMutedWordValue(field) const targets = ['tag', options.includes('content') && 'content'].filter( Boolean, ) as AppBskyActorDefs.MutedWord['targets'] - if (!value || !targets.length) return + if (!sanitizedValue || !targets.length) { + setField('') + setError(_(msg`Please enter a valid word, tag, or phrase to mute`)) + return + } try { - await addMutedWord([{value, targets}]) + // send raw value and rely on SDK as sanitization source of truth + await addMutedWord([{value: field, targets}]) setField('') } catch (e: any) { logger.error(`Failed to save muted word`, {message: e.message}) setError(e.message) } - }, [field, options, addMutedWord, setField]) + }, [_, field, options, addMutedWord, setField]) return ( @@ -87,7 +99,12 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) { label={_(msg`Enter a word or tag`)} placeholder={_(msg`Enter a word or tag`)} value={field} - onChangeText={setField} + onChangeText={value => { + if (error) { + setError('') + } + setField(value) + }} onSubmitEditing={submit} /> @@ -99,7 +116,7 @@ function MutedWordsInner({}: {control: Dialog.DialogOuterProps['control']}) { + {error && ( + + + {error} + + + )} + Date: Thu, 29 Feb 2024 17:56:29 -0800 Subject: Dedicated screen for hashtags, POC ALF list (#3047) * create dedicated hashtag "search" screen clarify loading component name more adjustments rework `ViewHeader` to keep chevron centered w/ first line adjustments adjustments use `author` instead of `handle` in route add web route for url add web route for url Add desktop list header support web keep header lowercase add optional subtitle to view header correct isFetching logic oops use `isFetching` for clarity in footer combine logic update bskyweb finish screen style, add footer, add spinner, etc add list add header, params create a screen * add variable to server path * localize `By` * add empty state * more adjustments * sanitize author * fix web * add custom message for hashtag not found error * ellipsis in middle * fix * fix trans * account for multiple # * encode # * replaceall * Use sanitized tag * don't call function in lingui * add share button --------- Co-authored-by: Eric Bailey --- bskyweb/cmd/bskyweb/server.go | 1 + src/Navigation.tsx | 6 + src/components/Lists.tsx | 228 +++++++++++++++++++++++++++++++++++ src/components/RichText.tsx | 11 +- src/components/TagMenu/index.tsx | 54 ++++----- src/components/TagMenu/index.web.tsx | 25 ++-- src/lib/routes/types.ts | 3 + src/routes.ts | 1 + src/screens/Hashtag.tsx | 157 ++++++++++++++++++++++++ src/view/com/util/ViewHeader.tsx | 97 +++++++++------ 10 files changed, 502 insertions(+), 81 deletions(-) create mode 100644 src/components/Lists.tsx create mode 100644 src/screens/Hashtag.tsx (limited to 'src') diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index f13d568b7..6b76acc94 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -180,6 +180,7 @@ func serve(cctx *cli.Context) error { e.GET("/", server.WebHome) // generic routes + e.GET("/hashtag/:tag", server.WebGeneric) e.GET("/search", server.WebGeneric) e.GET("/feeds", server.WebGeneric) e.GET("/notifications", server.WebGeneric) diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 0aeeeb6ad..c650c1f40 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -77,6 +77,7 @@ import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbed import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth' import {msg} from '@lingui/macro' import {i18n, MessageDescriptor} from '@lingui/core' +import HashtagScreen from '#/screens/Hashtag' const navigationRef = createNavigationContainerRef() @@ -262,6 +263,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { requireAuth: true, }} /> + HashtagScreen} + options={{title: title(msg`Hashtag`)}} + /> ) } diff --git a/src/components/Lists.tsx b/src/components/Lists.tsx new file mode 100644 index 000000000..cf00734f0 --- /dev/null +++ b/src/components/Lists.tsx @@ -0,0 +1,228 @@ +import React from 'react' +import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {View} from 'react-native' +import {Loader} from '#/components/Loader' +import {Trans} from '@lingui/macro' +import {cleanError} from 'lib/strings/errors' +import {Button} from '#/components/Button' +import {Text} from '#/components/Typography' +import {StackActions} from '@react-navigation/native' +import {useNavigation} from '@react-navigation/core' +import {NavigationProp} from 'lib/routes/types' + +export function ListFooter({ + isFetching, + isError, + error, + onRetry, +}: { + isFetching: boolean + isError: boolean + error?: string + onRetry?: () => Promise +}) { + const t = useTheme() + + return ( + + {isFetching ? ( + + ) : ( + + )} + + ) +} + +function ListFooterMaybeError({ + isError, + error, + onRetry, +}: { + isError: boolean + error?: string + onRetry?: () => Promise +}) { + const t = useTheme() + + if (!isError) return null + + return ( + + + + {error ? ( + cleanError(error) + ) : ( + Oops, something went wrong! + )} + + + + + ) +} + +export function ListHeaderDesktop({ + title, + subtitle, +}: { + title: string + subtitle?: string +}) { + const {gtTablet} = useBreakpoints() + const t = useTheme() + + if (!gtTablet) return null + + return ( + + {title} + {subtitle ? ( + + {subtitle} + + ) : undefined} + + ) +} + +export function ListMaybePlaceholder({ + isLoading, + isEmpty, + isError, + empty, + error, + onRetry, +}: { + isLoading: boolean + isEmpty: boolean + isError: boolean + empty?: string + error?: string + onRetry?: () => Promise +}) { + const navigation = useNavigation() + const t = useTheme() + + const canGoBack = navigation.canGoBack() + const onGoBack = React.useCallback(() => { + if (canGoBack) { + navigation.goBack() + } else { + navigation.navigate('HomeTab') + navigation.dispatch(StackActions.popToTop()) + } + }, [navigation, canGoBack]) + + if (!isEmpty) return null + + return ( + + {isLoading ? ( + + + + ) : ( + <> + + + {isError ? ( + Oops! + ) : isEmpty ? ( + Page not found + ) : undefined} + + + {isError ? ( + + {error ? error : Something went wrong!} + + ) : isEmpty ? ( + + {empty ? ( + empty + ) : ( + + We're sorry! We can't find the page you were looking for. + + )} + + ) : undefined} + + + {isError && onRetry && ( + + )} + + + + )} + + ) +} diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 5d82d7e5e..1a14415cf 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -120,6 +120,7 @@ export function RichText({ - {tag} + {text} diff --git a/src/components/TagMenu/index.tsx b/src/components/TagMenu/index.tsx index c18c0d6a2..c9ced9a54 100644 --- a/src/components/TagMenu/index.tsx +++ b/src/components/TagMenu/index.tsx @@ -34,6 +34,10 @@ export function TagMenu({ authorHandle, }: React.PropsWithChildren<{ control: Dialog.DialogOuterProps['control'] + /** + * This should be the sanitized tag value from the facet itself, not the + * "display" value with a leading `#`. + */ tag: string authorHandle?: string }>) { @@ -52,16 +56,16 @@ export function TagMenu({ variables: optimisticRemove, reset: resetRemove, } = useRemoveMutedWordMutation() + const displayTag = '#' + tag - const sanitizedTag = tag.replace(/^#/, '') const isMuted = Boolean( (preferences?.mutedWords?.find( - m => m.value === sanitizedTag && m.targets.includes('tag'), + m => m.value === tag && m.targets.includes('tag'), ) ?? optimisticUpsert?.find( - m => m.value === sanitizedTag && m.targets.includes('tag'), + m => m.value === tag && m.targets.includes('tag'), )) && - !(optimisticRemove?.value === sanitizedTag), + !(optimisticRemove?.value === tag), ) return ( @@ -71,7 +75,7 @@ export function TagMenu({ - + {isPreferencesLoading ? ( @@ -87,18 +91,14 @@ export function TagMenu({ t.atoms.bg_contrast_25, ]}> { e.preventDefault() control.close(() => { - // @ts-ignore :ron_swanson: "I know more than you" - navigation.navigate('SearchTab', { - screen: 'Search', - params: { - q: tag, - }, + navigation.push('Hashtag', { + tag: tag.replaceAll('#', '%23'), }) }) @@ -128,7 +128,7 @@ export function TagMenu({ See{' '} - {tag} + {displayTag} {' '} posts @@ -142,21 +142,19 @@ export function TagMenu({ { e.preventDefault() control.close(() => { - // @ts-ignore :ron_swanson: "I know more than you" - navigation.navigate('SearchTab', { - screen: 'Search', - params: { - q: - tag + - (authorHandle ? ` from:${authorHandle}` : ''), - }, + navigation.push('Hashtag', { + tag: tag.replaceAll('#', '%23'), + author: authorHandle, }) }) @@ -190,7 +188,7 @@ export function TagMenu({ See{' '} - {tag} + {displayTag} {' '} posts by this user @@ -207,8 +205,8 @@ export function TagMenu({ - - + + + - {error && ( - - - {error} - - - )} + + {error} + + + )} - - - We recommend avoiding common words that appear in many posts, since - it can result in no posts being shown. - - - + + + We recommend avoiding common words that appear in many posts, + since it can result in no posts being shown. + + + - + - - - Your muted words - + + + Your muted words + - {isPreferencesLoading ? ( - - ) : preferencesError || !preferences ? ( - - - - We're sorry, but we weren't able to load your muted words at - this time. Please try again. - - - - ) : preferences.mutedWords.length ? ( - [...preferences.mutedWords] - .reverse() - .map((word, i) => ( - - )) - ) : ( - - - You haven't muted any words or tags yet - - - )} - + {isPreferencesLoading ? ( + + ) : preferencesError || !preferences ? ( + + + + We're sorry, but we weren't able to load your muted words at + this time. Please try again. + + + + ) : preferences.mutedWords.length ? ( + [...preferences.mutedWords] + .reverse() + .map((word, i) => ( + + )) + ) : ( + + + You haven't muted any words or tags yet + + + )} + - {isNative && } + {isNative && } - + + ) } -- cgit 1.4.1 From b07846f2fa8e2830e7871c72afd5a81d2eecfe99 Mon Sep 17 00:00:00 2001 From: Hailey Date: Fri, 1 Mar 2024 17:15:45 -0800 Subject: Revert "Enable tags inside of quotes (#3041)" (#3075) This reverts commit f016cdbca9660d9e10faefae5c34c8574795419e. --- src/components/RichText.tsx | 42 ++++++++++++++++------------ src/view/com/util/post-embeds/QuoteEmbed.tsx | 5 +--- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 83498846b..1a14415cf 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -78,31 +78,40 @@ export function RichText({ const link = segment.link const mention = segment.mention const tag = segment.tag - if (mention && AppBskyRichtextFacet.validateMention(mention).success) { + if ( + mention && + AppBskyRichtextFacet.validateMention(mention).success && + !disableLinks + ) { els.push( {segment.text} , ) } else if (link && AppBskyRichtextFacet.validateLink(link).success) { - els.push( - - {toShortUrl(segment.text)} - , - ) + if (disableLinks) { + els.push(toShortUrl(segment.text)) + } else { + els.push( + + {toShortUrl(segment.text)} + , + ) + } } else if ( + !disableLinks && enableTags && tag && AppBskyRichtextFacet.validateTag(tag).success @@ -115,7 +124,6 @@ export function RichText({ style={styles} selectable={selectable} authorHandle={authorHandle} - disableLinks={disableLinks} />, ) } else { @@ -128,7 +136,7 @@ export function RichText({ @@ -143,13 +151,11 @@ function RichTextTag({ style, selectable, authorHandle, - disableLinks, }: { text: string tag: string selectable?: boolean authorHandle?: string - disableLinks?: boolean } & TextStyleProp) { const t = useTheme() const {_} = useLingui() @@ -198,7 +204,7 @@ function RichTextTag({ style={[ style, { - pointerEvents: disableLinks ? 'none' : 'auto', + pointerEvents: 'auto', color: t.palette.primary_500, }, web({ diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx index 10718fe93..35b091269 100644 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx @@ -91,10 +91,7 @@ export function QuoteEmbed({ const richText = React.useMemo( () => quote.text.trim() - ? new RichTextAPI({ - text: quote.text, - facets: quote.facets, - }) + ? new RichTextAPI({text: quote.text, facets: quote.facets}) : undefined, [quote.text, quote.facets], ) -- cgit 1.4.1 From f2249614bee2dcc82ec2b4ecdd853dc3444e626c Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 2 Mar 2024 01:34:43 +0000 Subject: Fix Profile tab switch jumps on Chrome (#3076) --- src/view/screens/Profile.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index 64e067593..b30b4491b 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -491,6 +491,8 @@ const styles = StyleSheet.create({ container: { flexDirection: 'column', height: '100%', + // @ts-ignore Web-only. + overflowAnchor: 'none', // Fixes jumps when switching tabs while scrolled down. }, loading: { paddingVertical: 10, -- cgit 1.4.1 From b70c404d4b369d6fab0dfbafd6b31390ffd20014 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 2 Mar 2024 02:40:47 +0000 Subject: Sticky desktop header (#3077) --- src/view/com/home/HomeHeader.tsx | 2 +- src/view/com/home/HomeHeaderLayout.web.tsx | 76 +++++++++++++++------------- src/view/com/home/HomeHeaderLayoutMobile.tsx | 1 + src/view/screens/Home.tsx | 3 +- 4 files changed, 43 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/view/com/home/HomeHeader.tsx b/src/view/com/home/HomeHeader.tsx index bbd16465a..aa3ecb7fc 100644 --- a/src/view/com/home/HomeHeader.tsx +++ b/src/view/com/home/HomeHeader.tsx @@ -52,7 +52,7 @@ export function HomeHeader( ) return ( - + {children} + return } else { - return {children} + return } } -function HomeHeaderLayoutTablet({children}: {children: React.ReactNode}) { +function HomeHeaderLayoutDesktopAndTablet({ + children, + tabBarAnchor, +}: { + children: React.ReactNode + tabBarAnchor: JSX.Element | null | undefined +}) { const pal = usePalette('default') - const {headerMinimalShellTransform} = useMinimalShellMode() - const {headerHeight} = useShellLayout() const {_} = useLingui() return ( - // @ts-ignore the type signature for transform wrong here, translateX and translateY need to be in separate objects -prf - { - headerHeight.value = e.nativeEvent.layout.height - }}> - - + + - } - /> + accessibilityHint=""> + + - {children} - + {tabBarAnchor} + + {children} + + ) } const styles = StyleSheet.create({ + bar: { + // @ts-ignore Web only + left: 'calc(50% - 300px)', + width: 600, + borderLeftWidth: 1, + borderRightWidth: 1, + }, topBar: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 18, - paddingVertical: 8, - marginTop: 8, - width: '100%', + paddingTop: 16, + paddingBottom: 8, }, tabBar: { // @ts-ignore Web only position: 'sticky', - zIndex: 1, - // @ts-ignore Web only -prf - left: 'calc(50% - 300px)', - width: 600, top: 0, flexDirection: 'column', alignItems: 'center', borderLeftWidth: 1, borderRightWidth: 1, + zIndex: 1, }, }) diff --git a/src/view/com/home/HomeHeaderLayoutMobile.tsx b/src/view/com/home/HomeHeaderLayoutMobile.tsx index f51efb7b4..d7b7231c6 100644 --- a/src/view/com/home/HomeHeaderLayoutMobile.tsx +++ b/src/view/com/home/HomeHeaderLayoutMobile.tsx @@ -23,6 +23,7 @@ export function HomeHeaderLayoutMobile({ children, }: { children: React.ReactNode + tabBarAnchor: JSX.Element | null | undefined }) { const pal = usePalette('default') const {_} = useLingui() diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 7ad9beb56..99ac8c44a 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -123,8 +123,7 @@ function HomeScreenReady({ return (