From 943acd16aac187ed4eb5f9cea67d6c507aab1ab5 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Mon, 19 Feb 2024 10:08:21 -0600 Subject: [PATCH 01/18] Add `selectable` to new text components (#2899) * Make new text selectable (broken) * Fixes * Fix bad conflict resolution * Remove console --- src/components/Link.tsx | 77 +++++++++++------------ src/components/RichText.tsx | 23 ++++--- src/components/Typography.tsx | 38 +++++------ src/view/screens/Storybook/Typography.tsx | 5 +- 4 files changed, 74 insertions(+), 69 deletions(-) diff --git a/src/components/Link.tsx b/src/components/Link.tsx index afd30b5ee2..85c13270ad 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -1,9 +1,5 @@ import React from 'react' -import { - GestureResponderEvent, - Linking, - TouchableWithoutFeedback, -} from 'react-native' +import {GestureResponderEvent, Linking} from 'react-native' import { useLinkProps, useNavigation, @@ -23,7 +19,7 @@ import { } from '#/lib/strings/url-helpers' import {useModalControls} from '#/state/modals' import {router} from '#/routes' -import {Text} from '#/components/Typography' +import {Text, TextProps} from '#/components/Typography' /** * Only available within a `Link`, since that inherits from `Button`. @@ -217,7 +213,7 @@ export function Link({ } export type InlineLinkProps = React.PropsWithChildren< - BaseLinkProps & TextStyleProp + BaseLinkProps & TextStyleProp & Pick > export function InlineLink({ @@ -228,6 +224,7 @@ export function InlineLink({ style, onPress: outerOnPress, download, + selectable, ...rest }: InlineLinkProps) { const t = useTheme() @@ -253,43 +250,41 @@ export function InlineLink({ const flattenedStyle = flatten(style) return ( - - - {children} - - + onBlur={onBlur} + onMouseEnter={onHoverIn} + onMouseLeave={onHoverOut} + accessibilityRole="link" + href={href} + {...web({ + hrefAttrs: { + target: download ? undefined : isExternal ? 'blank' : undefined, + rel: isExternal ? 'noopener noreferrer' : undefined, + download, + }, + dataSet: { + // default to no underline, apply this ourselves + noUnderline: '1', + }, + })}> + {children} + ) } diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 068ee99e07..8aeef9ea11 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -3,7 +3,7 @@ import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api' import {atoms as a, TextStyleProp} from '#/alf' import {InlineLink} from '#/components/Link' -import {Text} from '#/components/Typography' +import {Text, TextProps} from '#/components/Typography' import {toShortUrl} from 'lib/strings/url-helpers' import {getAgent} from '#/state/session' @@ -16,13 +16,15 @@ export function RichText({ numberOfLines, disableLinks, resolveFacets = false, -}: TextStyleProp & { - value: RichTextAPI | string - testID?: string - numberOfLines?: number - disableLinks?: boolean - resolveFacets?: boolean -}) { + selectable, +}: TextStyleProp & + Pick & { + value: RichTextAPI | string + testID?: string + numberOfLines?: number + disableLinks?: boolean + resolveFacets?: boolean + }) { const detected = React.useRef(false) const [richText, setRichText] = React.useState(() => value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), @@ -50,6 +52,7 @@ export function RichText({ if (text.length <= 5 && /^\p{Extended_Pictographic}+$/u.test(text)) { return ( + return selectable && isIOS ? ( + + ) : ( + + ) } export function createHeadingElement({level}: {level: number}) { return function HeadingElement({style, ...rest}: TextProps) { - const t = useTheme() const attr = web({ role: 'heading', 'aria-level': level, }) || {} - return ( - - ) + return } } @@ -78,21 +84,15 @@ export const H4 = createHeadingElement({level: 4}) export const H5 = createHeadingElement({level: 5}) export const H6 = createHeadingElement({level: 6}) export function P({style, ...rest}: TextProps) { - const t = useTheme() const attr = web({ role: 'paragraph', }) || {} return ( - ) } diff --git a/src/view/screens/Storybook/Typography.tsx b/src/view/screens/Storybook/Typography.tsx index 5d3a96f4d0..8ee4270b20 100644 --- a/src/view/screens/Storybook/Typography.tsx +++ b/src/view/screens/Storybook/Typography.tsx @@ -8,7 +8,9 @@ import {RichText} from '#/components/RichText' export function Typography() { return ( - atoms.text_5xl + + atoms.text_5xl + atoms.text_4xl atoms.text_3xl atoms.text_2xl @@ -24,6 +26,7 @@ export function Typography() { value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`} /> Date: Mon, 19 Feb 2024 13:30:25 -0800 Subject: [PATCH 02/18] Update IS_PROD check (#2930) * update is_prod check * remove unused constants * rename `IS_PROD` function to `IS_PROD_SERVICE` --- src/lib/constants.ts | 97 ++------------------- src/lib/strings/url-helpers.ts | 4 +- src/screens/Onboarding/StepTopicalFeeds.tsx | 12 ++- src/view/com/auth/create/state.ts | 4 +- src/view/com/auth/server-input/index.tsx | 6 +- 5 files changed, 23 insertions(+), 100 deletions(-) diff --git a/src/lib/constants.ts b/src/lib/constants.ts index aec8338d03..c8e5273d47 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -3,9 +3,8 @@ import {Insets, Platform} from 'react-native' export const LOCAL_DEV_SERVICE = Platform.OS === 'android' ? 'http://10.0.2.2:2583' : 'http://localhost:2583' export const STAGING_SERVICE = 'https://staging.bsky.dev' -export const PROD_SERVICE = 'https://bsky.social' -export const DEFAULT_SERVICE = PROD_SERVICE - +export const BSKY_SERVICE = 'https://bsky.social' +export const DEFAULT_SERVICE = BSKY_SERVICE const HELP_DESK_LANG = 'en-us' export const HELP_DESK_URL = `https://blueskyweb.zendesk.com/hc/${HELP_DESK_LANG}` @@ -36,92 +35,12 @@ export const MAX_GRAPHEME_LENGTH = 300 // but increasing limit per user feedback export const MAX_ALT_TEXT = 1000 -export function IS_LOCAL_DEV(url: string) { - return url.includes('localhost') +export function IS_PROD_SERVICE(url?: string) { + return url && url !== STAGING_SERVICE && url !== LOCAL_DEV_SERVICE } -export function IS_STAGING(url: string) { - return url.startsWith('https://staging.bsky.dev') -} - -export function IS_PROD(url: string) { - // NOTE - // until open federation, "production" is defined as the main server - // this definition will not work once federation is enabled! - // -prf - return ( - url.startsWith('https://bsky.social') || - url.startsWith('https://api.bsky.app') || - /bsky\.network\/?$/.test(url) - ) -} - -export const PROD_TEAM_HANDLES = [ - 'jay.bsky.social', - 'pfrazee.com', - 'divy.zone', - 'dholms.xyz', - 'why.bsky.world', - 'iamrosewang.bsky.social', -] -export const STAGING_TEAM_HANDLES = [ - 'arcalinea.staging.bsky.dev', - 'paul.staging.bsky.dev', - 'paul2.staging.bsky.dev', -] -export const DEV_TEAM_HANDLES = ['alice.test', 'bob.test', 'carla.test'] - -export function TEAM_HANDLES(serviceUrl: string) { - if (serviceUrl.includes('localhost')) { - return DEV_TEAM_HANDLES - } else if (serviceUrl.includes('staging')) { - return STAGING_TEAM_HANDLES - } else { - return PROD_TEAM_HANDLES - } -} - -export const STAGING_DEFAULT_FEED = (rkey: string) => - `at://did:plc:wqzurwm3kmaig6e6hnc2gqwo/app.bsky.feed.generator/${rkey}` export const PROD_DEFAULT_FEED = (rkey: string) => `at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/${rkey}` -export async function DEFAULT_FEEDS( - serviceUrl: string, - resolveHandle: (name: string) => Promise, -) { - // TODO: remove this when the test suite no longer relies on it - if (IS_LOCAL_DEV(serviceUrl)) { - // local dev - const aliceDid = await resolveHandle('alice.test') - return { - pinned: [ - `at://${aliceDid}/app.bsky.feed.generator/alice-favs`, - `at://${aliceDid}/app.bsky.feed.generator/alice-favs2`, - ], - saved: [ - `at://${aliceDid}/app.bsky.feed.generator/alice-favs`, - `at://${aliceDid}/app.bsky.feed.generator/alice-favs2`, - ], - } - } else if (IS_STAGING(serviceUrl)) { - // staging - return { - pinned: [STAGING_DEFAULT_FEED('whats-hot')], - saved: [ - STAGING_DEFAULT_FEED('bsky-team'), - STAGING_DEFAULT_FEED('with-friends'), - STAGING_DEFAULT_FEED('whats-hot'), - STAGING_DEFAULT_FEED('hot-classic'), - ], - } - } else { - // production - return { - pinned: [PROD_DEFAULT_FEED('whats-hot')], - saved: [PROD_DEFAULT_FEED('whats-hot')], - } - } -} export const POST_IMG_MAX = { width: 2000, @@ -135,13 +54,11 @@ export const STAGING_LINK_META_PROXY = export const PROD_LINK_META_PROXY = 'https://cardyb.bsky.app/v1/extract?url=' export function LINK_META_PROXY(serviceUrl: string) { - if (IS_LOCAL_DEV(serviceUrl)) { - return STAGING_LINK_META_PROXY - } else if (IS_STAGING(serviceUrl)) { - return STAGING_LINK_META_PROXY - } else { + if (IS_PROD_SERVICE(serviceUrl)) { return PROD_LINK_META_PROXY } + + return STAGING_LINK_META_PROXY } export const STATUS_PAGE_URL = 'https://status.bsky.app/' diff --git a/src/lib/strings/url-helpers.ts b/src/lib/strings/url-helpers.ts index 8a71718c8d..ef341154df 100644 --- a/src/lib/strings/url-helpers.ts +++ b/src/lib/strings/url-helpers.ts @@ -1,5 +1,5 @@ import {AtUri} from '@atproto/api' -import {PROD_SERVICE} from 'lib/constants' +import {BSKY_SERVICE} from 'lib/constants' import TLDs from 'tlds' import psl from 'psl' @@ -28,7 +28,7 @@ export function makeRecordUri( export function toNiceDomain(url: string): string { try { const urlp = new URL(url) - if (`https://${urlp.host}` === PROD_SERVICE) { + if (`https://${urlp.host}` === BSKY_SERVICE) { return 'Bluesky Social' } return urlp.host ? urlp.host : url diff --git a/src/screens/Onboarding/StepTopicalFeeds.tsx b/src/screens/Onboarding/StepTopicalFeeds.tsx index 4a2210853f..636565e340 100644 --- a/src/screens/Onboarding/StepTopicalFeeds.tsx +++ b/src/screens/Onboarding/StepTopicalFeeds.tsx @@ -3,7 +3,6 @@ import {View} from 'react-native' import {useLingui} from '@lingui/react' import {msg, Trans} from '@lingui/macro' -import {IS_PROD} from '#/env' import {atoms as a} from '#/alf' import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron' import {ListMagnifyingGlass_Stroke2_Corner0_Rounded as ListMagnifyingGlass} from '#/components/icons/ListMagnifyingGlass' @@ -22,21 +21,28 @@ import { import {FeedCard} from '#/screens/Onboarding/StepAlgoFeeds/FeedCard' import {aggregateInterestItems} from '#/screens/Onboarding/util' import {IconCircle} from '#/components/IconCircle' +import {IS_PROD_SERVICE} from 'lib/constants' +import {useSession} from 'state/session' export function StepTopicalFeeds() { const {_} = useLingui() const {track} = useAnalytics() + const {currentAccount} = useSession() const {state, dispatch, interestsDisplayNames} = React.useContext(Context) const [selectedFeedUris, setSelectedFeedUris] = React.useState([]) const [saving, setSaving] = React.useState(false) const suggestedFeedUris = React.useMemo(() => { - if (!IS_PROD) return [] + if (!IS_PROD_SERVICE(currentAccount?.service)) return [] return aggregateInterestItems( state.interestsStepResults.selectedInterests, state.interestsStepResults.apiResponse.suggestedFeedUris, state.interestsStepResults.apiResponse.suggestedFeedUris.default, ).slice(0, 10) - }, [state.interestsStepResults]) + }, [ + currentAccount?.service, + state.interestsStepResults.apiResponse.suggestedFeedUris, + state.interestsStepResults.selectedInterests, + ]) const interestsText = React.useMemo(() => { const i = state.interestsStepResults.selectedInterests.map( diff --git a/src/view/com/auth/create/state.ts b/src/view/com/auth/create/state.ts index 276eaf924c..68cfaceec5 100644 --- a/src/view/com/auth/create/state.ts +++ b/src/view/com/auth/create/state.ts @@ -12,7 +12,7 @@ import {createFullHandle} from '#/lib/strings/handles' import {cleanError} from '#/lib/strings/errors' import {useOnboardingDispatch} from '#/state/shell/onboarding' import {useSessionApi} from '#/state/session' -import {DEFAULT_SERVICE, IS_PROD} from '#/lib/constants' +import {DEFAULT_SERVICE, IS_PROD_SERVICE} from '#/lib/constants' import { DEFAULT_PROD_FEEDS, usePreferencesSetBirthDateMutation, @@ -147,7 +147,7 @@ export function useSubmitCreateAccount( : undefined, }) setBirthDate({birthDate: uiState.birthDate}) - if (IS_PROD(uiState.serviceUrl)) { + if (IS_PROD_SERVICE(uiState.serviceUrl)) { setSavedFeeds(DEFAULT_PROD_FEEDS) } } catch (e: any) { diff --git a/src/view/com/auth/server-input/index.tsx b/src/view/com/auth/server-input/index.tsx index a706219739..32b5a3141a 100644 --- a/src/view/com/auth/server-input/index.tsx +++ b/src/view/com/auth/server-input/index.tsx @@ -2,7 +2,7 @@ import React from 'react' import {View} from 'react-native' import {useLingui} from '@lingui/react' import {Trans, msg} from '@lingui/macro' -import {PROD_SERVICE} from 'lib/constants' +import {BSKY_SERVICE} from 'lib/constants' import * as persisted from '#/state/persisted' import {atoms as a, useBreakpoints, useTheme} from '#/alf' @@ -26,7 +26,7 @@ export function ServerInputDialog({ const [pdsAddressHistory, setPdsAddressHistory] = React.useState( persisted.get('pdsAddressHistory') || [], ) - const [fixedOption, setFixedOption] = React.useState([PROD_SERVICE]) + const [fixedOption, setFixedOption] = React.useState([BSKY_SERVICE]) const [customAddress, setCustomAddress] = React.useState('') const onClose = React.useCallback(() => { @@ -86,7 +86,7 @@ export function ServerInputDialog({ label="Preferences" values={fixedOption} onChange={setFixedOption}> - + {_(msg`Bluesky`)} Date: Mon, 19 Feb 2024 14:44:09 -0800 Subject: [PATCH 03/18] Add embed to post view when converting from embed view (#2929) --- src/state/queries/util.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/state/queries/util.ts b/src/state/queries/util.ts index f3a87ae5db..54752b332a 100644 --- a/src/state/queries/util.ts +++ b/src/state/queries/util.ts @@ -53,5 +53,6 @@ export function embedViewRecordToPostView( record: v.value, indexedAt: v.indexedAt, labels: v.labels, + embed: v.embeds?.[0], } } From da62a77f05258ce2b0609248cb5677c2406a4e63 Mon Sep 17 00:00:00 2001 From: Hailey Date: Mon, 19 Feb 2024 16:15:42 -0800 Subject: [PATCH 04/18] Prefer post over quote when finding cached post (#2935) --- src/state/queries/post-feed.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/state/queries/post-feed.ts b/src/state/queries/post-feed.ts index 3200090897..5035065a0a 100644 --- a/src/state/queries/post-feed.ts +++ b/src/state/queries/post-feed.ts @@ -391,6 +391,9 @@ export function* findAllPostsInQueryData( >({ queryKey: ['post-feed'], }) + + let foundEmbed: AppBskyFeedDefs.PostView | undefined + for (const [_queryKey, queryData] of queryDatas) { if (!queryData?.pages) { continue @@ -402,7 +405,7 @@ export function* findAllPostsInQueryData( } const quotedPost = getEmbeddedPost(item.post.embed) if (quotedPost?.uri === uri) { - yield embedViewRecordToPostView(quotedPost) + foundEmbed = embedViewRecordToPostView(quotedPost) } if ( AppBskyFeedDefs.isPostView(item.reply?.parent) && @@ -419,6 +422,10 @@ export function* findAllPostsInQueryData( } } } + + if (foundEmbed) { + yield foundEmbed + } } function assertSomePostsPassModeration(feed: AppBskyFeedDefs.FeedViewPost[]) { From b52a742925cff4429885e94815d61f3f7cfb5a66 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Mon, 19 Feb 2024 18:18:13 -0600 Subject: [PATCH 05/18] Improve dialogs (#2933) * Improve dialogs * Remove comment, revert storybook * Hacky fix * Comments --- src/components/Dialog/index.tsx | 121 ++++++++++++++----------- src/components/Dialog/index.web.tsx | 57 ++++++------ src/components/Dialog/types.ts | 21 +++-- src/components/Prompt.tsx | 2 +- src/components/icons/Times.tsx | 5 + src/view/screens/Storybook/Dialogs.tsx | 2 +- 6 files changed, 123 insertions(+), 85 deletions(-) create mode 100644 src/components/icons/Times.tsx diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index 9132e68de5..f30d24e6ac 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -8,7 +8,7 @@ import BottomSheet, { } from '@gorhom/bottom-sheet' import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {useTheme, atoms as a} from '#/alf' +import {useTheme, atoms as a, flatten} from '#/alf' import {Portal} from '#/components/Portal' import {createInput} from '#/components/forms/TextField' @@ -36,9 +36,23 @@ export function Outer({ const hasSnapPoints = !!sheetOptions.snapPoints const insets = useSafeAreaInsets() - const open = React.useCallback((i = 0) => { - sheet.current?.snapToIndex(i) - }, []) + /* + * Used to manage open/closed, but index is otherwise handled internally by `BottomSheet` + */ + const [openIndex, setOpenIndex] = React.useState(-1) + + /* + * `openIndex` is the index of the snap point to open the bottom sheet to. If >0, the bottom sheet is open. + */ + const isOpen = openIndex > -1 + + const open = React.useCallback( + ({index} = {}) => { + // can be set to any index of `snapPoints`, but `0` is the first i.e. "open" + setOpenIndex(index || 0) + }, + [setOpenIndex], + ) const close = React.useCallback(() => { sheet.current?.close() @@ -57,77 +71,80 @@ export function Outer({ (index: number) => { if (index === -1) { onClose?.() + setOpenIndex(-1) } }, - [onClose], + [onClose, setOpenIndex], ) const context = React.useMemo(() => ({close}), [close]) return ( - - ( - - )} - handleIndicatorStyle={{backgroundColor: t.palette.primary_500}} - handleStyle={{display: 'none'}} - onChange={onChange}> - - - {children} - - - + isOpen && ( + + ( + + )} + handleIndicatorStyle={{backgroundColor: t.palette.primary_500}} + handleStyle={{display: 'none'}} + onChange={onChange}> + + + {hasSnapPoints ? children : {children}} + + + + ) ) } -// TODO a11y props here, or is that handled by the sheet? -export function Inner(props: DialogInnerProps) { +export function Inner({children, style}: DialogInnerProps) { const insets = useSafeAreaInsets() return ( - {props.children} + {children} ) } -export function ScrollableInner(props: DialogInnerProps) { +export function ScrollableInner({children, style}: DialogInnerProps) { const insets = useSafeAreaInsets() return ( - {props.children} + {children} ) diff --git a/src/components/Dialog/index.web.tsx b/src/components/Dialog/index.web.tsx index 305c00e97a..79441fb5e1 100644 --- a/src/components/Dialog/index.web.tsx +++ b/src/components/Dialog/index.web.tsx @@ -5,11 +5,13 @@ import Animated, {FadeInDown, FadeIn} from 'react-native-reanimated' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useTheme, atoms as a, useBreakpoints, web} from '#/alf' +import {useTheme, atoms as a, useBreakpoints, web, flatten} from '#/alf' import {Portal} from '#/components/Portal' import {DialogOuterProps, DialogInnerProps} from '#/components/Dialog/types' import {Context} from '#/components/Dialog/context' +import {Button, ButtonIcon} from '#/components/Button' +import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' export {useDialogControl, useDialogContext} from '#/components/Dialog/context' export * from '#/components/Dialog/types' @@ -18,9 +20,9 @@ export {Input} from '#/components/forms/TextField' const stopPropagation = (e: any) => e.stopPropagation() export function Outer({ + children, control, onClose, - children, }: React.PropsWithChildren) { const {_} = useLingui() const t = useTheme() @@ -147,7 +149,7 @@ export function Inner({ a.rounded_md, a.w_full, a.border, - gtMobile ? a.p_xl : a.p_lg, + gtMobile ? a.p_2xl : a.p_xl, t.atoms.bg, { maxWidth: 600, @@ -156,7 +158,7 @@ export function Inner({ shadowOpacity: t.name === 'light' ? 0.1 : 0.4, shadowRadius: 30, }, - ...(Array.isArray(style) ? style : [style || {}]), + flatten(style), ]}> {children} @@ -170,25 +172,28 @@ export function Handle() { return null } -/** - * TODO(eric) unused rn - */ -// export function Close() { -// const {_} = useLingui() -// const t = useTheme() -// const {close} = useDialogContext() -// return ( -// -// -// -// ) -// } +export function Close() { + const {_} = useLingui() + const {close} = React.useContext(Context) + return ( + + + + ) +} diff --git a/src/components/Dialog/types.ts b/src/components/Dialog/types.ts index d36784183c..00178926a0 100644 --- a/src/components/Dialog/types.ts +++ b/src/components/Dialog/types.ts @@ -1,15 +1,27 @@ import React from 'react' -import type {ViewStyle, AccessibilityProps} from 'react-native' +import type {AccessibilityProps} from 'react-native' import {BottomSheetProps} from '@gorhom/bottom-sheet' +import {ViewStyleProp} from '#/alf' + type A11yProps = Required export type DialogContextProps = { close: () => void } +export type DialogControlOpenOptions = { + /** + * NATIVE ONLY + * + * Optional index of the snap point to open the bottom sheet to. Defaults to + * 0, which is the first snap point (i.e. "open"). + */ + index?: number +} + export type DialogControlProps = { - open: (index?: number) => void + open: (options?: DialogControlOpenOptions) => void close: () => void } @@ -26,10 +38,7 @@ export type DialogOuterProps = { webOptions?: {} } -type DialogInnerPropsBase = React.PropsWithChildren<{ - style?: ViewStyle -}> & - T +type DialogInnerPropsBase = React.PropsWithChildren & T export type DialogInnerProps = | DialogInnerPropsBase<{ label?: undefined diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx index 2c79d27cf1..411679102b 100644 --- a/src/components/Prompt.tsx +++ b/src/components/Prompt.tsx @@ -41,7 +41,7 @@ export function Outer({ + style={[{width: 'auto', maxWidth: 400}]}> {children} diff --git a/src/components/icons/Times.tsx b/src/components/icons/Times.tsx new file mode 100644 index 0000000000..678ac3fcb3 --- /dev/null +++ b/src/components/icons/Times.tsx @@ -0,0 +1,5 @@ +import {createSinglePathSVG} from './TEMPLATE' + +export const TimesLarge_Stroke2_Corner0_Rounded = createSinglePathSVG({ + path: 'M4.293 4.293a1 1 0 0 1 1.414 0L12 10.586l6.293-6.293a1 1 0 1 1 1.414 1.414L13.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414L12 13.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L10.586 12 4.293 5.707a1 1 0 0 1 0-1.414Z', +}) diff --git a/src/view/screens/Storybook/Dialogs.tsx b/src/view/screens/Storybook/Dialogs.tsx index db568c6bd5..a18b3c62f8 100644 --- a/src/view/screens/Storybook/Dialogs.tsx +++ b/src/view/screens/Storybook/Dialogs.tsx @@ -50,7 +50,7 @@ export function Dialogs() { + nativeOptions={{sheet: {snapPoints: ['100%']}}}> Date: Tue, 20 Feb 2024 10:04:07 -0600 Subject: [PATCH 06/18] Improve dialogs issue (#2941) * Fix collapse, update backdrop color * Remove test prop * Remove debug code --- src/components/Dialog/index.tsx | 4 ++- src/view/screens/Storybook/Dialogs.tsx | 43 +++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx index f30d24e6ac..5295350512 100644 --- a/src/components/Dialog/index.tsx +++ b/src/components/Dialog/index.tsx @@ -90,6 +90,7 @@ export function Outer({ keyboardBlurBehavior="restore" topInset={insets.top} {...sheetOptions} + snapPoints={sheetOptions.snapPoints || ['100%']} ref={sheet} index={openIndex} backgroundStyle={{backgroundColor: 'transparent'}} @@ -99,6 +100,7 @@ export function Outer({ appearsOnIndex={0} disappearsOnIndex={-1} {...props} + style={[flatten(props.style), t.atoms.bg_contrast_300]} /> )} handleIndicatorStyle={{backgroundColor: t.palette.primary_500}} @@ -117,7 +119,7 @@ export function Outer({ }, ]} /> - {hasSnapPoints ? children : {children}} + {children} diff --git a/src/view/screens/Storybook/Dialogs.tsx b/src/view/screens/Storybook/Dialogs.tsx index a18b3c62f8..821ac3c886 100644 --- a/src/view/screens/Storybook/Dialogs.tsx +++ b/src/view/screens/Storybook/Dialogs.tsx @@ -9,7 +9,8 @@ import * as Prompt from '#/components/Prompt' import {useDialogStateControlContext} from '#/state/dialogs' export function Dialogs() { - const control = Dialog.useDialogControl() + const scrollable = Dialog.useDialogControl() + const basic = Dialog.useDialogControl() const prompt = Prompt.usePromptControl() const {closeAllDialogs} = useDialogStateControlContext() @@ -20,8 +21,31 @@ export function Dialogs() { color="secondary" size="small" onPress={() => { - control.open() + scrollable.open() prompt.open() + basic.open() + }} + label="Open basic dialog"> + Open all dialogs + + + + + From df5a8f15427f8efeded692e21585e48df4907c3a Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 20 Feb 2024 11:03:04 -0600 Subject: [PATCH 07/18] Swap `RichText` (#2934) * Switch to new RT * Allow early exit from links * Build in tracking to text atoms * Clean up FeedSourceCard * Clean up leading after new default * Add deprecated notice --- src/alf/atoms.ts | 18 +++++++++++++++++- src/components/Link.tsx | 9 ++++++--- src/components/RichText.tsx | 4 ++-- src/view/com/feeds/FeedSourceCard.tsx | 8 +++++--- src/view/com/lists/ListCard.tsx | 7 ++++--- src/view/com/post-thread/PostThreadItem.tsx | 15 ++++++--------- src/view/com/posts/FeedItem.tsx | 9 ++++----- src/view/com/profile/ProfileHeader.tsx | 9 +++++---- src/view/com/util/post-embeds/QuoteEmbed.tsx | 10 +++++----- src/view/com/util/text/RichText.tsx | 3 +++ src/view/screens/ProfileFeed.tsx | 8 ++++---- src/view/screens/ProfileList.tsx | 8 ++++---- 12 files changed, 65 insertions(+), 43 deletions(-) diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index f75e8ffe0b..18f492d6e7 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -176,43 +176,59 @@ export const atoms = { }, text_2xs: { fontSize: tokens.fontSize._2xs, + letterSpacing: 0.25, }, text_xs: { fontSize: tokens.fontSize.xs, + letterSpacing: 0.25, }, text_sm: { fontSize: tokens.fontSize.sm, + letterSpacing: 0.25, }, text_md: { fontSize: tokens.fontSize.md, + letterSpacing: 0.25, }, text_lg: { fontSize: tokens.fontSize.lg, + letterSpacing: 0.25, }, text_xl: { fontSize: tokens.fontSize.xl, + letterSpacing: 0.25, }, text_2xl: { fontSize: tokens.fontSize._2xl, + letterSpacing: 0.25, }, text_3xl: { fontSize: tokens.fontSize._3xl, + letterSpacing: 0.25, }, text_4xl: { fontSize: tokens.fontSize._4xl, + letterSpacing: 0.25, }, text_5xl: { fontSize: tokens.fontSize._5xl, + letterSpacing: 0.25, }, leading_tight: { lineHeight: 1.15, }, leading_snug: { - lineHeight: 1.25, + lineHeight: 1.3, }, leading_normal: { lineHeight: 1.5, }, + tracking_normal: { + letterSpacing: 0, + }, + tracking_wide: { + letterSpacing: 0.25, + }, font_normal: { fontWeight: tokens.fontWeight.normal, }, diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 85c13270ad..593b0863a7 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -51,11 +51,12 @@ type BaseLinkProps = Pick< warnOnMismatchingTextChild?: boolean /** - * Callback for when the link is pressed. + * Callback for when the link is pressed. Prevent default and return `false` + * to exit early and prevent navigation. * * DO NOT use this for navigation, that's what the `to` prop is for. */ - onPress?: (e: GestureResponderEvent) => void + onPress?: (e: GestureResponderEvent) => void | false /** * Web-only attribute. Sets `download` attr on web. @@ -82,7 +83,9 @@ export function useLink({ const onPress = React.useCallback( (e: GestureResponderEvent) => { - outerOnPress?.(e) + const exitEarlyIfFalse = outerOnPress?.(e) + + if (exitEarlyIfFalse === false) return const requiresWarning = Boolean( warnOnMismatchingTextChild && diff --git a/src/components/RichText.tsx b/src/components/RichText.tsx index 8aeef9ea11..c72fcabdd8 100644 --- a/src/components/RichText.tsx +++ b/src/components/RichText.tsx @@ -1,7 +1,7 @@ import React from 'react' import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api' -import {atoms as a, TextStyleProp} from '#/alf' +import {atoms as a, TextStyleProp, flatten} from '#/alf' import {InlineLink} from '#/components/Link' import {Text, TextProps} from '#/components/Typography' import {toShortUrl} from 'lib/strings/url-helpers' @@ -29,7 +29,7 @@ export function RichText({ const [richText, setRichText] = React.useState(() => value instanceof RichTextAPI ? value : new RichTextAPI({text: value}), ) - const styles = [a.leading_normal, style] + const styles = [a.leading_snug, flatten(style)] React.useEffect(() => { if (!resolveFacets) return diff --git a/src/view/com/feeds/FeedSourceCard.tsx b/src/view/com/feeds/FeedSourceCard.tsx index 0de88b248d..9bd7238df2 100644 --- a/src/view/com/feeds/FeedSourceCard.tsx +++ b/src/view/com/feeds/FeedSourceCard.tsx @@ -2,7 +2,7 @@ import React from 'react' import {Pressable, StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {Text} from '../util/text/Text' -import {RichText} from '../util/text/RichText' +import {RichText} from '#/components/RichText' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {UserAvatar} from '../util/UserAvatar' @@ -25,6 +25,7 @@ import { } from '#/state/queries/preferences' import {useFeedSourceInfoQuery, FeedSourceInfo} from '#/state/queries/feed' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {useTheme} from '#/alf' export function FeedSourceCard({ feedUri, @@ -82,6 +83,7 @@ export function FeedSourceCardLoaded({ pinOnSave?: boolean showMinimalPlaceholder?: boolean }) { + const t = useTheme() const pal = usePalette('default') const {_} = useLingui() const navigation = useNavigation() @@ -266,8 +268,8 @@ export function FeedSourceCardLoaded({ {showDescription && feed.description ? ( ) : null} diff --git a/src/view/com/lists/ListCard.tsx b/src/view/com/lists/ListCard.tsx index 5750faec19..19842eb54d 100644 --- a/src/view/com/lists/ListCard.tsx +++ b/src/view/com/lists/ListCard.tsx @@ -3,7 +3,7 @@ import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' import {AtUri, AppBskyGraphDefs, RichText} from '@atproto/api' import {Link} from '../util/Link' import {Text} from '../util/text/Text' -import {RichText as RichTextCom} from '../util/text/RichText' +import {RichText as RichTextCom} from '#/components/RichText' import {UserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' @@ -12,6 +12,7 @@ import {sanitizeDisplayName} from 'lib/strings/display-names' import {sanitizeHandle} from 'lib/strings/handles' import {makeProfileLink} from 'lib/routes/links' import {Trans} from '@lingui/macro' +import {atoms as a} from '#/alf' export const ListCard = ({ testID, @@ -119,9 +120,9 @@ export const ListCard = ({ {descriptionRichText ? ( ) : undefined} diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index f9bdaebedf..2ef1b1447b 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -11,7 +11,7 @@ import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn' import {Link, TextLink} from '../util/Link' -import {RichText} from '../util/text/RichText' +import {RichText} from '#/components/RichText' import {Text} from '../util/text/Text' import {PreviewableUserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' @@ -44,6 +44,7 @@ import {ThreadPost} from '#/state/queries/post-thread' import {useSession} from 'state/session' import {WhoCanReply} from '../threadgate/WhoCanReply' import {LoadingPlaceholder} from '../util/LoadingPlaceholder' +import {atoms as a} from '#/alf' export function PostThreadItem({ post, @@ -326,10 +327,8 @@ let PostThreadItemLoaded = ({ styles.postTextLargeContainer, ]}> @@ -522,10 +521,8 @@ let PostThreadItemLoaded = ({ {richText?.text ? ( diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx index 8d0f2bef26..6f64de181c 100644 --- a/src/view/com/posts/FeedItem.tsx +++ b/src/view/com/posts/FeedItem.tsx @@ -20,7 +20,7 @@ import {PostCtrls} from '../util/post-ctrls/PostCtrls' import {PostEmbeds} from '../util/post-embeds' import {ContentHider} from '../util/moderation/ContentHider' import {PostAlerts} from '../util/moderation/PostAlerts' -import {RichText} from '../util/text/RichText' +import {RichText} from '#/components/RichText' import {PreviewableUserAvatar} from '../util/UserAvatar' import {s} from 'lib/styles' import {usePalette} from 'lib/hooks/usePalette' @@ -36,6 +36,7 @@ import {FeedNameText} from '../util/FeedInfoText' import {useSession} from '#/state/session' import {Trans, msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {atoms as a} from '#/alf' export function FeedItem({ post, @@ -347,11 +348,9 @@ let PostContent = ({ ) : undefined} diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx index 8fd50fad6c..3e479d7b53 100644 --- a/src/view/com/profile/ProfileHeader.tsx +++ b/src/view/com/profile/ProfileHeader.tsx @@ -23,7 +23,7 @@ import * as Toast from '../util/Toast' import {LoadingPlaceholder} from '../util/LoadingPlaceholder' import {Text} from '../util/text/Text' import {ThemedText} from '../util/text/ThemedText' -import {RichText} from '../util/text/RichText' +import {RichText} from '#/components/RichText' import {UserAvatar} from '../util/UserAvatar' import {UserBanner} from '../util/UserBanner' import {ProfileHeaderAlerts} from '../util/moderation/ProfileHeaderAlerts' @@ -56,6 +56,7 @@ import {Shadow} from '#/state/cache/types' import {useRequireAuth} from '#/state/session' import {LabelInfo} from '../util/moderation/LabelInfo' import {useProfileShadow} from 'state/cache/profile-shadow' +import {atoms as a} from '#/alf' let ProfileHeaderLoading = (_props: {}): React.ReactNode => { const pal = usePalette('default') @@ -608,12 +609,12 @@ let ProfileHeader = ({ {descriptionRT && !moderation.profile.blur ? ( - + ) : undefined} diff --git a/src/view/com/util/post-embeds/QuoteEmbed.tsx b/src/view/com/util/post-embeds/QuoteEmbed.tsx index d9d84feb45..c128a6f001 100644 --- a/src/view/com/util/post-embeds/QuoteEmbed.tsx +++ b/src/view/com/util/post-embeds/QuoteEmbed.tsx @@ -20,7 +20,8 @@ import {PostAlerts} from '../moderation/PostAlerts' import {makeProfileLink} from 'lib/routes/links' import {InfoCircleIcon} from 'lib/icons' import {Trans} from '@lingui/macro' -import {RichText} from 'view/com/util/text/RichText' +import {RichText} from '#/components/RichText' +import {atoms as a} from '#/alf' export function MaybeQuoteEmbed({ embed, @@ -127,11 +128,10 @@ export function QuoteEmbed({ ) : null} {richText ? ( ) : null} {embed && } diff --git a/src/view/com/util/text/RichText.tsx b/src/view/com/util/text/RichText.tsx index e910127fec..b6d461224c 100644 --- a/src/view/com/util/text/RichText.tsx +++ b/src/view/com/util/text/RichText.tsx @@ -10,6 +10,9 @@ import {usePalette} from 'lib/hooks/usePalette' const WORD_WRAP = {wordWrap: 1} +/** + * @deprecated use `#/components/RichText` + */ export function RichText({ testID, type = 'md', diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx index be9eec8165..212c10e745 100644 --- a/src/view/screens/ProfileFeed.tsx +++ b/src/view/screens/ProfileFeed.tsx @@ -17,7 +17,7 @@ import {TextLink} from 'view/com/util/Link' import {ListRef} from 'view/com/util/List' import {Button} from 'view/com/util/forms/Button' import {Text} from 'view/com/util/text/Text' -import {RichText} from 'view/com/util/text/RichText' +import {RichText} from '#/components/RichText' import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' import {FAB} from 'view/com/util/fab/FAB' import {EmptyState} from 'view/com/util/EmptyState' @@ -59,6 +59,7 @@ import {useComposerControls} from '#/state/shell/composer' import {truncateAndInvalidate} from '#/state/queries/util' import {isNative} from '#/platform/detection' import {listenSoftReset} from '#/state/events' +import {atoms as a} from '#/alf' const SECTION_TITLES = ['Posts', 'About'] @@ -575,9 +576,8 @@ function AboutSection({ {feedInfo.description ? ( ) : ( diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 7964648836..d86b569e25 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -14,7 +14,7 @@ import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown' import {CenteredView} from 'view/com/util/Views' import {EmptyState} from 'view/com/util/EmptyState' import {LoadingScreen} from 'view/com/util/LoadingScreen' -import {RichText} from 'view/com/util/text/RichText' +import {RichText} from '#/components/RichText' import {Button} from 'view/com/util/forms/Button' import {TextLink} from 'view/com/util/Link' import {ListRef} from 'view/com/util/List' @@ -60,6 +60,7 @@ import { import {logger} from '#/logger' import {useAnalytics} from '#/lib/analytics/analytics' import {listenSoftReset} from '#/state/events' +import {atoms as a} from '#/alf' const SECTION_TITLES_CURATE = ['Posts', 'About'] const SECTION_TITLES_MOD = ['About'] @@ -742,9 +743,8 @@ const AboutSection = React.forwardRef( {descriptionRT ? ( ) : ( Date: Wed, 21 Feb 2024 03:09:02 +0900 Subject: [PATCH 08/18] Remove redundant `` tag (#2938) --- bskyweb/templates/base.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index 99b6f1dc67..e29e4032bb 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -2,7 +2,6 @@ -