diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index a2d81fc96a..198e3baa76 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -215,6 +215,7 @@ + {% block html_head_extra -%}{%- endblock %} diff --git a/package.json b/package.json index 837a8d0e45..4a3a2a7dc1 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "js-sha256": "^0.9.0", "jwt-decode": "^4.0.0", "lande": "^1.0.10", - "libphonenumber-js": "^1.10.53", "lodash.chunk": "^4.2.0", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", @@ -137,7 +136,7 @@ "mobx": "^6.6.1", "mobx-react-lite": "^3.4.0", "mobx-utils": "^6.0.6", - "nanoid": "^5.0.2", + "nanoid": "^5.0.5", "normalize-url": "^8.0.0", "patch-package": "^6.5.1", "postinstall-postinstall": "^2.1.0", @@ -164,6 +163,7 @@ "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", "react-native-svg": "14.1.0", + "react-native-ui-text-view": "link:./modules/react-native-ui-text-view", "react-native-url-polyfill": "^1.3.0", "react-native-uuid": "^2.0.1", "react-native-version-number": "^0.3.6", @@ -178,8 +178,7 @@ "tlds": "^1.234.0", "use-deep-compare": "^1.1.0", "zeego": "^1.6.2", - "zod": "^3.20.2", - "react-native-ui-text-view": "link:./modules/react-native-ui-text-view" + "zod": "^3.20.2" }, "devDependencies": { "@atproto/dev-env": "^0.2.28", diff --git a/src/Splash.tsx b/src/Splash.tsx index 80d0a66e75..b2381f9232 100644 --- a/src/Splash.tsx +++ b/src/Splash.tsx @@ -21,7 +21,6 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context' import Svg, {Path, SvgProps} from 'react-native-svg' import {isAndroid} from '#/platform/detection' -import {useThemePrefs} from 'state/shell' import {Logotype} from '#/view/icons/Logotype' // @ts-ignore @@ -75,10 +74,8 @@ export function Splash(props: React.PropsWithChildren) { isLayoutReady && reduceMotion !== undefined - const {colorMode} = useThemePrefs() const colorScheme = useColorScheme() - const themeName = colorMode === 'system' ? colorScheme : colorMode - const isDarkMode = themeName === 'dark' + const isDarkMode = colorScheme === 'dark' const logoAnimation = useAnimatedStyle(() => { return { @@ -263,7 +260,12 @@ export function Splash(props: React.PropsWithChildren) { )} diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index bbf7e32431..f75e8ffe0b 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -122,6 +122,9 @@ export const atoms = { flex_shrink: { flexShrink: 1, }, + justify_start: { + justifyContent: 'flex-start', + }, justify_center: { justifyContent: 'center', }, @@ -140,10 +143,31 @@ export const atoms = { align_end: { alignItems: 'flex-end', }, + self_auto: { + alignSelf: 'auto', + }, + self_start: { + alignSelf: 'flex-start', + }, + self_end: { + alignSelf: 'flex-end', + }, + self_center: { + alignSelf: 'center', + }, + self_stretch: { + alignSelf: 'stretch', + }, + self_baseline: { + alignSelf: 'baseline', + }, /* * Text */ + text_left: { + textAlign: 'left', + }, text_center: { textAlign: 'center', }, @@ -195,10 +219,16 @@ export const atoms = { font_bold: { fontWeight: tokens.fontWeight.semibold, }, + italic: { + fontStyle: 'italic', + }, /* * Border */ + border_0: { + borderWidth: 0, + }, border: { borderWidth: 1, }, @@ -208,6 +238,12 @@ export const atoms = { border_b: { borderBottomWidth: 1, }, + border_l: { + borderLeftWidth: 1, + }, + border_r: { + borderRightWidth: 1, + }, /* * Shadow diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 68cee43745..e401bda2a5 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -27,7 +27,7 @@ export type ButtonColor = | 'gradient_sunset' | 'gradient_nordic' | 'gradient_bonfire' -export type ButtonSize = 'small' | 'large' +export type ButtonSize = 'tiny' | 'small' | 'large' export type ButtonShape = 'round' | 'square' | 'default' export type VariantProps = { /** @@ -48,25 +48,32 @@ export type VariantProps = { shape?: ButtonShape } -export type ButtonProps = React.PropsWithChildren< - Pick & - AccessibilityProps & - VariantProps & { - testID?: string - label: string - style?: StyleProp - } -> +export type ButtonState = { + hovered: boolean + focused: boolean + pressed: boolean + disabled: boolean +} + +export type ButtonContext = VariantProps & ButtonState + +export type ButtonProps = Pick< + PressableProps, + 'disabled' | 'onPress' | 'testID' +> & + AccessibilityProps & + VariantProps & { + testID?: string + label: string + style?: StyleProp + children: + | React.ReactNode + | string + | ((context: ButtonContext) => React.ReactNode | string) + } export type ButtonTextProps = TextProps & VariantProps & {disabled?: boolean} -const Context = React.createContext< - VariantProps & { - hovered: boolean - focused: boolean - pressed: boolean - disabled: boolean - } ->({ +const Context = React.createContext({ hovered: false, focused: false, pressed: false, @@ -277,6 +284,8 @@ export function Button({ baseStyles.push({paddingVertical: 15}, a.px_2xl, a.rounded_sm, a.gap_md) } else if (size === 'small') { baseStyles.push({paddingVertical: 9}, a.px_lg, a.rounded_sm, a.gap_sm) + } else if (size === 'tiny') { + baseStyles.push({paddingVertical: 4}, a.px_sm, a.rounded_xs, a.gap_xs) } } else if (shape === 'round' || shape === 'square') { if (size === 'large') { @@ -287,12 +296,18 @@ export function Button({ } } else if (size === 'small') { baseStyles.push({height: 40, width: 40}) + } else if (size === 'tiny') { + baseStyles.push({height: 20, width: 20}) } if (shape === 'round') { baseStyles.push(a.rounded_full) } else if (shape === 'square') { - baseStyles.push(a.rounded_sm) + if (size === 'tiny') { + baseStyles.push(a.rounded_xs) + } else { + baseStyles.push(a.rounded_sm) + } } } @@ -338,7 +353,7 @@ export function Button({ } }, [variant, color]) - const context = React.useMemo( + const context = React.useMemo( () => ({ ...state, variant, @@ -349,6 +364,8 @@ export function Button({ [state, variant, color, size, disabled], ) + const flattenedBaseStyles = flatten(baseStyles) + return ( {variant === 'gradient' && ( - + + + )} {typeof children === 'string' ? ( {children} + ) : typeof children === 'function' ? ( + children(context) ) : ( children )} @@ -493,6 +519,8 @@ export function useSharedButtonTextStyles() { if (size === 'large') { baseStyles.push(a.text_md, android({paddingBottom: 1})) + } else if (size === 'tiny') { + baseStyles.push(a.text_xs, android({paddingBottom: 1})) } else { baseStyles.push(a.text_sm, android({paddingBottom: 1})) } @@ -514,9 +542,11 @@ export function ButtonText({children, style, ...rest}: ButtonTextProps) { export function ButtonIcon({ icon: Comp, position, + size: iconSize, }: { icon: React.ComponentType position?: 'left' | 'right' + size?: SVGIconProps['size'] }) { const {size, disabled} = useButtonContext() const textStyles = useSharedButtonTextStyles() @@ -532,7 +562,9 @@ export function ButtonIcon({ }, ]}> diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 763f07ca93..afd30b5ee2 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -13,7 +13,7 @@ import {sanitizeUrl} from '@braintree/sanitize-url' import {useInteractionState} from '#/components/hooks/useInteractionState' import {isWeb} from '#/platform/detection' -import {useTheme, web, flatten, TextStyleProp} from '#/alf' +import {useTheme, web, flatten, TextStyleProp, atoms as a} from '#/alf' import {Button, ButtonProps} from '#/components/Button' import {AllNavigatorParams, NavigationProp} from '#/lib/routes/types' import { @@ -35,6 +35,13 @@ type BaseLinkProps = Pick< Parameters>[0], 'to' > & { + testID?: string + + /** + * Label for a11y. Defaults to the href. + */ + label?: string + /** * The React Navigation `StackAction` to perform when the link is pressed. */ @@ -46,6 +53,18 @@ type BaseLinkProps = Pick< * Note: atm this only works for `InlineLink`s with a string child. */ warnOnMismatchingTextChild?: boolean + + /** + * Callback for when the link is pressed. + * + * DO NOT use this for navigation, that's what the `to` prop is for. + */ + onPress?: (e: GestureResponderEvent) => void + + /** + * Web-only attribute. Sets `download` attr on web. + */ + download?: string } export function useLink({ @@ -53,6 +72,7 @@ export function useLink({ displayText, action = 'push', warnOnMismatchingTextChild, + onPress: outerOnPress, }: BaseLinkProps & { displayText: string }) { @@ -66,6 +86,8 @@ export function useLink({ const onPress = React.useCallback( (e: GestureResponderEvent) => { + outerOnPress?.(e) + const requiresWarning = Boolean( warnOnMismatchingTextChild && displayText && @@ -132,6 +154,7 @@ export function useLink({ displayText, closeModal, openModal, + outerOnPress, ], ) @@ -143,16 +166,7 @@ export function useLink({ } export type LinkProps = Omit & - Omit & { - /** - * Label for a11y. Defaults to the href. - */ - label?: string - /** - * Web-only attribute. Sets `download` attr on web. - */ - download?: string - } + Omit /** * A interactive element that renders as a `` tag on the web. On mobile it @@ -166,6 +180,7 @@ export function Link({ children, to, action = 'push', + onPress: outerOnPress, download, ...rest }: LinkProps) { @@ -173,24 +188,26 @@ export function Link({ to, displayText: typeof children === 'string' ? children : '', action, + onPress: outerOnPress, }) return (