An ATproto social media client -- with an independent Appview.

refactor!: remove deprecated `usePalette` hook

removes the already-deprecated `usePalette` hook from the codebase.

authored by

serenity and committed by
GitHub
61535897 ee11b4fa

+1634 -592
+3
.editorconfig
··· 1 [*.{kt,kts}] 2 indent_size=2
··· 1 [*.{kt,kts}] 2 indent_size=2 3 + 4 + [*.{ts,tsx}] 5 + indent_size=2
+28
src/alf/catppuccin/palette.ts
···
··· 1 + export const mocha = { 2 + crust: '#11111b', 3 + mantle: '#181825', 4 + base: '#1e1e2e', 5 + surface0: '#313244', 6 + surface1: '#45475a', 7 + surface2: '#585b70', 8 + overlay0: '#6c7086', 9 + overlay1: '#7f849c', 10 + overlay2: '#9399b2', 11 + subtext0: '#a6adc8', 12 + subtext1: '#bac2de', 13 + text: '#cdd6f4', 14 + lavender: '#b4befe', 15 + blue: '#89b4fa', 16 + sapphire: '#74c7ec', 17 + sky: '#89dceb', 18 + teal: '#94e2d5', 19 + green: '#a6e3a1', 20 + yellow: '#f9e2af', 21 + peach: '#fab387', 22 + maroon: '#eba0ac', 23 + red: '#f38ba8', 24 + mauve: '#cba6f7', 25 + pink: '#f5c2e7', 26 + flamingo: '#f2cdcd', 27 + rosewater: '#f5e0dc', 28 + }
+223
src/alf/util/colors/conversion.ts
···
··· 1 + import {type HexCode, type HslColor, type RgbColor} from '#/alf/util/colors' 2 + 3 + /** 4 + * Converts a hexcode string in the format `"#RRGGBB"` to a `HslColor` object (`{ h: number, s: number, l: number, a: number}`). 5 + * @param {HexCode} hex - A hexcode string in the format `#RRGGBB`. The leading "#" symbol is optional. 6 + * @returns{HslColor} A HSL colour object. 7 + */ 8 + export const hexToHsl = (hex: HexCode): HslColor => { 9 + hex = hex.replace('#', '') 10 + 11 + const r = parseInt(hex.substring(0, 2), 16) / 255 12 + const g = parseInt(hex.substring(2, 4), 16) / 255 13 + const b = parseInt(hex.substring(4, 6), 16) / 255 14 + const a = hex.length > 6 ? parseInt(hex.substring(6, 8), 16) / 255 : undefined 15 + 16 + const max = Math.max(r, g, b) 17 + const min = Math.min(r, g, b) 18 + const diff = max - min 19 + 20 + let h = 0, 21 + s, 22 + l 23 + 24 + l = (max + min) / 2 25 + 26 + if (diff === 0) { 27 + h = s = 0 28 + } else { 29 + s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min) 30 + 31 + switch (max) { 32 + case r: 33 + h = (g - b) / diff + (g < b ? 6 : 0) 34 + break 35 + case g: 36 + h = (b - r) / diff + 2 37 + break 38 + case b: 39 + h = (r - g) / diff + 4 40 + break 41 + } 42 + h /= 6 43 + } 44 + 45 + return { 46 + h: h * 360, 47 + s: s * 100, 48 + l: l * 100, 49 + a, 50 + } 51 + } 52 + 53 + /** 54 + * Converts a `HslColor` object (`{ h: number, s: number, l: number, a: number }`) to a hexcode string in the format `"#RRGGBB"`. 55 + * @param {HslColor} hsl - A HSL colour object. 56 + * @param {boolean} appendSymbol - Whether to append "#" to the start of the hex string. Defaults to true. 57 + * @returns {HexCode} A hexcode string in the format `"#RRGGBB"`. The leading "#" symbol is optional. 58 + */ 59 + export const hslToHex = ( 60 + {h, s, l, a}: HslColor, 61 + appendSymbol: boolean = true, 62 + ): HexCode => { 63 + h = (h % 360) / 360 64 + s = Math.max(0, Math.min(1, s / 100)) 65 + l = Math.max(0, Math.min(1, l / 100)) 66 + 67 + let m2: number, m1: number 68 + 69 + if (l <= 0.5) { 70 + m2 = l * (s + 1) 71 + } else { 72 + m2 = l + s - l * s 73 + } 74 + 75 + m1 = l * 2 - m2 76 + 77 + function hue(hueValue: number) { 78 + hueValue = 79 + hueValue < 0 ? hueValue + 1 : hueValue > 1 ? hueValue - 1 : hueValue 80 + 81 + if (hueValue * 6 < 1) { 82 + return m1 + (m2 - m1) * hueValue * 6 83 + } else if (hueValue * 2 < 1) { 84 + return m2 85 + } else if (hueValue * 3 < 2) { 86 + return m1 + (m2 - m1) * (2 / 3 - hueValue) * 6 87 + } else { 88 + return m1 89 + } 90 + } 91 + 92 + const r = Math.round(hue(h + 1 / 3) * 255) 93 + const g = Math.round(hue(h) * 255) 94 + const b = Math.round(hue(h - 1 / 3) * 255) 95 + 96 + return `${appendSymbol ? '#' : ''}${r.toString(16)}${g.toString(16)}${b.toString(16)}${typeof a !== 'undefined' ? Math.round(a).toString(16) : ''}` 97 + } 98 + 99 + /** 100 + * Converts an `RgbColor` object (`{ r: number, g: number, b: number, a: number }`) to a hexcode string in the format `"#RRGGBB"`. 101 + * @param {RgbColor} rgb - An RGB colour object. 102 + * @param {boolean} appendSymbol - Whether to append "#" to the start of the hex string. Defaults to true. 103 + * @returns {HexCode} A hexcode string in the format `"#RRGGBB"`. The leading "#" symbol is optional. 104 + */ 105 + export const rgbToHex = ( 106 + {r, g, b, a}: RgbColor, 107 + appendSymbol: boolean = true, 108 + ): HexCode => { 109 + return `${appendSymbol ? '#' : ''}${r.toString(16)}${g.toString(16)}${b.toString(16)}${typeof a !== 'undefined' ? a.toString(16) : ''}` 110 + } 111 + 112 + /** 113 + * Converts a hexcode string in the format `"#RRGGBB"` to an `RgbColor` object (`{ r: number, g: number, b: number, a: number }`). 114 + * @param {HexCode} hex - A hexcode string in the format `#RRGGBB`. The leading "#" symbol is optional. 115 + * @returns {RgbColor} An RGB colour object. 116 + */ 117 + export const hexToRgb = (hex: HexCode): RgbColor => { 118 + hex = hex.replace('#', '') 119 + const r = parseInt(hex.substring(0, 2), 16) / 255 120 + const g = parseInt(hex.substring(2, 4), 16) / 255 121 + const b = parseInt(hex.substring(4, 6), 16) / 255 122 + const a = hex.length > 6 ? parseInt(hex.substring(6, 8), 16) / 255 : undefined 123 + return {r, g, b, a} 124 + } 125 + 126 + /** 127 + * Converts an `RgbColor` object (`{ r: number, g: number, b: number, a: number }`) to a `HslColor` object (`{ h: number, s: number, l: number, a: number }`) 128 + * @param {RgbColor} - An RGB colour object. 129 + * @returns {HslColor} A HSL colour object. 130 + */ 131 + export const rgbToHsl = ({r, g, b, a}: RgbColor): HslColor => { 132 + r = r / 255 133 + g = g / 255 134 + b = b / 255 135 + const max = Math.max(r, g, b) 136 + const min = Math.min(r, g, b) 137 + const diff = max - min 138 + 139 + let h = 0, 140 + s, 141 + l 142 + 143 + l = (max + min) / 2 144 + 145 + if (diff === 0) { 146 + h = s = 0 147 + } else { 148 + s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min) 149 + 150 + switch (max) { 151 + case r: 152 + h = (g - b) / diff + (g < b ? 6 : 0) 153 + break 154 + case g: 155 + h = (b - r) / diff + 2 156 + break 157 + case b: 158 + h = (r - g) / diff + 4 159 + break 160 + } 161 + h /= 6 162 + } 163 + 164 + return { 165 + h: h * 360, 166 + s: s * 100, 167 + l: l * 100, 168 + a: typeof a !== 'undefined' ? (a / 255) * 100 : undefined, 169 + } 170 + } 171 + 172 + /** 173 + * Converts a `HslColor` object (`{ h: number, s: number, l: number, a: number }`) to a `RgbColor` object (`{ r: number, g: number, b: number, a: number }`) 174 + * @param {HslColor} - A HSL colour object. 175 + * @returns {RgbColor} An RGB colour object. 176 + */ 177 + export const hslToRgb = ({h, s, l, a}: HslColor): RgbColor => { 178 + h = (h % 360) / 360 179 + s = Math.max(0, Math.min(1, s / 100)) 180 + l = Math.max(0, Math.min(1, l / 100)) 181 + 182 + let m2: number, m1: number 183 + 184 + if (l <= 0.5) { 185 + m2 = l * (s + 1) 186 + } else { 187 + m2 = l + s - l * s 188 + } 189 + 190 + m1 = l * 2 - m2 191 + 192 + function hue(hueValue: number) { 193 + hueValue = 194 + hueValue < 0 ? hueValue + 1 : hueValue > 1 ? hueValue - 1 : hueValue 195 + 196 + if (hueValue * 6 < 1) { 197 + return m1 + (m2 - m1) * hueValue * 6 198 + } else if (hueValue * 2 < 1) { 199 + return m2 200 + } else if (hueValue * 3 < 2) { 201 + return m1 + (m2 - m1) * (2 / 3 - hueValue) * 6 202 + } else { 203 + return m1 204 + } 205 + } 206 + 207 + const r = hue(h + 1 / 3) * 255 208 + const g = hue(h) * 255 209 + const b = hue(h - 1 / 3) * 255 210 + 211 + return { 212 + r: Math.round(r), 213 + g: Math.round(g), 214 + b: Math.round(b), 215 + a: typeof a !== 'undefined' ? (a / 100) * 255 : undefined, 216 + } 217 + } 218 + 219 + export const rgbObjectToString = ({r, g, b, a}: RgbColor) => { 220 + const res = `rgba(${r}, ${g}, ${b}${a ? `, ${(a / 255) * 100}` : ''})` 221 + console.log(res) 222 + return res 223 + }
+66
src/alf/util/colors/index.ts
···
··· 1 + import { 2 + hexToHsl, 3 + hslToHex, 4 + hslToRgb, 5 + rgbObjectToString, 6 + } from '#/alf/util/colors/conversion' 7 + 8 + export interface HslColor { 9 + h: number 10 + s: number 11 + l: number 12 + a?: number 13 + } 14 + 15 + export interface RgbColor { 16 + r: number 17 + g: number 18 + b: number 19 + a?: number 20 + } 21 + 22 + export type HexCode = `#${string}` | string 23 + 24 + export const clamp = (val: number) => { 25 + return Math.min(1, Math.max(0, val)) 26 + } 27 + 28 + export const lighten = ( 29 + hex: HexCode, 30 + amount: number, 31 + method: 'relative' | undefined = undefined, 32 + ) => { 33 + const hsl = hexToHsl(hex) 34 + 35 + if (typeof method !== 'undefined' && method === 'relative') { 36 + hsl.l += (hsl.l * amount) / 100 37 + } else { 38 + hsl.l += amount / 100 39 + } 40 + hsl.l = clamp(hsl.l) 41 + return hslToHex(hsl) 42 + } 43 + 44 + export const darken = ( 45 + hex: HexCode, 46 + amount: number, 47 + method: 'relative' | undefined = undefined, 48 + ) => { 49 + const hsl = hexToHsl(hex) 50 + 51 + if (typeof method !== 'undefined' && method === 'relative') { 52 + hsl.l -= (hsl.l * amount) / 100 53 + } else { 54 + hsl.l -= amount / 100 55 + } 56 + hsl.l = clamp(hsl.l) 57 + return hslToHex(hsl) 58 + } 59 + 60 + export const fade = (hex: HexCode, amount: number) => { 61 + const hsl = hexToHsl(hex) 62 + 63 + hsl.a = amount / 100 64 + hsl.a = clamp(hsl.a) 65 + return rgbObjectToString(hslToRgb(hsl)) 66 + }
+13 -4
src/components/Post/Embed/PostPlaceholder.tsx
··· 1 import {StyleSheet, View} from 'react-native' 2 3 - import {usePalette} from '#/lib/hooks/usePalette' 4 import {InfoCircleIcon} from '#/lib/icons' 5 import {Text} from '#/view/com/util/text/Text' 6 import {atoms as a, useTheme} from '#/alf' 7 8 export function PostPlaceholder({children}: {children: React.ReactNode}) { 9 const t = useTheme() 10 - const pal = usePalette('default') 11 return ( 12 <View 13 style={[styles.errorContainer, a.border, t.atoms.border_contrast_low]}> 14 - <InfoCircleIcon size={18} style={pal.text} /> 15 - <Text type="lg" style={pal.text}> 16 {children} 17 </Text> 18 </View>
··· 1 import {StyleSheet, View} from 'react-native' 2 3 import {InfoCircleIcon} from '#/lib/icons' 4 import {Text} from '#/view/com/util/text/Text' 5 import {atoms as a, useTheme} from '#/alf' 6 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 7 8 export function PostPlaceholder({children}: {children: React.ReactNode}) { 9 const t = useTheme() 10 + const colorMode = useColorModeTheme() 11 return ( 12 <View 13 style={[styles.errorContainer, a.border, t.atoms.border_contrast_low]}> 14 + <InfoCircleIcon 15 + size={18} 16 + style={{ 17 + color: colorMode === 'light' ? t.palette.black : t.palette.white, 18 + }} 19 + /> 20 + <Text 21 + type="lg" 22 + style={{ 23 + color: colorMode === 'light' ? t.palette.black : t.palette.white, 24 + }}> 25 {children} 26 </Text> 27 </View>
+5 -7
src/components/Post/Embed/index.tsx
··· 11 import {Trans} from '@lingui/macro' 12 import {useQueryClient} from '@tanstack/react-query' 13 14 - import {usePalette} from '#/lib/hooks/usePalette' 15 import {makeProfileLink} from '#/lib/routes/links' 16 import {useModerationOpts} from '#/state/preferences/moderation-opts' 17 import {unstableCacheProfileView} from '#/state/queries/profile' ··· 176 case 'post_not_found': { 177 return ( 178 <PostPlaceholderText> 179 - <Trans>Deleted</Trans> 180 </PostPlaceholderText> 181 ) 182 } 183 case 'post_blocked': { 184 return ( 185 <PostPlaceholderText> 186 - <Trans>Blocked</Trans> 187 </PostPlaceholderText> 188 ) 189 } ··· 209 return ( 210 <PostPlaceholderText> 211 {isViewerOwner ? ( 212 - <Trans>Removed by you</Trans> 213 ) : ( 214 - <Trans>Removed by author</Trans> 215 )} 216 </PostPlaceholderText> 217 ) ··· 247 248 const t = useTheme() 249 const queryClient = useQueryClient() 250 - const pal = usePalette('default') 251 const itemUrip = new AtUri(quote.uri) 252 const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey) 253 const itemTitle = `Post by ${quote.author.handle}` ··· 287 {!active && <SubtleWebHover hover={hover} style={[a.rounded_md]} />} 288 <Link 289 style={[!active && a.p_md]} 290 - hoverStyle={{borderColor: pal.colors.borderLinkHover}} 291 href={itemHref} 292 title={itemTitle} 293 onBeforePress={onBeforePress}>
··· 11 import {Trans} from '@lingui/macro' 12 import {useQueryClient} from '@tanstack/react-query' 13 14 import {makeProfileLink} from '#/lib/routes/links' 15 import {useModerationOpts} from '#/state/preferences/moderation-opts' 16 import {unstableCacheProfileView} from '#/state/queries/profile' ··· 175 case 'post_not_found': { 176 return ( 177 <PostPlaceholderText> 178 + <Trans>Deleted </Trans> 179 </PostPlaceholderText> 180 ) 181 } 182 case 'post_blocked': { 183 return ( 184 <PostPlaceholderText> 185 + <Trans>Blocked </Trans> 186 </PostPlaceholderText> 187 ) 188 } ··· 208 return ( 209 <PostPlaceholderText> 210 {isViewerOwner ? ( 211 + <Trans> Removed by you </Trans> 212 ) : ( 213 + <Trans>Removed by author </Trans> 214 )} 215 </PostPlaceholderText> 216 ) ··· 246 247 const t = useTheme() 248 const queryClient = useQueryClient() 249 const itemUrip = new AtUri(quote.uri) 250 const itemHref = makeProfileLink(quote.author, 'post', itemUrip.rkey) 251 const itemTitle = `Post by ${quote.author.handle}` ··· 285 {!active && <SubtleWebHover hover={hover} style={[a.rounded_md]} />} 286 <Link 287 style={[!active && a.p_md]} 288 + hoverStyle={{borderColor: t.palette.contrast_300}} 289 href={itemHref} 290 title={itemTitle} 291 onBeforePress={onBeforePress}>
+6 -4
src/components/SearchError.tsx
··· 1 import {View} from 'react-native' 2 3 - import {usePalette} from '#/lib/hooks/usePalette' 4 - import {atoms as a, useBreakpoints} from '#/alf' 5 import * as Layout from '#/components/Layout' 6 import {Text} from '#/components/Typography' 7 import {TimesLarge_Stroke2_Corner0_Rounded} from './icons/Times' ··· 13 title?: string 14 children?: React.ReactNode 15 }) { 16 const {gtMobile} = useBreakpoints() 17 - const pal = usePalette('default') 18 19 return ( 20 <Layout.Content> ··· 27 paddingVertical: 150, 28 }, 29 ]}> 30 - <TimesLarge_Stroke2_Corner0_Rounded width={32} fill={pal.colors.icon} /> 31 <View 32 style={[ 33 a.align_center,
··· 1 import {View} from 'react-native' 2 3 + import {atoms as a, useBreakpoints, useTheme} from '#/alf' 4 import * as Layout from '#/components/Layout' 5 import {Text} from '#/components/Typography' 6 import {TimesLarge_Stroke2_Corner0_Rounded} from './icons/Times' ··· 12 title?: string 13 children?: React.ReactNode 14 }) { 15 + const theme = useTheme() 16 const {gtMobile} = useBreakpoints() 17 18 return ( 19 <Layout.Content> ··· 26 paddingVertical: 150, 27 }, 28 ]}> 29 + <TimesLarge_Stroke2_Corner0_Rounded 30 + width={32} 31 + fill={theme.palette.contrast_500} 32 + /> 33 <View 34 style={[ 35 a.align_center,
-65
src/lib/hooks/usePalette.ts
··· 1 - import {useMemo} from 'react' 2 - import {type TextStyle, type ViewStyle} from 'react-native' 3 - 4 - import { 5 - type PaletteColor, 6 - type PaletteColorName, 7 - useTheme, 8 - } from '../ThemeContext' 9 - 10 - export interface UsePaletteValue { 11 - colors: PaletteColor 12 - view: ViewStyle 13 - viewLight: ViewStyle 14 - btn: ViewStyle 15 - border: ViewStyle 16 - borderDark: ViewStyle 17 - text: TextStyle 18 - textLight: TextStyle 19 - textInverted: TextStyle 20 - link: TextStyle 21 - icon: TextStyle 22 - } 23 - 24 - /** 25 - * @deprecated use `useTheme` from `#/alf` 26 - */ 27 - export function usePalette(color: PaletteColorName): UsePaletteValue { 28 - const theme = useTheme() 29 - return useMemo(() => { 30 - const palette = theme.palette[color] 31 - return { 32 - colors: palette, 33 - view: { 34 - backgroundColor: palette.background, 35 - }, 36 - viewLight: { 37 - backgroundColor: palette.backgroundLight, 38 - }, 39 - btn: { 40 - backgroundColor: palette.backgroundLight, 41 - }, 42 - border: { 43 - borderColor: palette.border, 44 - }, 45 - borderDark: { 46 - borderColor: palette.borderDark, 47 - }, 48 - text: { 49 - color: palette.text, 50 - }, 51 - textLight: { 52 - color: palette.textLight, 53 - }, 54 - textInverted: { 55 - color: palette.textInverted, 56 - }, 57 - link: { 58 - color: palette.link, 59 - }, 60 - icon: { 61 - color: palette.icon, 62 - }, 63 - } 64 - }, [theme, color]) 65 - }
···
+25 -9
src/screens/Profile/ProfileFeed/index.tsx
··· 1 import React, {useCallback, useMemo} from 'react' 2 - import {StyleSheet, View} from 'react-native' 3 import {useAnimatedRef} from 'react-native-reanimated' 4 import {AppBskyFeedDefs} from '@atproto/api' 5 import {msg, Trans} from '@lingui/macro' ··· 10 11 import {VIDEO_FEED_URIS} from '#/lib/constants' 12 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 13 - import {usePalette} from '#/lib/hooks/usePalette' 14 import {useSetTitle} from '#/lib/hooks/useSetTitle' 15 import {ComposeIcon2} from '#/lib/icons' 16 import {type CommonNavigatorParams} from '#/lib/routes/types' ··· 45 ProfileFeedHeader, 46 ProfileFeedHeaderSkeleton, 47 } from '#/screens/Profile/components/ProfileFeedHeader' 48 import * as Layout from '#/components/Layout' 49 50 type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeed'> ··· 56 feedCacheKey: props.route.params.feedCacheKey, 57 } 58 : undefined 59 - const pal = usePalette('default') 60 const {_} = useLingui() 61 const navigation = useNavigation<NavigationProp>() 62 63 const uri = useMemo( 64 () => makeRecordUri(handleOrDid, 'app.bsky.feed.generator', rkey), 65 [rkey, handleOrDid], ··· 78 return ( 79 <Layout.Screen testID="profileFeedScreenError"> 80 <Layout.Content> 81 - <View style={[pal.view, pal.border, styles.notFoundContainer]}> 82 - <Text type="title-lg" style={[pal.text, s.mb10]}> 83 - <Trans>Could not load feed</Trans> 84 </Text> 85 - <Text type="md" style={[pal.text, s.mb20]}> 86 {error.toString()} 87 </Text> 88 ··· 93 accessibilityHint={_(msg`Returns to previous page`)} 94 onPress={onPressBack} 95 style={{flexShrink: 1}}> 96 - <Text type="button" style={pal.text}> 97 - <Trans>Go Back</Trans> 98 </Text> 99 </Button> 100 </View>
··· 1 import React, {useCallback, useMemo} from 'react' 2 + import {StyleSheet, type TextStyle, View} from 'react-native' 3 import {useAnimatedRef} from 'react-native-reanimated' 4 import {AppBskyFeedDefs} from '@atproto/api' 5 import {msg, Trans} from '@lingui/macro' ··· 10 11 import {VIDEO_FEED_URIS} from '#/lib/constants' 12 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 13 import {useSetTitle} from '#/lib/hooks/useSetTitle' 14 import {ComposeIcon2} from '#/lib/icons' 15 import {type CommonNavigatorParams} from '#/lib/routes/types' ··· 44 ProfileFeedHeader, 45 ProfileFeedHeaderSkeleton, 46 } from '#/screens/Profile/components/ProfileFeedHeader' 47 + import {useTheme} from '#/alf' 48 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 49 import * as Layout from '#/components/Layout' 50 51 type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileFeed'> ··· 57 feedCacheKey: props.route.params.feedCacheKey, 58 } 59 : undefined 60 + const theme = useTheme() 61 + const colorMode = useColorModeTheme() 62 const {_} = useLingui() 63 const navigation = useNavigation<NavigationProp>() 64 65 + const textStyle: TextStyle = { 66 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 67 + } 68 + 69 const uri = useMemo( 70 () => makeRecordUri(handleOrDid, 'app.bsky.feed.generator', rkey), 71 [rkey, handleOrDid], ··· 84 return ( 85 <Layout.Screen testID="profileFeedScreenError"> 86 <Layout.Content> 87 + <View 88 + style={[ 89 + { 90 + borderColor: theme.palette.contrast_100, 91 + backgroundColor: 92 + colorMode === 'light' 93 + ? theme.palette.white 94 + : theme.palette.black, 95 + }, 96 + styles.notFoundContainer, 97 + ]}> 98 + <Text type="title-lg" style={[textStyle, s.mb10]}> 99 + <Trans>Could not load feed </Trans> 100 </Text> 101 + <Text type="md" style={[textStyle, s.mb20]}> 102 {error.toString()} 103 </Text> 104 ··· 109 accessibilityHint={_(msg`Returns to previous page`)} 110 onPress={onPressBack} 111 style={{flexShrink: 1}}> 112 + <Text type="button" style={textStyle}> 113 + <Trans>Go Back </Trans> 114 </Text> 115 </Button> 116 </View>
+2 -4
src/screens/Search/SearchResults.tsx
··· 4 import {msg, Trans} from '@lingui/macro' 5 import {useLingui} from '@lingui/react' 6 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import {augmentSearchQuery} from '#/lib/strings/helpers' 9 import {useActorSearch} from '#/state/queries/actor-search' 10 import {usePopularFeedsSearch} from '#/state/queries/feed' ··· 193 hasNextPage, 194 } = useSearchPostsQuery({query: augmentedQuery, sort, enabled: active}) 195 196 - const pal = usePalette('default') 197 const t = useTheme() 198 const onPullToRefresh = useCallback(async () => { 199 setIsPTR(true) ··· 254 <Text style={[a.text_md, a.text_center, a.leading_snug]}> 255 <Trans> 256 <InlineLinkText 257 - style={[pal.link]} 258 label={_(msg`Sign in`)} 259 to={'#'} 260 onPress={showSignIn}> ··· 262 </InlineLinkText> 263 <Text style={t.atoms.text_contrast_medium}> or </Text> 264 <InlineLinkText 265 - style={[pal.link]} 266 label={_(msg`Create an account`)} 267 to={'#'} 268 onPress={showCreateAccount}>
··· 4 import {msg, Trans} from '@lingui/macro' 5 import {useLingui} from '@lingui/react' 6 7 import {augmentSearchQuery} from '#/lib/strings/helpers' 8 import {useActorSearch} from '#/state/queries/actor-search' 9 import {usePopularFeedsSearch} from '#/state/queries/feed' ··· 192 hasNextPage, 193 } = useSearchPostsQuery({query: augmentedQuery, sort, enabled: active}) 194 195 const t = useTheme() 196 const onPullToRefresh = useCallback(async () => { 197 setIsPTR(true) ··· 252 <Text style={[a.text_md, a.text_center, a.leading_snug]}> 253 <Trans> 254 <InlineLinkText 255 + style={[{color: t.palette.primary_500}]} 256 label={_(msg`Sign in`)} 257 to={'#'} 258 onPress={showSignIn}> ··· 260 </InlineLinkText> 261 <Text style={t.atoms.text_contrast_medium}> or </Text> 262 <InlineLinkText 263 + style={[{color: t.palette.primary_500}]} 264 label={_(msg`Create an account`)} 265 to={'#'} 266 onPress={showCreateAccount}>
+31 -6
src/view/com/composer/Composer.tsx
··· 69 import {useAppState} from '#/lib/hooks/useAppState' 70 import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' 71 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 72 - import {usePalette} from '#/lib/hooks/usePalette' 73 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 74 import {mimeToExt} from '#/lib/media/video/util' 75 import {type NavigationProp} from '#/lib/routes/types' ··· 121 import {Text} from '#/view/com/util/text/Text' 122 import {UserAvatar} from '#/view/com/util/UserAvatar' 123 import {atoms as a, native, useTheme, web} from '#/alf' 124 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 125 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' 126 import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji' ··· 994 topBarAnimatedStyle: StyleProp<ViewStyle> 995 children?: React.ReactNode 996 }) { 997 - const pal = usePalette('default') 998 const {_} = useLingui() 999 return ( 1000 <Animated.View ··· 1019 <View style={a.flex_1} /> 1020 {isPublishing ? ( 1021 <> 1022 - <Text style={pal.textLight}>{publishingStage}</Text> 1023 <View style={styles.postBtn}> 1024 <ActivityIndicator /> 1025 </View> ··· 1085 } 1086 1087 function AltTextReminder({error}: {error: string}) { 1088 - const pal = usePalette('default') 1089 return ( 1090 - <View style={[styles.reminderLine, pal.viewLight]}> 1091 <View style={styles.errorIcon}> 1092 <FontAwesomeIcon 1093 icon="exclamation" ··· 1095 size={10} 1096 /> 1097 </View> 1098 - <Text style={[pal.text, a.flex_1]}>{error}</Text> 1099 </View> 1100 ) 1101 }
··· 69 import {useAppState} from '#/lib/hooks/useAppState' 70 import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' 71 import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 72 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 73 import {mimeToExt} from '#/lib/media/video/util' 74 import {type NavigationProp} from '#/lib/routes/types' ··· 120 import {Text} from '#/view/com/util/text/Text' 121 import {UserAvatar} from '#/view/com/util/UserAvatar' 122 import {atoms as a, native, useTheme, web} from '#/alf' 123 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 124 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 125 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' 126 import {EmojiArc_Stroke2_Corner0_Rounded as EmojiSmileIcon} from '#/components/icons/Emoji' ··· 994 topBarAnimatedStyle: StyleProp<ViewStyle> 995 children?: React.ReactNode 996 }) { 997 + const theme = useTheme() 998 + const colorMode = useColorModeTheme() 999 const {_} = useLingui() 1000 return ( 1001 <Animated.View ··· 1020 <View style={a.flex_1} /> 1021 {isPublishing ? ( 1022 <> 1023 + <Text 1024 + style={{ 1025 + color: 1026 + colorMode === 'light' 1027 + ? theme.palette.white 1028 + : theme.palette.black, 1029 + }}> 1030 + {publishingStage} 1031 + </Text> 1032 <View style={styles.postBtn}> 1033 <ActivityIndicator /> 1034 </View> ··· 1094 } 1095 1096 function AltTextReminder({error}: {error: string}) { 1097 + const theme = useTheme() 1098 + const colorMode = useColorModeTheme() 1099 return ( 1100 + <View 1101 + style={[ 1102 + styles.reminderLine, 1103 + { 1104 + backgroundColor: theme.palette.contrast_25, 1105 + }, 1106 + ]}> 1107 <View style={styles.errorIcon}> 1108 <FontAwesomeIcon 1109 icon="exclamation" ··· 1111 size={10} 1112 /> 1113 </View> 1114 + <Text 1115 + style={[ 1116 + { 1117 + color: 1118 + colorMode === 'light' ? theme.palette.black : theme.palette.white, 1119 + }, 1120 + a.flex_1, 1121 + ]}> 1122 + {error} 1123 + </Text> 1124 </View> 1125 ) 1126 }
+13 -6
src/view/com/composer/char-progress/CharProgress.tsx
··· 10 import ProgressPie from 'react-native-progress/Pie' 11 12 import {MAX_GRAPHEME_LENGTH} from '#/lib/constants' 13 - import {usePalette} from '#/lib/hooks/usePalette' 14 - import {atoms as a} from '#/alf' 15 import {Text} from '../../util/text/Text' 16 17 export function CharProgress({ ··· 27 textStyle?: StyleProp<TextStyle> 28 size?: number 29 }) { 30 const maxLength = max || MAX_GRAPHEME_LENGTH 31 - const pal = usePalette('default') 32 - const textColor = count > maxLength ? '#e60000' : pal.colors.text 33 - const circleColor = count > maxLength ? '#e60000' : pal.colors.link 34 return ( 35 <View 36 style={[a.flex_row, a.align_center, a.justify_between, a.gap_sm, style]}> ··· 55 <ProgressCircle 56 size={size ?? 30} 57 borderWidth={1} 58 - borderColor={pal.colors.border} 59 color={circleColor} 60 progress={count / maxLength} 61 />
··· 10 import ProgressPie from 'react-native-progress/Pie' 11 12 import {MAX_GRAPHEME_LENGTH} from '#/lib/constants' 13 + import {atoms as a, useTheme} from '#/alf' 14 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 15 import {Text} from '../../util/text/Text' 16 17 export function CharProgress({ ··· 27 textStyle?: StyleProp<TextStyle> 28 size?: number 29 }) { 30 + const theme = useTheme() 31 + const colorMode = useColorModeTheme() 32 const maxLength = max || MAX_GRAPHEME_LENGTH 33 + const textColor = 34 + count > maxLength 35 + ? theme.palette.negative_500 36 + : colorMode === 'light' 37 + ? theme.palette.black 38 + : theme.palette.white 39 + const circleColor = 40 + count > maxLength ? theme.palette.negative_500 : theme.palette.primary_500 41 return ( 42 <View 43 style={[a.flex_row, a.align_center, a.justify_between, a.gap_sm, style]}> ··· 62 <ProgressCircle 63 size={size ?? 30} 64 borderWidth={1} 65 + borderColor={theme.palette.contrast_100} 66 color={circleColor} 67 progress={count / maxLength} 68 />
+18 -10
src/view/com/lists/MyLists.tsx
··· 11 import {msg} from '@lingui/macro' 12 import {useLingui} from '@lingui/react' 13 14 - import {usePalette} from '#/lib/hooks/usePalette' 15 import {cleanError} from '#/lib/strings/errors' 16 import {s} from '#/lib/styles' 17 import {logger} from '#/logger' 18 import {useModerationOpts} from '#/state/preferences/moderation-opts' 19 import {type MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists' 20 import {atoms as a, useTheme} from '#/alf' 21 import {BulletList_Stroke2_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList' 22 import * as ListCard from '#/components/ListCard' 23 import {Text} from '#/components/Typography' ··· 41 renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element 42 testID?: string 43 }) { 44 - const pal = usePalette('default') 45 - const t = useTheme() 46 const {_} = useLingui() 47 const moderationOpts = useModerationOpts() 48 const [isPTRing, setIsPTRing] = React.useState(false) ··· 108 a.align_center, 109 a.justify_center, 110 a.rounded_full, 111 - t.atoms.bg_contrast_25, 112 { 113 width: 32, 114 height: 32, 115 }, 116 ]}> 117 - <ListIcon size="md" fill={t.atoms.text_contrast_low.color} /> 118 </View> 119 <Text 120 style={[ ··· 122 a.flex_1, 123 a.text_sm, 124 a.leading_snug, 125 - t.atoms.text_contrast_medium, 126 { 127 maxWidth: 200, 128 }, ··· 151 <View 152 style={[ 153 index !== 0 && a.border_t, 154 - t.atoms.border_contrast_low, 155 a.px_lg, 156 a.py_lg, 157 ]}> ··· 159 </View> 160 ) 161 }, 162 - [t, renderItem, error, onRefresh, emptyText], 163 ) 164 165 if (inline) { ··· 175 <RefreshControl 176 refreshing={isPTRing} 177 onRefresh={onRefresh} 178 - tintColor={pal.colors.text} 179 - titleColor={pal.colors.text} 180 /> 181 } 182 contentContainerStyle={[s.contentContainer]}
··· 11 import {msg} from '@lingui/macro' 12 import {useLingui} from '@lingui/react' 13 14 import {cleanError} from '#/lib/strings/errors' 15 import {s} from '#/lib/styles' 16 import {logger} from '#/logger' 17 import {useModerationOpts} from '#/state/preferences/moderation-opts' 18 import {type MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists' 19 import {atoms as a, useTheme} from '#/alf' 20 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 21 import {BulletList_Stroke2_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList' 22 import * as ListCard from '#/components/ListCard' 23 import {Text} from '#/components/Typography' ··· 41 renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element 42 testID?: string 43 }) { 44 + const theme = useTheme() 45 + const colorMode = useColorModeTheme() 46 const {_} = useLingui() 47 const moderationOpts = useModerationOpts() 48 const [isPTRing, setIsPTRing] = React.useState(false) ··· 108 a.align_center, 109 a.justify_center, 110 a.rounded_full, 111 + theme.atoms.bg_contrast_25, 112 { 113 width: 32, 114 height: 32, 115 }, 116 ]}> 117 + <ListIcon size="md" fill={theme.atoms.text_contrast_low.color} /> 118 </View> 119 <Text 120 style={[ ··· 122 a.flex_1, 123 a.text_sm, 124 a.leading_snug, 125 + theme.atoms.text_contrast_medium, 126 { 127 maxWidth: 200, 128 }, ··· 151 <View 152 style={[ 153 index !== 0 && a.border_t, 154 + theme.atoms.border_contrast_low, 155 a.px_lg, 156 a.py_lg, 157 ]}> ··· 159 </View> 160 ) 161 }, 162 + [theme, renderItem, error, onRefresh, emptyText], 163 ) 164 165 if (inline) { ··· 175 <RefreshControl 176 refreshing={isPTRing} 177 onRefresh={onRefresh} 178 + tintColor={ 179 + colorMode === 'light' 180 + ? theme.palette.black 181 + : theme.palette.white 182 + } 183 + titleColor={ 184 + colorMode === 'light' 185 + ? theme.palette.black 186 + : theme.palette.white 187 + } 188 /> 189 } 190 contentContainerStyle={[s.contentContainer]}
+46 -15
src/view/com/modals/CreateOrEditList.tsx
··· 5 ScrollView, 6 StyleSheet, 7 TextInput, 8 TouchableOpacity, 9 View, 10 } from 'react-native' 11 import {LinearGradient} from 'expo-linear-gradient' 12 import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api' 13 import {msg, Trans} from '@lingui/macro' 14 import {useLingui} from '@lingui/react' 15 16 - import {usePalette} from '#/lib/hooks/usePalette' 17 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 18 import {cleanError, isNetworkError} from '#/lib/strings/errors' 19 import {enforceLen} from '#/lib/strings/helpers' 20 import {richTextToString} from '#/lib/strings/rich-text-helpers' 21 import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip' 22 import {colors, gradients, s} from '#/lib/styles' 23 - import {useTheme} from '#/lib/ThemeContext' 24 import {type ImageMeta} from '#/state/gallery' 25 import {useModalControls} from '#/state/modals' 26 import { ··· 32 import {Text} from '#/view/com/util/text/Text' 33 import * as Toast from '#/view/com/util/Toast' 34 import {EditableUserAvatar} from '#/view/com/util/UserAvatar' 35 36 const MAX_NAME = 64 // todo 37 const MAX_DESCRIPTION = 300 // todo ··· 50 const {closeModal} = useModalControls() 51 const {isMobile} = useWebMediaQueries() 52 const [error, setError] = useState<string>('') 53 - const pal = usePalette('default') 54 const theme = useTheme() 55 const {_} = useLingui() 56 const listCreateMutation = useListCreateMutation() 57 const listMetadataMutation = useListMetadataMutation() ··· 92 return shortenLinks(descriptionRt).graphemeLength 93 }, [descriptionRt]) 94 const isDescriptionOver = graphemeLength > MAX_DESCRIPTION 95 96 const [avatar, setAvatar] = useState<string | undefined>(list?.avatar) 97 const [newAvatar, setNewAvatar] = useState<ImageMeta | undefined | null>() ··· 211 <KeyboardAvoidingView behavior="height"> 212 <ScrollView 213 style={[ 214 - pal.view, 215 { 216 paddingHorizontal: isMobile ? 16 : 0, 217 }, 218 ]} 219 testID="createOrEditListModal"> 220 - <Text style={[styles.title, pal.text]}> 221 {isCurateList ? ( 222 list ? ( 223 <Trans>Edit User List</Trans> ··· 235 <ErrorMessage message={error} /> 236 </View> 237 )} 238 - <Text style={[styles.label, pal.text]}> 239 <Trans>List Avatar</Trans> 240 </Text> 241 - <View style={[styles.avi, {borderColor: pal.colors.background}]}> 242 <EditableUserAvatar 243 type="list" 244 size={80} ··· 249 <View style={styles.form}> 250 <View> 251 <View style={styles.labelWrapper}> 252 - <Text style={[styles.label, pal.text]} nativeID="list-name"> 253 <Trans>List Name</Trans> 254 </Text> 255 </View> 256 <TextInput 257 testID="editNameInput" 258 - style={[styles.textInput, pal.border, pal.text]} 259 placeholder={ 260 isCurateList 261 ? _(msg`e.g. Great Posters`) ··· 273 <View style={s.pb10}> 274 <View style={styles.labelWrapper}> 275 <Text 276 - style={[styles.label, pal.text]} 277 nativeID="list-description"> 278 <Trans>Description</Trans> 279 </Text> 280 <Text 281 - style={[!isDescriptionOver ? pal.textLight : s.red3, s.f13]}> 282 - {graphemeLength}/{MAX_DESCRIPTION} 283 </Text> 284 </View> 285 <TextInput 286 testID="editDescriptionInput" 287 - style={[styles.textArea, pal.border, pal.text]} 288 placeholder={ 289 isCurateList 290 ? _(msg`e.g. The posters who never miss.`) 291 : _(msg`e.g. Users that repeatedly reply with ads.`) 292 } 293 placeholderTextColor={colors.gray4} 294 - keyboardAppearance={theme.colorScheme} 295 multiline 296 value={descriptionRt.text} 297 onChangeText={onDescriptionChange} ··· 334 accessibilityHint="" 335 onAccessibilityEscape={onPressCancel}> 336 <View style={[styles.btn]}> 337 - <Text style={[s.black, s.bold, pal.text]}> 338 <Trans context="action">Cancel</Trans> 339 </Text> 340 </View>
··· 5 ScrollView, 6 StyleSheet, 7 TextInput, 8 + type TextStyle, 9 TouchableOpacity, 10 + useColorScheme, 11 View, 12 + type ViewStyle, 13 } from 'react-native' 14 import {LinearGradient} from 'expo-linear-gradient' 15 import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api' 16 import {msg, Trans} from '@lingui/macro' 17 import {useLingui} from '@lingui/react' 18 19 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 20 import {cleanError, isNetworkError} from '#/lib/strings/errors' 21 import {enforceLen} from '#/lib/strings/helpers' 22 import {richTextToString} from '#/lib/strings/rich-text-helpers' 23 import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip' 24 import {colors, gradients, s} from '#/lib/styles' 25 import {type ImageMeta} from '#/state/gallery' 26 import {useModalControls} from '#/state/modals' 27 import { ··· 33 import {Text} from '#/view/com/util/text/Text' 34 import * as Toast from '#/view/com/util/Toast' 35 import {EditableUserAvatar} from '#/view/com/util/UserAvatar' 36 + import {useTheme} from '#/alf' 37 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 38 39 const MAX_NAME = 64 // todo 40 const MAX_DESCRIPTION = 300 // todo ··· 53 const {closeModal} = useModalControls() 54 const {isMobile} = useWebMediaQueries() 55 const [error, setError] = useState<string>('') 56 const theme = useTheme() 57 + const colorMode = useColorModeTheme() 58 const {_} = useLingui() 59 const listCreateMutation = useListCreateMutation() 60 const listMetadataMutation = useListMetadataMutation() ··· 95 return shortenLinks(descriptionRt).graphemeLength 96 }, [descriptionRt]) 97 const isDescriptionOver = graphemeLength > MAX_DESCRIPTION 98 + 99 + const colorScheme = useColorScheme() ?? 'default' 100 + 101 + const viewStyle: ViewStyle = { 102 + backgroundColor: 103 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 104 + } 105 + 106 + const textStyle: TextStyle = { 107 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 108 + } 109 + 110 + const textStyleLight: TextStyle = { 111 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 112 + } 113 + 114 + const borderStyle: ViewStyle = { 115 + borderColor: theme.palette.contrast_100, 116 + } 117 118 const [avatar, setAvatar] = useState<string | undefined>(list?.avatar) 119 const [newAvatar, setNewAvatar] = useState<ImageMeta | undefined | null>() ··· 233 <KeyboardAvoidingView behavior="height"> 234 <ScrollView 235 style={[ 236 + viewStyle, 237 { 238 paddingHorizontal: isMobile ? 16 : 0, 239 }, 240 ]} 241 testID="createOrEditListModal"> 242 + <Text style={[styles.title, textStyle]}> 243 {isCurateList ? ( 244 list ? ( 245 <Trans>Edit User List</Trans> ··· 257 <ErrorMessage message={error} /> 258 </View> 259 )} 260 + <Text style={[styles.label, textStyle]}> 261 <Trans>List Avatar</Trans> 262 </Text> 263 + <View 264 + style={[ 265 + styles.avi, 266 + { 267 + borderColor: 268 + colorMode === 'light' 269 + ? theme.palette.white 270 + : theme.palette.black, 271 + }, 272 + ]}> 273 <EditableUserAvatar 274 type="list" 275 size={80} ··· 280 <View style={styles.form}> 281 <View> 282 <View style={styles.labelWrapper}> 283 + <Text style={[styles.label, textStyle]} nativeID="list-name"> 284 <Trans>List Name</Trans> 285 </Text> 286 </View> 287 <TextInput 288 testID="editNameInput" 289 + style={[styles.textInput, borderStyle, textStyle]} 290 placeholder={ 291 isCurateList 292 ? _(msg`e.g. Great Posters`) ··· 304 <View style={s.pb10}> 305 <View style={styles.labelWrapper}> 306 <Text 307 + style={[styles.label, textStyle]} 308 nativeID="list-description"> 309 <Trans>Description</Trans> 310 </Text> 311 <Text 312 + style={[!isDescriptionOver ? textStyleLight : s.red3, s.f13]}> 313 + {graphemeLength} / {MAX_DESCRIPTION} 314 </Text> 315 </View> 316 <TextInput 317 testID="editDescriptionInput" 318 + style={[styles.textArea, borderStyle, textStyle]} 319 placeholder={ 320 isCurateList 321 ? _(msg`e.g. The posters who never miss.`) 322 : _(msg`e.g. Users that repeatedly reply with ads.`) 323 } 324 placeholderTextColor={colors.gray4} 325 + keyboardAppearance={colorScheme} 326 multiline 327 value={descriptionRt.text} 328 onChangeText={onDescriptionChange} ··· 365 accessibilityHint="" 366 onAccessibilityEscape={onPressCancel}> 367 <View style={[styles.btn]}> 368 + <Text style={[s.black, s.bold, textStyle]}> 369 <Trans context="action">Cancel</Trans> 370 </Text> 371 </View>
+4 -4
src/view/com/modals/CropImage.web.tsx
··· 6 import {useLingui} from '@lingui/react' 7 import ReactCrop, {type PercentCrop} from 'react-image-crop' 8 9 - import {usePalette} from '#/lib/hooks/usePalette' 10 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 11 import {type PickerImage} from '#/lib/media/picker.shared' 12 import {getDataUriSize} from '#/lib/media/util' 13 import {gradients, s} from '#/lib/styles' 14 import {useModalControls} from '#/state/modals' 15 import {Text} from '#/view/com/util/text/Text' 16 17 export const snapPoints = ['0%'] 18 ··· 27 circular?: boolean 28 onSelect: (img?: PickerImage) => void 29 }) { 30 - const pal = usePalette('default') 31 const {_} = useLingui() 32 33 const {closeModal} = useModalControls() ··· 78 79 return ( 80 <View> 81 - <View style={[styles.cropper, pal.borderDark]}> 82 <ReactCrop 83 aspect={aspect} 84 crop={crop} ··· 93 accessibilityRole="button" 94 accessibilityLabel={_(msg`Cancel image crop`)} 95 accessibilityHint={_(msg`Exits image cropping process`)}> 96 - <Text type="xl" style={pal.link}> 97 <Trans>Cancel</Trans> 98 </Text> 99 </TouchableOpacity>
··· 6 import {useLingui} from '@lingui/react' 7 import ReactCrop, {type PercentCrop} from 'react-image-crop' 8 9 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 10 import {type PickerImage} from '#/lib/media/picker.shared' 11 import {getDataUriSize} from '#/lib/media/util' 12 import {gradients, s} from '#/lib/styles' 13 import {useModalControls} from '#/state/modals' 14 import {Text} from '#/view/com/util/text/Text' 15 + import {useTheme} from '#/alf' 16 17 export const snapPoints = ['0%'] 18 ··· 27 circular?: boolean 28 onSelect: (img?: PickerImage) => void 29 }) { 30 + const theme = useTheme() 31 const {_} = useLingui() 32 33 const {closeModal} = useModalControls() ··· 78 79 return ( 80 <View> 81 + <View style={[styles.cropper, {borderColor: theme.palette.contrast_200}]}> 82 <ReactCrop 83 aspect={aspect} 84 crop={crop} ··· 93 accessibilityRole="button" 94 accessibilityLabel={_(msg`Cancel image crop`)} 95 accessibilityHint={_(msg`Exits image cropping process`)}> 96 + <Text type="xl" style={{color: theme.palette.primary_500}}> 97 <Trans>Cancel</Trans> 98 </Text> 99 </TouchableOpacity>
+49 -23
src/view/com/modals/DeleteAccount.tsx
··· 3 ActivityIndicator, 4 SafeAreaView, 5 StyleSheet, 6 TouchableOpacity, 7 View, 8 } from 'react-native' 9 import {LinearGradient} from 'expo-linear-gradient' 10 import {msg, Trans} from '@lingui/macro' 11 import {useLingui} from '@lingui/react' 12 13 import {DM_SERVICE_HEADERS} from '#/lib/constants' 14 - import {usePalette} from '#/lib/hooks/usePalette' 15 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 16 import {cleanError} from '#/lib/strings/errors' 17 import {colors, gradients, s} from '#/lib/styles' 18 - import {useTheme} from '#/lib/ThemeContext' 19 import {isAndroid, isWeb} from '#/platform/detection' 20 import {useModalControls} from '#/state/modals' 21 import {useAgent, useSession, useSessionApi} from '#/state/session' 22 import {atoms as a, useTheme as useNewTheme} from '#/alf' 23 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 24 import {Text as NewText} from '#/components/Typography' 25 import {resetToTab} from '../../../Navigation' ··· 31 export const snapPoints = isAndroid ? ['90%'] : ['55%'] 32 33 export function Component({}: {}) { 34 - const pal = usePalette('default') 35 - const theme = useTheme() 36 - const t = useNewTheme() 37 const {currentAccount} = useSession() 38 const agent = useAgent() 39 const {removeAccount} = useSessionApi() 40 const {_} = useLingui() 41 const {closeModal} = useModalControls() 42 const {isMobile} = useWebMediaQueries() 43 const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false) 44 const [confirmCode, setConfirmCode] = React.useState<string>('') 45 const [password, setPassword] = React.useState<string>('') ··· 95 } 96 return ( 97 <SafeAreaView style={[s.flex1]}> 98 - <ScrollView style={[pal.view]} keyboardShouldPersistTaps="handled"> 99 - <View style={[styles.titleContainer, pal.view]}> 100 - <Text type="title-xl" style={[s.textCenter, pal.text]}> 101 <Trans> 102 Delete Account{' '} 103 - <Text type="title-xl" style={[pal.text, s.bold]}> 104 " 105 </Text> 106 <Text ··· 108 numberOfLines={1} 109 style={[ 110 isMobile ? styles.titleMobile : styles.titleDesktop, 111 - pal.text, 112 s.bold, 113 ]}> 114 {currentAccount?.handle} 115 </Text> 116 - <Text type="title-xl" style={[pal.text, s.bold]}> 117 " 118 </Text> 119 </Trans> ··· 121 </View> 122 {!isEmailSent ? ( 123 <> 124 - <Text type="lg" style={[styles.description, pal.text]}> 125 <Trans> 126 For security reasons, we'll need to send a confirmation code to 127 your email address. ··· 166 accessibilityLabel={_(msg`Cancel account deletion`)} 167 accessibilityHint="" 168 onAccessibilityEscape={onCancel}> 169 - <Text type="button-lg" style={pal.textLight}> 170 <Trans context="action">Cancel</Trans> 171 </Text> 172 </TouchableOpacity> ··· 182 a.mt_lg, 183 a.p_lg, 184 a.rounded_sm, 185 - t.atoms.bg_contrast_25, 186 ]}> 187 <CircleInfo 188 size="md" ··· 208 {/* TODO: Update this label to be more concise */} 209 <Text 210 type="lg" 211 - style={[pal.text, styles.description]} 212 nativeID="confirmationCode"> 213 <Trans> 214 Check your inbox for an email with the confirmation code to ··· 216 </Trans> 217 </Text> 218 <TextInput 219 - style={[styles.textInput, pal.borderDark, pal.text, styles.mb20]} 220 placeholder={_(msg`Confirmation code`)} 221 - placeholderTextColor={pal.textLight.color} 222 - keyboardAppearance={theme.colorScheme} 223 value={confirmCode} 224 onChangeText={setConfirmCode} 225 accessibilityLabelledBy="confirmationCode" ··· 230 /> 231 <Text 232 type="lg" 233 - style={[pal.text, styles.description]} 234 nativeID="password"> 235 <Trans>Please enter your password as well:</Trans> 236 </Text> 237 <TextInput 238 - style={[styles.textInput, pal.borderDark, pal.text]} 239 placeholder={_(msg`Password`)} 240 - placeholderTextColor={pal.textLight.color} 241 - keyboardAppearance={theme.colorScheme} 242 secureTextEntry 243 value={password} 244 onChangeText={setPassword} ··· 274 accessibilityLabel={_(msg`Cancel account deletion`)} 275 accessibilityHint={_(msg`Exits account deletion process`)} 276 onAccessibilityEscape={onCancel}> 277 - <Text type="button-lg" style={pal.textLight}> 278 <Trans context="action">Cancel</Trans> 279 </Text> 280 </TouchableOpacity>
··· 3 ActivityIndicator, 4 SafeAreaView, 5 StyleSheet, 6 + type TextStyle, 7 TouchableOpacity, 8 + useColorScheme, 9 View, 10 + type ViewStyle, 11 } from 'react-native' 12 import {LinearGradient} from 'expo-linear-gradient' 13 import {msg, Trans} from '@lingui/macro' 14 import {useLingui} from '@lingui/react' 15 16 import {DM_SERVICE_HEADERS} from '#/lib/constants' 17 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 18 import {cleanError} from '#/lib/strings/errors' 19 import {colors, gradients, s} from '#/lib/styles' 20 import {isAndroid, isWeb} from '#/platform/detection' 21 import {useModalControls} from '#/state/modals' 22 import {useAgent, useSession, useSessionApi} from '#/state/session' 23 import {atoms as a, useTheme as useNewTheme} from '#/alf' 24 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 25 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' 26 import {Text as NewText} from '#/components/Typography' 27 import {resetToTab} from '../../../Navigation' ··· 33 export const snapPoints = isAndroid ? ['90%'] : ['55%'] 34 35 export function Component({}: {}) { 36 + const theme = useNewTheme() 37 + const colorMode = useColorModeTheme() 38 const {currentAccount} = useSession() 39 const agent = useAgent() 40 const {removeAccount} = useSessionApi() 41 const {_} = useLingui() 42 const {closeModal} = useModalControls() 43 const {isMobile} = useWebMediaQueries() 44 + 45 + const colorScheme = useColorScheme() ?? 'default' 46 + 47 + const viewStyle: ViewStyle = { 48 + backgroundColor: 49 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 50 + } 51 + 52 + const textStyle: TextStyle = { 53 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 54 + } 55 + 56 + const textStyleLight: TextStyle = { 57 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 58 + } 59 + 60 const [isEmailSent, setIsEmailSent] = React.useState<boolean>(false) 61 const [confirmCode, setConfirmCode] = React.useState<string>('') 62 const [password, setPassword] = React.useState<string>('') ··· 112 } 113 return ( 114 <SafeAreaView style={[s.flex1]}> 115 + <ScrollView style={[viewStyle]} keyboardShouldPersistTaps="handled"> 116 + <View style={[styles.titleContainer, viewStyle]}> 117 + <Text type="title-xl" style={[s.textCenter, textStyle]}> 118 <Trans> 119 Delete Account{' '} 120 + <Text type="title-xl" style={[textStyle, s.bold]}> 121 " 122 </Text> 123 <Text ··· 125 numberOfLines={1} 126 style={[ 127 isMobile ? styles.titleMobile : styles.titleDesktop, 128 + textStyle, 129 s.bold, 130 ]}> 131 {currentAccount?.handle} 132 </Text> 133 + <Text type="title-xl" style={[textStyle, s.bold]}> 134 " 135 </Text> 136 </Trans> ··· 138 </View> 139 {!isEmailSent ? ( 140 <> 141 + <Text type="lg" style={[styles.description, textStyle]}> 142 <Trans> 143 For security reasons, we'll need to send a confirmation code to 144 your email address. ··· 183 accessibilityLabel={_(msg`Cancel account deletion`)} 184 accessibilityHint="" 185 onAccessibilityEscape={onCancel}> 186 + <Text type="button-lg" style={textStyleLight}> 187 <Trans context="action">Cancel</Trans> 188 </Text> 189 </TouchableOpacity> ··· 199 a.mt_lg, 200 a.p_lg, 201 a.rounded_sm, 202 + theme.atoms.bg_contrast_25, 203 ]}> 204 <CircleInfo 205 size="md" ··· 225 {/* TODO: Update this label to be more concise */} 226 <Text 227 type="lg" 228 + style={[textStyle, styles.description]} 229 nativeID="confirmationCode"> 230 <Trans> 231 Check your inbox for an email with the confirmation code to ··· 233 </Trans> 234 </Text> 235 <TextInput 236 + style={[ 237 + styles.textInput, 238 + {borderColor: theme.palette.contrast_200}, 239 + textStyle, 240 + styles.mb20, 241 + ]} 242 placeholder={_(msg`Confirmation code`)} 243 + placeholderTextColor={textStyleLight.color} 244 + keyboardAppearance={colorScheme} 245 value={confirmCode} 246 onChangeText={setConfirmCode} 247 accessibilityLabelledBy="confirmationCode" ··· 252 /> 253 <Text 254 type="lg" 255 + style={[textStyle, styles.description]} 256 nativeID="password"> 257 <Trans>Please enter your password as well:</Trans> 258 </Text> 259 <TextInput 260 + style={[ 261 + styles.textInput, 262 + {borderColor: theme.palette.contrast_200}, 263 + textStyle, 264 + ]} 265 placeholder={_(msg`Password`)} 266 + placeholderTextColor={textStyleLight.color} 267 + keyboardAppearance={colorScheme} 268 secureTextEntry 269 value={password} 270 onChangeText={setPassword} ··· 300 accessibilityLabel={_(msg`Cancel account deletion`)} 301 accessibilityHint={_(msg`Exits account deletion process`)} 302 onAccessibilityEscape={onCancel}> 303 + <Text type="button-lg" style={textStyleLight}> 304 <Trans context="action">Cancel</Trans> 305 </Text> 306 </TouchableOpacity>
+55 -18
src/view/com/modals/InviteCodes.tsx
··· 2 import { 3 ActivityIndicator, 4 StyleSheet, 5 TouchableOpacity, 6 View, 7 } from 'react-native' 8 import {setStringAsync} from 'expo-clipboard' 9 import {type ComAtprotoServerDefs} from '@atproto/api' ··· 14 import {msg, Trans} from '@lingui/macro' 15 import {useLingui} from '@lingui/react' 16 17 - import {usePalette} from '#/lib/hooks/usePalette' 18 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 19 import {makeProfileLink} from '#/lib/routes/links' 20 import {cleanError} from '#/lib/strings/errors' ··· 25 type InviteCodesQueryResponse, 26 useInviteCodesQuery, 27 } from '#/state/queries/invites' 28 import {ErrorMessage} from '../util/error/ErrorMessage' 29 import {Button} from '../util/forms/Button' 30 import {Link} from '../util/Link' ··· 50 } 51 52 export function Inner({invites}: {invites: InviteCodesQueryResponse}) { 53 - const pal = usePalette('default') 54 const {_} = useLingui() 55 const {closeModal} = useModalControls() 56 const {isTabletOrDesktop} = useWebMediaQueries() ··· 59 closeModal() 60 }, [closeModal]) 61 62 if (invites.all.length === 0) { 63 return ( 64 - <View style={[styles.container, pal.view]} testID="inviteCodesModal"> 65 - <View style={[styles.empty, pal.viewLight]}> 66 - <Text type="lg" style={[pal.text, styles.emptyText]}> 67 <Trans> 68 You don't have any invite codes yet! We'll send you some when 69 you've been on Bluesky for a little longer. ··· 89 } 90 91 return ( 92 - <View style={[styles.container, pal.view]} testID="inviteCodesModal"> 93 - <Text type="title-xl" style={[styles.title, pal.text]}> 94 - <Trans>Invite a Friend</Trans> 95 </Text> 96 - <Text type="lg" style={[styles.description, pal.text]}> 97 <Trans> 98 Each code works once. You'll receive more invite codes periodically. 99 </Trans> 100 </Text> 101 - <ScrollView style={[styles.scrollContainer, pal.border]}> 102 {invites.available.map((invite, i) => ( 103 <InviteCode 104 testID={`inviteCode-${i}`} ··· 142 used?: boolean 143 invites: InviteCodesQueryResponse 144 }) { 145 - const pal = usePalette('default') 146 const {_} = useLingui() 147 const invitesState = useInvitesState() 148 const {setInviteCopied} = useInvitesAPI() ··· 154 setInviteCopied(invite.code) 155 }, [setInviteCopied, invite, _]) 156 157 return ( 158 <View 159 style={[ 160 - pal.border, 161 {borderBottomWidth: 1, paddingHorizontal: 20, paddingVertical: 14}, 162 ]}> 163 <TouchableOpacity ··· 174 <Text 175 testID={`${testID}-code`} 176 type={used ? 'md' : 'md-bold'} 177 - style={used ? [pal.textLight, styles.strikeThrough] : pal.text}> 178 {invite.code} 179 </Text> 180 <View style={styles.flex1} /> 181 {!used && invitesState.copiedInvites.includes(invite.code) && ( 182 - <Text style={[pal.textLight, styles.codeCopied]}> 183 <Trans>Copied</Trans> 184 </Text> 185 )} 186 {!used && ( 187 <FontAwesomeIcon 188 icon={['far', 'clone']} 189 - style={pal.text as FontAwesomeIconStyle} 190 /> 191 )} 192 </TouchableOpacity> ··· 197 gap: 8, 198 paddingTop: 6, 199 }}> 200 - <Text style={pal.text}> 201 <Trans>Used by:</Trans>{' '} 202 {uses.map((use, i) => ( 203 <Link ··· 206 style={{ 207 flexDirection: 'row', 208 }}> 209 - <UserInfoText did={use.usedBy} style={pal.link} /> 210 - {i !== uses.length - 1 && <Text style={pal.text}>, </Text>} 211 </Link> 212 ))} 213 </Text>
··· 2 import { 3 ActivityIndicator, 4 StyleSheet, 5 + type TextStyle, 6 TouchableOpacity, 7 View, 8 + type ViewStyle, 9 } from 'react-native' 10 import {setStringAsync} from 'expo-clipboard' 11 import {type ComAtprotoServerDefs} from '@atproto/api' ··· 16 import {msg, Trans} from '@lingui/macro' 17 import {useLingui} from '@lingui/react' 18 19 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 20 import {makeProfileLink} from '#/lib/routes/links' 21 import {cleanError} from '#/lib/strings/errors' ··· 26 type InviteCodesQueryResponse, 27 useInviteCodesQuery, 28 } from '#/state/queries/invites' 29 + import {useTheme} from '#/alf' 30 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 31 import {ErrorMessage} from '../util/error/ErrorMessage' 32 import {Button} from '../util/forms/Button' 33 import {Link} from '../util/Link' ··· 53 } 54 55 export function Inner({invites}: {invites: InviteCodesQueryResponse}) { 56 + const theme = useTheme() 57 + const colorMode = useColorModeTheme() 58 const {_} = useLingui() 59 const {closeModal} = useModalControls() 60 const {isTabletOrDesktop} = useWebMediaQueries() ··· 63 closeModal() 64 }, [closeModal]) 65 66 + const viewStyle: ViewStyle = { 67 + backgroundColor: 68 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 69 + } 70 + 71 + const viewStyleLight: ViewStyle = { 72 + backgroundColor: theme.palette.contrast_25, 73 + } 74 + 75 + const textStyle: TextStyle = { 76 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 77 + } 78 + 79 + const borderStyle: ViewStyle = { 80 + borderColor: theme.palette.contrast_100, 81 + } 82 + 83 if (invites.all.length === 0) { 84 return ( 85 + <View style={[styles.container, viewStyle]} testID="inviteCodesModal"> 86 + <View style={[styles.empty, viewStyleLight]}> 87 + <Text type="lg" style={[textStyle, styles.emptyText]}> 88 <Trans> 89 You don't have any invite codes yet! We'll send you some when 90 you've been on Bluesky for a little longer. ··· 110 } 111 112 return ( 113 + <View style={[styles.container, viewStyle]} testID="inviteCodesModal"> 114 + <Text type="title-xl" style={[styles.title, textStyle]}> 115 + <Trans>Invite a Friend </Trans> 116 </Text> 117 + <Text type="lg" style={[styles.description, textStyle]}> 118 <Trans> 119 Each code works once. You'll receive more invite codes periodically. 120 </Trans> 121 </Text> 122 + <ScrollView style={[styles.scrollContainer, borderStyle]}> 123 {invites.available.map((invite, i) => ( 124 <InviteCode 125 testID={`inviteCode-${i}`} ··· 163 used?: boolean 164 invites: InviteCodesQueryResponse 165 }) { 166 + const theme = useTheme() 167 + const colorMode = useColorModeTheme() 168 const {_} = useLingui() 169 const invitesState = useInvitesState() 170 const {setInviteCopied} = useInvitesAPI() ··· 176 setInviteCopied(invite.code) 177 }, [setInviteCopied, invite, _]) 178 179 + const textStyle: TextStyle = { 180 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 181 + } 182 + 183 + const textStyleLight: TextStyle = { 184 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 185 + } 186 + 187 + const borderStyle: ViewStyle = { 188 + borderColor: theme.palette.contrast_100, 189 + } 190 + 191 return ( 192 <View 193 style={[ 194 + borderStyle, 195 {borderBottomWidth: 1, paddingHorizontal: 20, paddingVertical: 14}, 196 ]}> 197 <TouchableOpacity ··· 208 <Text 209 testID={`${testID}-code`} 210 type={used ? 'md' : 'md-bold'} 211 + style={used ? [textStyleLight, styles.strikeThrough] : textStyle}> 212 {invite.code} 213 </Text> 214 <View style={styles.flex1} /> 215 {!used && invitesState.copiedInvites.includes(invite.code) && ( 216 + <Text style={[textStyleLight, styles.codeCopied]}> 217 <Trans>Copied</Trans> 218 </Text> 219 )} 220 {!used && ( 221 <FontAwesomeIcon 222 icon={['far', 'clone']} 223 + style={textStyle as FontAwesomeIconStyle} 224 /> 225 )} 226 </TouchableOpacity> ··· 231 gap: 8, 232 paddingTop: 6, 233 }}> 234 + <Text style={textStyle}> 235 <Trans>Used by:</Trans>{' '} 236 {uses.map((use, i) => ( 237 <Link ··· 240 style={{ 241 flexDirection: 'row', 242 }}> 243 + <UserInfoText 244 + did={use.usedBy} 245 + style={{color: theme.palette.primary_500}} 246 + /> 247 + {i !== uses.length - 1 && <Text style={textStyle}>, </Text>} 248 </Link> 249 ))} 250 </Text>
+17 -7
src/view/com/modals/Modal.tsx
··· 1 import {Fragment, useEffect, useRef} from 'react' 2 - import {StyleSheet} from 'react-native' 3 import {SafeAreaView} from 'react-native-safe-area-context' 4 import BottomSheet from '@discord/bottom-sheet/src' 5 6 - import {usePalette} from '#/lib/hooks/usePalette' 7 import {useModalControls, useModals} from '#/state/modals' 8 import {FullWindowOverlay} from '#/components/FullWindowOverlay' 9 import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' 10 import * as CreateOrEditListModal from './CreateOrEditList' ··· 20 const {isModalActive, activeModals} = useModals() 21 const {closeModal} = useModalControls() 22 const bottomSheetRef = useRef<BottomSheet>(null) 23 - const pal = usePalette('default') 24 const activeModal = activeModals[activeModals.length - 1] 25 26 const onBottomSheetChange = async (snapPoint: number) => { 27 if (snapPoint === -1) { 28 closeModal() ··· 65 66 if (snapPoints[0] === 'fullscreen') { 67 return ( 68 - <SafeAreaView style={[styles.fullscreenContainer, pal.view]}> 69 {element} 70 </SafeAreaView> 71 ) ··· 86 backdropComponent={ 87 isModalActive ? createCustomBackdrop(onClose) : undefined 88 } 89 - handleIndicatorStyle={{backgroundColor: pal.text.color}} 90 - handleStyle={[styles.handle, pal.view]} 91 - backgroundStyle={pal.view} 92 onChange={onBottomSheetChange}> 93 {element} 94 </BottomSheet>
··· 1 import {Fragment, useEffect, useRef} from 'react' 2 + import {StyleSheet, type ViewStyle} from 'react-native' 3 import {SafeAreaView} from 'react-native-safe-area-context' 4 import BottomSheet from '@discord/bottom-sheet/src' 5 6 import {useModalControls, useModals} from '#/state/modals' 7 + import {useTheme} from '#/alf' 8 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 9 import {FullWindowOverlay} from '#/components/FullWindowOverlay' 10 import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' 11 import * as CreateOrEditListModal from './CreateOrEditList' ··· 21 const {isModalActive, activeModals} = useModals() 22 const {closeModal} = useModalControls() 23 const bottomSheetRef = useRef<BottomSheet>(null) 24 + const theme = useTheme() 25 + const colorMode = useColorModeTheme() 26 const activeModal = activeModals[activeModals.length - 1] 27 28 + const viewStyle: ViewStyle = { 29 + backgroundColor: 30 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 31 + } 32 + 33 const onBottomSheetChange = async (snapPoint: number) => { 34 if (snapPoint === -1) { 35 closeModal() ··· 72 73 if (snapPoints[0] === 'fullscreen') { 74 return ( 75 + <SafeAreaView style={[styles.fullscreenContainer, viewStyle]}> 76 {element} 77 </SafeAreaView> 78 ) ··· 93 backdropComponent={ 94 isModalActive ? createCustomBackdrop(onClose) : undefined 95 } 96 + handleIndicatorStyle={{ 97 + backgroundColor: 98 + colorMode === 'light' ? theme.palette.black : theme.palette.white, 99 + }} 100 + handleStyle={[styles.handle, viewStyle]} 101 + backgroundStyle={viewStyle} 102 onChange={onBottomSheetChange}> 103 {element} 104 </BottomSheet>
+11 -4
src/view/com/modals/Modal.web.tsx
··· 2 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' 3 import {RemoveScrollBar} from 'react-remove-scroll-bar' 4 5 - import {usePalette} from '#/lib/hooks/usePalette' 6 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 7 import {type Modal as ModalIface} from '#/state/modals' 8 import {useModalControls, useModals} from '#/state/modals' 9 import * as CreateOrEditListModal from './CreateOrEditList' 10 import * as DeleteAccountModal from './DeleteAccount' 11 import * as InviteCodesModal from './InviteCodes' ··· 30 } 31 32 function Modal({modal}: {modal: ModalIface}) { 33 const {isModalActive} = useModals() 34 const {closeModal} = useModalControls() 35 - const pal = usePalette('default') 36 const {isMobile} = useWebMediaQueries() 37 38 if (!isModalActive) { ··· 75 style={[ 76 styles.container, 77 isMobile && styles.containerMobile, 78 - pal.view, 79 - pal.border, 80 ]}> 81 {element} 82 </View>
··· 2 import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' 3 import {RemoveScrollBar} from 'react-remove-scroll-bar' 4 5 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 6 import {type Modal as ModalIface} from '#/state/modals' 7 import {useModalControls, useModals} from '#/state/modals' 8 + import {useTheme} from '#/alf' 9 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 10 import * as CreateOrEditListModal from './CreateOrEditList' 11 import * as DeleteAccountModal from './DeleteAccount' 12 import * as InviteCodesModal from './InviteCodes' ··· 31 } 32 33 function Modal({modal}: {modal: ModalIface}) { 34 + const theme = useTheme() 35 + const colorMode = useColorModeTheme() 36 const {isModalActive} = useModals() 37 const {closeModal} = useModalControls() 38 const {isMobile} = useWebMediaQueries() 39 40 if (!isModalActive) { ··· 77 style={[ 78 styles.container, 79 isMobile && styles.containerMobile, 80 + { 81 + backgroundColor: 82 + colorMode === 'light' 83 + ? theme.palette.white 84 + : theme.palette.black, 85 + borderColor: theme.palette.contrast_100, 86 + }, 87 ]}> 88 {element} 89 </View>
+61 -12
src/view/com/modals/UserAddRemoveLists.tsx
··· 2 import { 3 ActivityIndicator, 4 StyleSheet, 5 useWindowDimensions, 6 View, 7 } from 'react-native' ··· 9 import {msg, Trans} from '@lingui/macro' 10 import {useLingui} from '@lingui/react' 11 12 - import {usePalette} from '#/lib/hooks/usePalette' 13 import {sanitizeDisplayName} from '#/lib/strings/display-names' 14 import {cleanError} from '#/lib/strings/errors' 15 import {sanitizeHandle} from '#/lib/strings/handles' ··· 24 useListMembershipRemoveMutation, 25 } from '#/state/queries/list-memberships' 26 import {useSession} from '#/state/session' 27 import {MyLists} from '../lists/MyLists' 28 import {Button} from '../util/forms/Button' 29 import {Text} from '../util/text/Text' ··· 45 onAdd?: (listUri: string) => void 46 onRemove?: (listUri: string) => void 47 }) { 48 const {closeModal} = useModalControls() 49 - const pal = usePalette('default') 50 const {height: screenHeight} = useWindowDimensions() 51 const {_} = useLingui() 52 const {data: memberships} = useDangerousListMembershipsQuery() 53 54 const onPressDone = useCallback(() => { 55 closeModal() 56 }, [closeModal]) 57 58 const listStyle = React.useMemo(() => { 59 if (isMobileWeb) { 60 - return [pal.border, {height: screenHeight / 2}] 61 } else if (isWeb) { 62 - return [pal.border, {height: screenHeight / 1.5}] 63 } 64 65 - return [pal.border, {flex: 1, borderTopWidth: StyleSheet.hairlineWidth}] 66 - }, [pal.border, screenHeight]) 67 68 const headerStyles = [ 69 { ··· 73 marginBottom: 12, 74 paddingHorizontal: 12, 75 } as const, 76 - pal.text, 77 ] 78 79 return ( ··· 104 )} 105 style={listStyle} 106 /> 107 - <View style={[styles.btns, pal.border]}> 108 <Button 109 testID="doneBtn" 110 type="default" ··· 137 onAdd?: (listUri: string) => void 138 onRemove?: (listUri: string) => void 139 }) { 140 - const pal = usePalette('default') 141 const {_} = useLingui() 142 const {currentAccount} = useSession() 143 const [isProcessing, setIsProcessing] = React.useState(false) ··· 192 testID={`toggleBtn-${list.name}`} 193 style={[ 194 styles.listItem, 195 - pal.border, 196 index !== 0 && {borderTopWidth: StyleSheet.hairlineWidth}, 197 ]}> 198 <View style={styles.listItemAvi}> ··· 201 <View style={styles.listItemContent}> 202 <Text 203 type="lg" 204 - style={[s.bold, pal.text]} 205 numberOfLines={1} 206 lineHeight={1.2}> 207 {sanitizeDisplayName(list.name)} 208 </Text> 209 - <Text type="md" style={[pal.textLight]} numberOfLines={1}> 210 {list.purpose === 'app.bsky.graph.defs#curatelist' && 211 (list.creator.did === currentAccount?.did ? ( 212 <Trans>User list by you</Trans>
··· 2 import { 3 ActivityIndicator, 4 StyleSheet, 5 + type TextStyle, 6 useWindowDimensions, 7 View, 8 } from 'react-native' ··· 10 import {msg, Trans} from '@lingui/macro' 11 import {useLingui} from '@lingui/react' 12 13 import {sanitizeDisplayName} from '#/lib/strings/display-names' 14 import {cleanError} from '#/lib/strings/errors' 15 import {sanitizeHandle} from '#/lib/strings/handles' ··· 24 useListMembershipRemoveMutation, 25 } from '#/state/queries/list-memberships' 26 import {useSession} from '#/state/session' 27 + import {useTheme} from '#/alf' 28 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 29 import {MyLists} from '../lists/MyLists' 30 import {Button} from '../util/forms/Button' 31 import {Text} from '../util/text/Text' ··· 47 onAdd?: (listUri: string) => void 48 onRemove?: (listUri: string) => void 49 }) { 50 + const theme = useTheme() 51 + const colorMode = useColorModeTheme() 52 const {closeModal} = useModalControls() 53 const {height: screenHeight} = useWindowDimensions() 54 const {_} = useLingui() 55 const {data: memberships} = useDangerousListMembershipsQuery() 56 57 + const textStyle: TextStyle = { 58 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 59 + } 60 + 61 const onPressDone = useCallback(() => { 62 closeModal() 63 }, [closeModal]) 64 65 const listStyle = React.useMemo(() => { 66 if (isMobileWeb) { 67 + return [ 68 + { 69 + borderColor: theme.palette.contrast_100, 70 + }, 71 + {height: screenHeight / 2}, 72 + ] 73 } else if (isWeb) { 74 + return [ 75 + { 76 + borderColor: theme.palette.contrast_100, 77 + }, 78 + {height: screenHeight / 1.5}, 79 + ] 80 } 81 82 + return [ 83 + { 84 + borderColor: theme.palette.contrast_100, 85 + }, 86 + {flex: 1, borderTopWidth: StyleSheet.hairlineWidth}, 87 + ] 88 + }, [theme, screenHeight]) 89 90 const headerStyles = [ 91 { ··· 95 marginBottom: 12, 96 paddingHorizontal: 12, 97 } as const, 98 + textStyle, 99 ] 100 101 return ( ··· 126 )} 127 style={listStyle} 128 /> 129 + <View 130 + style={[ 131 + styles.btns, 132 + { 133 + borderColor: theme.palette.contrast_100, 134 + }, 135 + ]}> 136 <Button 137 testID="doneBtn" 138 type="default" ··· 165 onAdd?: (listUri: string) => void 166 onRemove?: (listUri: string) => void 167 }) { 168 + const theme = useTheme() 169 + const colorMode = useColorModeTheme() 170 const {_} = useLingui() 171 const {currentAccount} = useSession() 172 const [isProcessing, setIsProcessing] = React.useState(false) ··· 221 testID={`toggleBtn-${list.name}`} 222 style={[ 223 styles.listItem, 224 + { 225 + borderColor: theme.palette.contrast_100, 226 + }, 227 index !== 0 && {borderTopWidth: StyleSheet.hairlineWidth}, 228 ]}> 229 <View style={styles.listItemAvi}> ··· 232 <View style={styles.listItemContent}> 233 <Text 234 type="lg" 235 + style={[ 236 + s.bold, 237 + { 238 + color: 239 + colorMode === 'light' 240 + ? theme.palette.black 241 + : theme.palette.white, 242 + }, 243 + ]} 244 numberOfLines={1} 245 lineHeight={1.2}> 246 {sanitizeDisplayName(list.name)} 247 </Text> 248 + <Text 249 + type="md" 250 + style={[ 251 + { 252 + color: 253 + colorMode === 'light' 254 + ? theme.palette.white 255 + : theme.palette.black, 256 + }, 257 + ]} 258 + numberOfLines={1}> 259 {list.purpose === 'app.bsky.graph.defs#curatelist' && 260 (list.creator.did === currentAccount?.did ? ( 261 <Trans>User list by you</Trans>
+3 -3
src/view/com/modals/lang-settings/ConfirmLanguagesButton.tsx
··· 3 import {msg, Trans} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 - import {usePalette} from '#/lib/hooks/usePalette' 7 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 8 import {colors, gradients, s} from '#/lib/styles' 9 10 export const ConfirmLanguagesButton = ({ 11 onPress, ··· 14 onPress: () => void 15 extraText?: string 16 }) => { 17 - const pal = usePalette('default') 18 const {_} = useLingui() 19 const {isMobile} = useWebMediaQueries() 20 return ( 21 <View 22 style={[ 23 styles.btnContainer, 24 - pal.borderDark, 25 isMobile && { 26 paddingBottom: 40, 27 borderTopWidth: 1,
··· 3 import {msg, Trans} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 7 import {colors, gradients, s} from '#/lib/styles' 8 + import {useTheme} from '#/alf' 9 10 export const ConfirmLanguagesButton = ({ 11 onPress, ··· 14 onPress: () => void 15 extraText?: string 16 }) => { 17 + const theme = useTheme() 18 const {_} = useLingui() 19 const {isMobile} = useWebMediaQueries() 20 return ( 21 <View 22 style={[ 23 styles.btnContainer, 24 + {borderColor: theme.palette.contrast_200}, 25 isMobile && { 26 paddingBottom: 40, 27 borderTopWidth: 1,
+22 -7
src/view/com/modals/lang-settings/ContentLanguagesSettings.tsx
··· 1 import React from 'react' 2 - import {StyleSheet, View} from 'react-native' 3 import {Trans} from '@lingui/macro' 4 5 - import {usePalette} from '#/lib/hooks/usePalette' 6 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 7 import {deviceLanguageCodes} from '#/locale/deviceLocales' 8 import {languageName} from '#/locale/helpers' ··· 11 useLanguagePrefs, 12 useLanguagePrefsApi, 13 } from '#/state/preferences/languages' 14 import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages' 15 import {Text} from '../../util/text/Text' 16 import {ScrollView} from '../util' ··· 23 const {closeModal} = useModalControls() 24 const langPrefs = useLanguagePrefs() 25 const setLangPrefs = useLanguagePrefsApi() 26 - const pal = usePalette('default') 27 const {isMobile} = useWebMediaQueries() 28 const onPressDone = React.useCallback(() => { 29 closeModal() 30 }, [closeModal]) 31 32 const languages = React.useMemo(() => { 33 const langs = LANGUAGES.filter( 34 lang => ··· 61 <View 62 testID="contentLanguagesModal" 63 style={[ 64 - pal.view, 65 styles.container, 66 // @ts-ignore vh is web only 67 isMobile ··· 72 maxHeight: '90vh', 73 }, 74 ]}> 75 - <Text style={[pal.text, styles.title]}> 76 <Trans>Content Languages</Trans> 77 </Text> 78 - <Text style={[pal.text, styles.description]}> 79 <Trans> 80 Which languages would you like to see in your algorithmic feeds? 81 </Trans> 82 </Text> 83 - <Text style={[pal.textLight, styles.description]}> 84 <Trans>Leave them all unselected to see any language.</Trans> 85 </Text> 86 <ScrollView style={styles.scrollContainer}>
··· 1 import React from 'react' 2 + import {StyleSheet, type TextStyle, View, type ViewStyle} from 'react-native' 3 import {Trans} from '@lingui/macro' 4 5 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 6 import {deviceLanguageCodes} from '#/locale/deviceLocales' 7 import {languageName} from '#/locale/helpers' ··· 10 useLanguagePrefs, 11 useLanguagePrefsApi, 12 } from '#/state/preferences/languages' 13 + import {useTheme} from '#/alf' 14 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 15 import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages' 16 import {Text} from '../../util/text/Text' 17 import {ScrollView} from '../util' ··· 24 const {closeModal} = useModalControls() 25 const langPrefs = useLanguagePrefs() 26 const setLangPrefs = useLanguagePrefsApi() 27 + const theme = useTheme() 28 + const colorMode = useColorModeTheme() 29 const {isMobile} = useWebMediaQueries() 30 const onPressDone = React.useCallback(() => { 31 closeModal() 32 }, [closeModal]) 33 34 + const viewStyle: ViewStyle = { 35 + backgroundColor: 36 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 37 + } 38 + 39 + const textStyle: TextStyle = { 40 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 41 + } 42 + 43 + const textStyleLight: TextStyle = { 44 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 45 + } 46 + 47 const languages = React.useMemo(() => { 48 const langs = LANGUAGES.filter( 49 lang => ··· 76 <View 77 testID="contentLanguagesModal" 78 style={[ 79 + viewStyle, 80 styles.container, 81 // @ts-ignore vh is web only 82 isMobile ··· 87 maxHeight: '90vh', 88 }, 89 ]}> 90 + <Text style={[textStyle, styles.title]}> 91 <Trans>Content Languages</Trans> 92 </Text> 93 + <Text style={[textStyle, styles.description]}> 94 <Trans> 95 Which languages would you like to see in your algorithmic feeds? 96 </Trans> 97 </Text> 98 + <Text style={[textStyleLight, styles.description]}> 99 <Trans>Leave them all unselected to see any language.</Trans> 100 </Text> 101 <ScrollView style={styles.scrollContainer}>
+7 -3
src/view/com/modals/lang-settings/LanguageToggle.tsx
··· 1 import {StyleSheet} from 'react-native' 2 3 - import {usePalette} from '#/lib/hooks/usePalette' 4 import {toPostLanguages, useLanguagePrefs} from '#/state/preferences/languages' 5 import {ToggleButton} from '#/view/com/util/forms/ToggleButton' 6 7 export function LanguageToggle({ 8 code2, ··· 15 onPress: () => void 16 langType: 'contentLanguages' | 'postLanguages' 17 }) { 18 - const pal = usePalette('default') 19 const langPrefs = useLanguagePrefs() 20 21 const values = ··· 35 label={name} 36 isSelected={isSelected} 37 onPress={isDisabled ? undefined : onPress} 38 - style={[pal.border, styles.languageToggle, isDisabled && styles.dimmed]} 39 /> 40 ) 41 }
··· 1 import {StyleSheet} from 'react-native' 2 3 import {toPostLanguages, useLanguagePrefs} from '#/state/preferences/languages' 4 import {ToggleButton} from '#/view/com/util/forms/ToggleButton' 5 + import {useTheme} from '#/alf' 6 7 export function LanguageToggle({ 8 code2, ··· 15 onPress: () => void 16 langType: 'contentLanguages' | 'postLanguages' 17 }) { 18 + const theme = useTheme() 19 const langPrefs = useLanguagePrefs() 20 21 const values = ··· 35 label={name} 36 isSelected={isSelected} 37 onPress={isDisabled ? undefined : onPress} 38 + style={[ 39 + {borderColor: theme.palette.contrast_100}, 40 + styles.languageToggle, 41 + isDisabled && styles.dimmed, 42 + ]} 43 /> 44 ) 45 }
+39 -20
src/view/com/notifications/NotificationFeedItem.tsx
··· 33 import {MAX_POST_LINES} from '#/lib/constants' 34 import {DM_SERVICE_HEADERS} from '#/lib/constants' 35 import {useAnimatedValue} from '#/lib/hooks/useAnimatedValue' 36 - import {usePalette} from '#/lib/hooks/usePalette' 37 import {makeProfileLink} from '#/lib/routes/links' 38 import {type NavigationProp} from '#/lib/routes/types' 39 import {forceLTR} from '#/lib/strings/bidi' ··· 51 import {TimeElapsed} from '#/view/com/util/TimeElapsed' 52 import {PreviewableUserAvatar, UserAvatar} from '#/view/com/util/UserAvatar' 53 import {atoms as a, platform, useTheme} from '#/alf' 54 import {Button, ButtonText} from '#/components/Button' 55 import {BellRinging_Filled_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging' 56 import { ··· 94 hideTopBorder?: boolean 95 }): React.ReactNode => { 96 const queryClient = useQueryClient() 97 - const pal = usePalette('default') 98 const t = useTheme() 99 const {_, i18n} = useLingui() 100 const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false) ··· 200 post={item.subject} 201 style={ 202 isHighlighted && { 203 - backgroundColor: pal.colors.unreadNotifBg, 204 - borderColor: pal.colors.unreadNotifBorder, 205 } 206 } 207 hideTopBorder={hideTopBorder} ··· 281 liked your post 282 </Trans> 283 ) : ( 284 - <Trans>{firstAuthorLink} liked your post</Trans> 285 ) 286 } else if (item.type === 'repost') { 287 a11yLabel = hasMultipleAuthors ··· 305 reposted your post 306 </Trans> 307 ) : ( 308 - <Trans>{firstAuthorLink} reposted your post</Trans> 309 ) 310 icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} /> 311 } else if (item.type === 'follow') { ··· 339 * see `src/state/queries/notifications/util.ts` 340 */ 341 a11yLabel = _(msg`${firstAuthorName} followed you back`) 342 - notificationContent = <Trans>{firstAuthorLink} followed you back</Trans> 343 } else { 344 a11yLabel = hasMultipleAuthors 345 ? _( ··· 362 followed you 363 </Trans> 364 ) : ( 365 - <Trans>{firstAuthorLink} followed you</Trans> 366 ) 367 } 368 icon = <PersonPlusIcon size="xl" style={{color: t.palette.primary_500}} /> ··· 388 liked your custom feed 389 </Trans> 390 ) : ( 391 - <Trans>{firstAuthorLink} liked your custom feed</Trans> 392 ) 393 } else if (item.type === 'starterpack-joined') { 394 a11yLabel = hasMultipleAuthors ··· 412 signed up with your starter pack 413 </Trans> 414 ) : ( 415 - <Trans>{firstAuthorLink} signed up with your starter pack</Trans> 416 ) 417 icon = ( 418 <View style={{height: 30, width: 30}}> ··· 431 notificationContent = hasMultipleAuthors ? ( 432 <Trans> 433 {firstAuthorLink} and{' '} 434 - <Text style={[pal.text, s.bold]}> 435 <Plural 436 value={additionalAuthorsCount} 437 one={`${formattedAuthorsCount} other`} ··· 441 verified you 442 </Trans> 443 ) : ( 444 - <Trans>{firstAuthorLink} verified you</Trans> 445 ) 446 icon = <VerifiedCheck size="xl" /> 447 } else if (item.type === 'unverified') { ··· 456 notificationContent = hasMultipleAuthors ? ( 457 <Trans> 458 {firstAuthorLink} and{' '} 459 - <Text style={[pal.text, s.bold]}> 460 <Plural 461 value={additionalAuthorsCount} 462 one={`${formattedAuthorsCount} other`} ··· 493 liked your repost 494 </Trans> 495 ) : ( 496 - <Trans>{firstAuthorLink} liked your repost</Trans> 497 ) 498 } else if (item.type === 'repost-via-repost') { 499 a11yLabel = hasMultipleAuthors ··· 517 reposted your repost 518 </Trans> 519 ) : ( 520 - <Trans>{firstAuthorLink} reposted your repost</Trans> 521 ) 522 icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} /> 523 } else if (item.type === 'subscribed-post') { ··· 574 item.notification.isRead 575 ? undefined 576 : { 577 - backgroundColor: pal.colors.unreadNotifBg, 578 - borderColor: pal.colors.unreadNotifBorder, 579 }, 580 !hideTopBorder && a.border_t, 581 a.overflow_hidden, ··· 728 </Pressable> 729 ) 730 } else { 731 - return <>{children}</> 732 } 733 } 734 ··· 773 } 774 }}> 775 <ButtonText> 776 - <Trans>Say hello!</Trans> 777 </ButtonText> 778 </Button> 779 ) ··· 809 style={[a.ml_xs, a.mr_md, t.atoms.text_contrast_high]} 810 /> 811 <Text style={[a.text_md, t.atoms.text_contrast_high]}> 812 - <Trans context="action">Hide</Trans> 813 </Text> 814 </TouchableOpacity> 815 </View>
··· 33 import {MAX_POST_LINES} from '#/lib/constants' 34 import {DM_SERVICE_HEADERS} from '#/lib/constants' 35 import {useAnimatedValue} from '#/lib/hooks/useAnimatedValue' 36 import {makeProfileLink} from '#/lib/routes/links' 37 import {type NavigationProp} from '#/lib/routes/types' 38 import {forceLTR} from '#/lib/strings/bidi' ··· 50 import {TimeElapsed} from '#/view/com/util/TimeElapsed' 51 import {PreviewableUserAvatar, UserAvatar} from '#/view/com/util/UserAvatar' 52 import {atoms as a, platform, useTheme} from '#/alf' 53 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 54 import {Button, ButtonText} from '#/components/Button' 55 import {BellRinging_Filled_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging' 56 import { ··· 94 hideTopBorder?: boolean 95 }): React.ReactNode => { 96 const queryClient = useQueryClient() 97 + const theme = useTheme() 98 + const colorMode = useColorModeTheme() 99 const t = useTheme() 100 const {_, i18n} = useLingui() 101 const [isAuthorsExpanded, setAuthorsExpanded] = useState<boolean>(false) ··· 201 post={item.subject} 202 style={ 203 isHighlighted && { 204 + backgroundColor: theme.palette.primary_25, 205 + borderColor: theme.palette.primary_100, 206 } 207 } 208 hideTopBorder={hideTopBorder} ··· 282 liked your post 283 </Trans> 284 ) : ( 285 + <Trans>{firstAuthorLink} liked your post </Trans> 286 ) 287 } else if (item.type === 'repost') { 288 a11yLabel = hasMultipleAuthors ··· 306 reposted your post 307 </Trans> 308 ) : ( 309 + <Trans>{firstAuthorLink} reposted your post </Trans> 310 ) 311 icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} /> 312 } else if (item.type === 'follow') { ··· 340 * see `src/state/queries/notifications/util.ts` 341 */ 342 a11yLabel = _(msg`${firstAuthorName} followed you back`) 343 + notificationContent = <Trans>{firstAuthorLink} followed you back </Trans> 344 } else { 345 a11yLabel = hasMultipleAuthors 346 ? _( ··· 363 followed you 364 </Trans> 365 ) : ( 366 + <Trans>{firstAuthorLink} followed you </Trans> 367 ) 368 } 369 icon = <PersonPlusIcon size="xl" style={{color: t.palette.primary_500}} /> ··· 389 liked your custom feed 390 </Trans> 391 ) : ( 392 + <Trans>{firstAuthorLink} liked your custom feed </Trans> 393 ) 394 } else if (item.type === 'starterpack-joined') { 395 a11yLabel = hasMultipleAuthors ··· 413 signed up with your starter pack 414 </Trans> 415 ) : ( 416 + <Trans>{firstAuthorLink} signed up with your starter pack </Trans> 417 ) 418 icon = ( 419 <View style={{height: 30, width: 30}}> ··· 432 notificationContent = hasMultipleAuthors ? ( 433 <Trans> 434 {firstAuthorLink} and{' '} 435 + <Text 436 + style={[ 437 + { 438 + color: 439 + colorMode === 'light' 440 + ? theme.palette.black 441 + : theme.palette.white, 442 + }, 443 + s.bold, 444 + ]}> 445 <Plural 446 value={additionalAuthorsCount} 447 one={`${formattedAuthorsCount} other`} ··· 451 verified you 452 </Trans> 453 ) : ( 454 + <Trans>{firstAuthorLink} verified you </Trans> 455 ) 456 icon = <VerifiedCheck size="xl" /> 457 } else if (item.type === 'unverified') { ··· 466 notificationContent = hasMultipleAuthors ? ( 467 <Trans> 468 {firstAuthorLink} and{' '} 469 + <Text 470 + style={[ 471 + { 472 + color: 473 + colorMode === 'light' 474 + ? theme.palette.black 475 + : theme.palette.white, 476 + }, 477 + s.bold, 478 + ]}> 479 <Plural 480 value={additionalAuthorsCount} 481 one={`${formattedAuthorsCount} other`} ··· 512 liked your repost 513 </Trans> 514 ) : ( 515 + <Trans>{firstAuthorLink} liked your repost </Trans> 516 ) 517 } else if (item.type === 'repost-via-repost') { 518 a11yLabel = hasMultipleAuthors ··· 536 reposted your repost 537 </Trans> 538 ) : ( 539 + <Trans>{firstAuthorLink} reposted your repost </Trans> 540 ) 541 icon = <RepostIcon size="xl" style={{color: t.palette.positive_600}} /> 542 } else if (item.type === 'subscribed-post') { ··· 593 item.notification.isRead 594 ? undefined 595 : { 596 + backgroundColor: theme.palette.primary_25, 597 + borderColor: theme.palette.primary_100, 598 }, 599 !hideTopBorder && a.border_t, 600 a.overflow_hidden, ··· 747 </Pressable> 748 ) 749 } else { 750 + return <>{children} </> 751 } 752 } 753 ··· 792 } 793 }}> 794 <ButtonText> 795 + <Trans>Say hello! </Trans> 796 </ButtonText> 797 </Button> 798 ) ··· 828 style={[a.ml_xs, a.mr_md, t.atoms.text_contrast_high]} 829 /> 830 <Text style={[a.text_md, t.atoms.text_contrast_high]}> 831 + <Trans context="action"> Hide </Trans> 832 </Text> 833 </TouchableOpacity> 834 </View>
+3 -4
src/view/com/post/Post.tsx
··· 12 13 import {MAX_POST_LINES} from '#/lib/constants' 14 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 15 - import {usePalette} from '#/lib/hooks/usePalette' 16 import {makeProfileLink} from '#/lib/routes/links' 17 import {countLines} from '#/lib/strings/helpers' 18 import {colors} from '#/lib/styles' ··· 26 import {Link} from '#/view/com/util/Link' 27 import {PostMeta} from '#/view/com/util/PostMeta' 28 import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 29 - import {atoms as a} from '#/alf' 30 import {ContentHider} from '#/components/moderation/ContentHider' 31 import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe' 32 import {PostAlerts} from '#/components/moderation/PostAlerts' ··· 114 onBeforePress?: () => void 115 }) { 116 const queryClient = useQueryClient() 117 - const pal = usePalette('default') 118 const {openComposer} = useOpenComposer() 119 const [limitLines, setLimitLines] = useState( 120 () => countLines(richText?.text) >= MAX_POST_LINES, ··· 156 href={itemHref} 157 style={[ 158 styles.outer, 159 - pal.border, 160 !hideTopBorder && {borderTopWidth: StyleSheet.hairlineWidth}, 161 style, 162 ]}
··· 12 13 import {MAX_POST_LINES} from '#/lib/constants' 14 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 15 import {makeProfileLink} from '#/lib/routes/links' 16 import {countLines} from '#/lib/strings/helpers' 17 import {colors} from '#/lib/styles' ··· 25 import {Link} from '#/view/com/util/Link' 26 import {PostMeta} from '#/view/com/util/PostMeta' 27 import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 28 + import {atoms as a, useTheme} from '#/alf' 29 import {ContentHider} from '#/components/moderation/ContentHider' 30 import {LabelsOnMyPost} from '#/components/moderation/LabelsOnMe' 31 import {PostAlerts} from '#/components/moderation/PostAlerts' ··· 113 onBeforePress?: () => void 114 }) { 115 const queryClient = useQueryClient() 116 + const theme = useTheme() 117 const {openComposer} = useOpenComposer() 118 const [limitLines, setLimitLines] = useState( 119 () => countLines(richText?.text) >= MAX_POST_LINES, ··· 155 href={itemHref} 156 style={[ 157 styles.outer, 158 + {borderColor: theme.palette.contrast_100}, 159 !hideTopBorder && {borderTopWidth: StyleSheet.hairlineWidth}, 160 style, 161 ]}
+15 -8
src/view/com/posts/CustomFeedEmptyState.tsx
··· 1 import React from 'react' 2 - import {StyleSheet, View} from 'react-native' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, ··· 7 import {Trans} from '@lingui/macro' 8 import {useNavigation} from '@react-navigation/native' 9 10 - import {usePalette} from '#/lib/hooks/usePalette' 11 import {MagnifyingGlassIcon} from '#/lib/icons' 12 import {type NavigationProp} from '#/lib/routes/types' 13 import {s} from '#/lib/styles' 14 import {isWeb} from '#/platform/detection' 15 import {Button} from '../util/forms/Button' 16 import {Text} from '../util/text/Text' 17 18 export function CustomFeedEmptyState() { 19 - const pal = usePalette('default') 20 - const palInverted = usePalette('inverted') 21 const navigation = useNavigation<NavigationProp>() 22 23 const onPressFindAccounts = React.useCallback(() => { 24 if (isWeb) { ··· 32 return ( 33 <View style={styles.emptyContainer}> 34 <View style={styles.emptyIconContainer}> 35 - <MagnifyingGlassIcon style={[styles.emptyIcon, pal.text]} size={62} /> 36 </View> 37 - <Text type="xl-medium" style={[s.textCenter, pal.text]}> 38 <Trans> 39 This feed is empty! You may need to follow more users or tune your 40 language settings. ··· 44 type="inverted" 45 style={styles.emptyBtn} 46 onPress={onPressFindAccounts}> 47 - <Text type="lg-medium" style={palInverted.text}> 48 <Trans>Find accounts to follow</Trans> 49 </Text> 50 <FontAwesomeIcon 51 icon="angle-right" 52 - style={palInverted.text as FontAwesomeIconStyle} 53 size={14} 54 /> 55 </Button>
··· 1 import React from 'react' 2 + import {StyleSheet, type TextStyle, View} from 'react-native' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, ··· 7 import {Trans} from '@lingui/macro' 8 import {useNavigation} from '@react-navigation/native' 9 10 import {MagnifyingGlassIcon} from '#/lib/icons' 11 import {type NavigationProp} from '#/lib/routes/types' 12 import {s} from '#/lib/styles' 13 import {isWeb} from '#/platform/detection' 14 + import {useTheme} from '#/alf' 15 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 16 import {Button} from '../util/forms/Button' 17 import {Text} from '../util/text/Text' 18 19 export function CustomFeedEmptyState() { 20 + const theme = useTheme() 21 + const colorMode = useColorModeTheme() 22 const navigation = useNavigation<NavigationProp>() 23 + const textStyle: TextStyle = { 24 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 25 + } 26 + const textStyleInverted: TextStyle = { 27 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 28 + } 29 30 const onPressFindAccounts = React.useCallback(() => { 31 if (isWeb) { ··· 39 return ( 40 <View style={styles.emptyContainer}> 41 <View style={styles.emptyIconContainer}> 42 + <MagnifyingGlassIcon style={[styles.emptyIcon, textStyle]} size={62} /> 43 </View> 44 + <Text type="xl-medium" style={[s.textCenter, textStyle]}> 45 <Trans> 46 This feed is empty! You may need to follow more users or tune your 47 language settings. ··· 51 type="inverted" 52 style={styles.emptyBtn} 53 onPress={onPressFindAccounts}> 54 + <Text type="lg-medium" style={textStyleInverted}> 55 <Trans>Find accounts to follow</Trans> 56 </Text> 57 <FontAwesomeIcon 58 icon="angle-right" 59 + style={textStyleInverted as FontAwesomeIconStyle} 60 size={14} 61 /> 62 </Button>
+23 -7
src/view/com/posts/DiscoverFallbackHeader.tsx
··· 1 import {View} from 'react-native' 2 import {Trans} from '@lingui/macro' 3 4 - import {usePalette} from '#/lib/hooks/usePalette' 5 import {InfoCircleIcon} from '#/lib/icons' 6 import {TextLink} from '../util/Link' 7 import {Text} from '../util/text/Text' 8 9 export function DiscoverFallbackHeader() { 10 - const pal = usePalette('default') 11 return ( 12 <View 13 style={[ ··· 17 paddingVertical: 12, 18 paddingHorizontal: 12, 19 borderTopWidth: 1, 20 }, 21 - pal.border, 22 - pal.viewLight, 23 ]}> 24 <View style={{width: 68, paddingLeft: 12}}> 25 - <InfoCircleIcon size={36} style={pal.textLight} strokeWidth={1.5} /> 26 </View> 27 <View style={{flex: 1}}> 28 - <Text type="md" style={pal.text}> 29 <Trans> 30 We ran out of posts from your follows. Here's the latest from{' '} 31 <TextLink 32 type="md-medium" 33 href="/profile/bsky.app/feed/whats-hot" 34 text="Discover" 35 - style={pal.link} 36 /> 37 . 38 </Trans>
··· 1 import {View} from 'react-native' 2 import {Trans} from '@lingui/macro' 3 4 import {InfoCircleIcon} from '#/lib/icons' 5 + import {useTheme} from '#/alf' 6 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 7 import {TextLink} from '../util/Link' 8 import {Text} from '../util/text/Text' 9 10 export function DiscoverFallbackHeader() { 11 + const theme = useTheme() 12 + const colorMode = useColorModeTheme() 13 return ( 14 <View 15 style={[ ··· 19 paddingVertical: 12, 20 paddingHorizontal: 12, 21 borderTopWidth: 1, 22 + borderColor: theme.palette.contrast_100, 23 + backgroundColor: theme.palette.contrast_25, 24 }, 25 ]}> 26 <View style={{width: 68, paddingLeft: 12}}> 27 + <InfoCircleIcon 28 + size={36} 29 + style={{ 30 + color: 31 + colorMode === 'dark' 32 + ? theme.palette.contrast_600 33 + : theme.palette.contrast_700, 34 + }} 35 + strokeWidth={1.5} 36 + /> 37 </View> 38 <View style={{flex: 1}}> 39 + <Text 40 + type="md" 41 + style={{ 42 + color: 43 + colorMode === 'light' ? theme.palette.black : theme.palette.white, 44 + }}> 45 <Trans> 46 We ran out of posts from your follows. Here's the latest from{' '} 47 <TextLink 48 type="md-medium" 49 href="/profile/bsky.app/feed/whats-hot" 50 text="Discover" 51 + style={{color: theme.palette.primary_500}} 52 /> 53 . 54 </Trans>
+21 -13
src/view/com/posts/FollowingEmptyState.tsx
··· 1 import React from 'react' 2 - import {StyleSheet, View} from 'react-native' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, ··· 7 import {Trans} from '@lingui/macro' 8 import {useNavigation} from '@react-navigation/native' 9 10 - import {usePalette} from '#/lib/hooks/usePalette' 11 import {MagnifyingGlassIcon} from '#/lib/icons' 12 import {type NavigationProp} from '#/lib/routes/types' 13 import {s} from '#/lib/styles' 14 import {isWeb} from '#/platform/detection' 15 import {Button} from '../util/forms/Button' 16 import {Text} from '../util/text/Text' 17 18 export function FollowingEmptyState() { 19 - const pal = usePalette('default') 20 - const palInverted = usePalette('inverted') 21 const navigation = useNavigation<NavigationProp>() 22 23 const onPressFindAccounts = React.useCallback(() => { 24 if (isWeb) { 25 navigation.navigate('Search', {}) ··· 37 <View style={styles.container}> 38 <View style={styles.inner}> 39 <View style={styles.iconContainer}> 40 - <MagnifyingGlassIcon style={[styles.icon, pal.text]} size={62} /> 41 </View> 42 - <Text type="xl-medium" style={[s.textCenter, pal.text]}> 43 <Trans> 44 Your following feed is empty! Follow more users to see what's 45 happening. ··· 49 type="inverted" 50 style={styles.emptyBtn} 51 onPress={onPressFindAccounts}> 52 - <Text type="lg-medium" style={palInverted.text}> 53 - <Trans>Find accounts to follow</Trans> 54 </Text> 55 <FontAwesomeIcon 56 icon="angle-right" 57 - style={palInverted.text as FontAwesomeIconStyle} 58 size={14} 59 /> 60 </Button> 61 62 - <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}> 63 <Trans>You can also discover new Custom Feeds to follow.</Trans> 64 </Text> 65 <Button 66 type="inverted" 67 style={[styles.emptyBtn, s.mt10]} 68 onPress={onPressDiscoverFeeds}> 69 - <Text type="lg-medium" style={palInverted.text}> 70 - <Trans>Discover new custom feeds</Trans> 71 </Text> 72 <FontAwesomeIcon 73 icon="angle-right" 74 - style={palInverted.text as FontAwesomeIconStyle} 75 size={14} 76 /> 77 </Button>
··· 1 import React from 'react' 2 + import {StyleSheet, type TextStyle, View} from 'react-native' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, ··· 7 import {Trans} from '@lingui/macro' 8 import {useNavigation} from '@react-navigation/native' 9 10 import {MagnifyingGlassIcon} from '#/lib/icons' 11 import {type NavigationProp} from '#/lib/routes/types' 12 import {s} from '#/lib/styles' 13 import {isWeb} from '#/platform/detection' 14 + import {useTheme} from '#/alf' 15 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 16 import {Button} from '../util/forms/Button' 17 import {Text} from '../util/text/Text' 18 19 export function FollowingEmptyState() { 20 + const theme = useTheme() 21 + const colorMode = useColorModeTheme() 22 const navigation = useNavigation<NavigationProp>() 23 24 + const textStyle: TextStyle = { 25 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 26 + } 27 + const textStyleInverted: TextStyle = { 28 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 29 + } 30 + 31 const onPressFindAccounts = React.useCallback(() => { 32 if (isWeb) { 33 navigation.navigate('Search', {}) ··· 45 <View style={styles.container}> 46 <View style={styles.inner}> 47 <View style={styles.iconContainer}> 48 + <MagnifyingGlassIcon style={[styles.icon, textStyle]} size={62} /> 49 </View> 50 + <Text type="xl-medium" style={[s.textCenter, textStyle]}> 51 <Trans> 52 Your following feed is empty! Follow more users to see what's 53 happening. ··· 57 type="inverted" 58 style={styles.emptyBtn} 59 onPress={onPressFindAccounts}> 60 + <Text type="lg-medium" style={textStyleInverted}> 61 + <Trans>Find accounts to follow </Trans> 62 </Text> 63 <FontAwesomeIcon 64 icon="angle-right" 65 + style={textStyleInverted as FontAwesomeIconStyle} 66 size={14} 67 /> 68 </Button> 69 70 + <Text type="xl-medium" style={[s.textCenter, textStyle, s.mt20]}> 71 <Trans>You can also discover new Custom Feeds to follow.</Trans> 72 </Text> 73 <Button 74 type="inverted" 75 style={[styles.emptyBtn, s.mt10]} 76 onPress={onPressDiscoverFeeds}> 77 + <Text type="lg-medium" style={textStyleInverted}> 78 + <Trans>Discover new custom feeds </Trans> 79 </Text> 80 <FontAwesomeIcon 81 icon="angle-right" 82 + style={textStyleInverted as FontAwesomeIconStyle} 83 size={14} 84 /> 85 </Button>
+25 -14
src/view/com/posts/FollowingEndOfFeed.tsx
··· 1 import React from 'react' 2 - import {Dimensions, StyleSheet, View} from 'react-native' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, ··· 7 import {Trans} from '@lingui/macro' 8 import {useNavigation} from '@react-navigation/native' 9 10 - import {usePalette} from '#/lib/hooks/usePalette' 11 import {type NavigationProp} from '#/lib/routes/types' 12 import {s} from '#/lib/styles' 13 import {isWeb} from '#/platform/detection' 14 import {Button} from '../util/forms/Button' 15 import {Text} from '../util/text/Text' 16 17 export function FollowingEndOfFeed() { 18 - const pal = usePalette('default') 19 - const palInverted = usePalette('inverted') 20 const navigation = useNavigation<NavigationProp>() 21 22 const onPressFindAccounts = React.useCallback(() => { 23 if (isWeb) { 24 navigation.navigate('Search', {}) ··· 36 <View 37 style={[ 38 styles.container, 39 - pal.border, 40 - {minHeight: Dimensions.get('window').height * 0.75}, 41 ]}> 42 <View style={styles.inner}> 43 - <Text type="xl-medium" style={[s.textCenter, pal.text]}> 44 <Trans> 45 You've reached the end of your feed! Find some more accounts to 46 follow. ··· 50 type="inverted" 51 style={styles.emptyBtn} 52 onPress={onPressFindAccounts}> 53 - <Text type="lg-medium" style={palInverted.text}> 54 - <Trans>Find accounts to follow</Trans> 55 </Text> 56 <FontAwesomeIcon 57 icon="angle-right" 58 - style={palInverted.text as FontAwesomeIconStyle} 59 size={14} 60 /> 61 </Button> 62 63 - <Text type="xl-medium" style={[s.textCenter, pal.text, s.mt20]}> 64 <Trans>You can also discover new Custom Feeds to follow.</Trans> 65 </Text> 66 <Button 67 type="inverted" 68 style={[styles.emptyBtn, s.mt10]} 69 onPress={onPressDiscoverFeeds}> 70 - <Text type="lg-medium" style={palInverted.text}> 71 - <Trans>Discover new custom feeds</Trans> 72 </Text> 73 <FontAwesomeIcon 74 icon="angle-right" 75 - style={palInverted.text as FontAwesomeIconStyle} 76 size={14} 77 /> 78 </Button>
··· 1 import React from 'react' 2 + import {Dimensions, StyleSheet, type TextStyle, View} from 'react-native' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, ··· 7 import {Trans} from '@lingui/macro' 8 import {useNavigation} from '@react-navigation/native' 9 10 import {type NavigationProp} from '#/lib/routes/types' 11 import {s} from '#/lib/styles' 12 import {isWeb} from '#/platform/detection' 13 + import {useTheme} from '#/alf' 14 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 15 import {Button} from '../util/forms/Button' 16 import {Text} from '../util/text/Text' 17 18 export function FollowingEndOfFeed() { 19 + const theme = useTheme() 20 + const colorMode = useColorModeTheme() 21 const navigation = useNavigation<NavigationProp>() 22 23 + const textStyle: TextStyle = { 24 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 25 + } 26 + 27 + const textStyleInverted: TextStyle = { 28 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 29 + } 30 + 31 const onPressFindAccounts = React.useCallback(() => { 32 if (isWeb) { 33 navigation.navigate('Search', {}) ··· 45 <View 46 style={[ 47 styles.container, 48 + { 49 + minHeight: Dimensions.get('window').height * 0.75, 50 + borderColor: theme.palette.contrast_100, 51 + }, 52 ]}> 53 <View style={styles.inner}> 54 + <Text type="xl-medium" style={[s.textCenter, textStyle]}> 55 <Trans> 56 You've reached the end of your feed! Find some more accounts to 57 follow. ··· 61 type="inverted" 62 style={styles.emptyBtn} 63 onPress={onPressFindAccounts}> 64 + <Text type="lg-medium" style={textStyleInverted}> 65 + <Trans>Find accounts to follow </Trans> 66 </Text> 67 <FontAwesomeIcon 68 icon="angle-right" 69 + style={textStyleInverted as FontAwesomeIconStyle} 70 size={14} 71 /> 72 </Button> 73 74 + <Text type="xl-medium" style={[s.textCenter, textStyle, s.mt20]}> 75 <Trans>You can also discover new Custom Feeds to follow.</Trans> 76 </Text> 77 <Button 78 type="inverted" 79 style={[styles.emptyBtn, s.mt10]} 80 onPress={onPressDiscoverFeeds}> 81 + <Text type="lg-medium" style={textStyleInverted}> 82 + <Trans>Discover new custom feeds </Trans> 83 </Text> 84 <FontAwesomeIcon 85 icon="angle-right" 86 + style={textStyleInverted as FontAwesomeIconStyle} 87 size={14} 88 /> 89 </Button>
+21 -7
src/view/com/posts/PostFeedErrorMessage.tsx
··· 9 import {useLingui} from '@lingui/react' 10 import {useNavigation} from '@react-navigation/native' 11 12 - import {usePalette} from '#/lib/hooks/usePalette' 13 import {type NavigationProp} from '#/lib/routes/types' 14 import {cleanError} from '#/lib/strings/errors' 15 import {logger} from '#/logger' 16 import {type FeedDescriptor} from '#/state/queries/post-feed' 17 import {useRemoveFeedMutation} from '#/state/queries/preferences' 18 import * as Prompt from '#/components/Prompt' 19 import {EmptyState} from '../util/EmptyState' 20 import {ErrorMessage} from '../util/error/ErrorMessage' ··· 94 rawError?: Error 95 savedFeedConfig?: AppBskyActorDefs.SavedFeed 96 }) { 97 - const pal = usePalette('default') 98 const {_: _l} = useLingui() 99 const navigation = useNavigation<NavigationProp>() 100 const msg = React.useMemo( ··· 189 <> 190 <View 191 style={[ 192 - pal.border, 193 - pal.viewLight, 194 { 195 borderTopWidth: 1, 196 paddingHorizontal: 20, ··· 198 gap: 12, 199 }, 200 ]}> 201 - <Text style={pal.text}>{msg}</Text> 202 203 {rawError?.message && ( 204 - <Text style={pal.textLight}> 205 - <Trans>Message from server: {rawError.message}</Trans> 206 </Text> 207 )} 208
··· 9 import {useLingui} from '@lingui/react' 10 import {useNavigation} from '@react-navigation/native' 11 12 import {type NavigationProp} from '#/lib/routes/types' 13 import {cleanError} from '#/lib/strings/errors' 14 import {logger} from '#/logger' 15 import {type FeedDescriptor} from '#/state/queries/post-feed' 16 import {useRemoveFeedMutation} from '#/state/queries/preferences' 17 + import {useTheme} from '#/alf' 18 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 19 import * as Prompt from '#/components/Prompt' 20 import {EmptyState} from '../util/EmptyState' 21 import {ErrorMessage} from '../util/error/ErrorMessage' ··· 95 rawError?: Error 96 savedFeedConfig?: AppBskyActorDefs.SavedFeed 97 }) { 98 + const theme = useTheme() 99 + const colorMode = useColorModeTheme() 100 const {_: _l} = useLingui() 101 const navigation = useNavigation<NavigationProp>() 102 const msg = React.useMemo( ··· 191 <> 192 <View 193 style={[ 194 + {borderColor: theme.palette.contrast_100}, 195 + {backgroundColor: theme.palette.contrast_25}, 196 { 197 borderTopWidth: 1, 198 paddingHorizontal: 20, ··· 200 gap: 12, 201 }, 202 ]}> 203 + <Text 204 + style={{ 205 + color: 206 + colorMode === 'light' ? theme.palette.black : theme.palette.white, 207 + }}> 208 + {msg} 209 + </Text> 210 211 {rawError?.message && ( 212 + <Text 213 + style={{ 214 + color: 215 + colorMode === 'dark' 216 + ? theme.palette.contrast_600 217 + : theme.palette.contrast_700, 218 + }}> 219 + <Trans>Message from server: {rawError.message} </Trans> 220 </Text> 221 )} 222
+65 -16
src/view/com/posts/PostFeedItem.tsx
··· 17 import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types' 18 import {MAX_POST_LINES} from '#/lib/constants' 19 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 20 - import {usePalette} from '#/lib/hooks/usePalette' 21 import {makeProfileLink} from '#/lib/routes/links' 22 import {sanitizeDisplayName} from '#/lib/strings/display-names' 23 import {sanitizeHandle} from '#/lib/strings/handles' ··· 40 import {PostMeta} from '#/view/com/util/PostMeta' 41 import {Text} from '#/view/com/util/text/Text' 42 import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 43 - import {atoms as a} from '#/alf' 44 import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin' 45 import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/Repost' 46 import {ContentHider} from '#/components/moderation/ContentHider' ··· 167 }): React.ReactNode => { 168 const queryClient = useQueryClient() 169 const {openComposer} = useOpenComposer() 170 - const pal = usePalette('default') 171 const {_} = useLingui() 172 173 const [hover, setHover] = useState(false) ··· 247 const outerStyles = [ 248 styles.outer, 249 { 250 - borderColor: pal.colors.border, 251 paddingBottom: 252 isThreadLastChild || (!isThreadChild && !isThreadParent) 253 ? 8 ··· 308 styles.replyLine, 309 { 310 flexGrow: 1, 311 - backgroundColor: pal.colors.replyLine, 312 marginBottom: 4, 313 }, 314 ]} ··· 321 <Link href={reason.href}> 322 <Text 323 type="sm-bold" 324 - style={pal.textLight} 325 lineHeight={1.2} 326 numberOfLines={1}> 327 <Trans context="from-feed"> ··· 332 href={reason.href} 333 lineHeight={1.2} 334 numberOfLines={1} 335 - style={pal.textLight} 336 /> 337 </Trans> 338 </Text> ··· 352 } 353 onBeforePress={onOpenReposter}> 354 <RepostIcon 355 - style={{color: pal.colors.textLight, marginRight: 3}} 356 width={13} 357 height={13} 358 /> 359 <Text 360 type="sm-bold" 361 - style={pal.textLight} 362 lineHeight={1.2} 363 numberOfLines={1}> 364 {isOwner ? ( 365 - <Trans>Reposted by you</Trans> 366 ) : ( 367 <Trans> 368 Reposted by{' '} 369 <ProfileHoverCard did={reason.by.did}> 370 <TextLinkOnWebOnly 371 type="sm-bold" 372 - style={pal.textLight} 373 lineHeight={1.2} 374 numberOfLines={1} 375 text={ 376 <Text 377 emoji 378 type="sm-bold" 379 - style={pal.textLight} 380 lineHeight={1.2}> 381 {sanitizeDisplayName( 382 reason.by.displayName || ··· 396 ) : AppBskyFeedDefs.isReasonPin(reason) ? ( 397 <View style={styles.includeReason}> 398 <PinIcon 399 - style={{color: pal.colors.textLight, marginRight: 3}} 400 width={13} 401 height={13} 402 /> 403 <Text 404 type="sm-bold" 405 - style={pal.textLight} 406 lineHeight={1.2} 407 numberOfLines={1}> 408 - <Trans>Pinned</Trans> 409 </Text> 410 </View> 411 ) : null} ··· 428 styles.replyLine, 429 { 430 flexGrow: 1, 431 - backgroundColor: pal.colors.replyLine, 432 marginTop: live ? 8 : 4, 433 }, 434 ]}
··· 17 import {isReasonFeedSource, type ReasonFeedSource} from '#/lib/api/feed/types' 18 import {MAX_POST_LINES} from '#/lib/constants' 19 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 20 import {makeProfileLink} from '#/lib/routes/links' 21 import {sanitizeDisplayName} from '#/lib/strings/display-names' 22 import {sanitizeHandle} from '#/lib/strings/handles' ··· 39 import {PostMeta} from '#/view/com/util/PostMeta' 40 import {Text} from '#/view/com/util/text/Text' 41 import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 42 + import {atoms as a, useTheme} from '#/alf' 43 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 44 import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin' 45 import {Repost_Stroke2_Corner2_Rounded as RepostIcon} from '#/components/icons/Repost' 46 import {ContentHider} from '#/components/moderation/ContentHider' ··· 167 }): React.ReactNode => { 168 const queryClient = useQueryClient() 169 const {openComposer} = useOpenComposer() 170 + const theme = useTheme() 171 + const colorMode = useColorModeTheme() 172 const {_} = useLingui() 173 174 const [hover, setHover] = useState(false) ··· 248 const outerStyles = [ 249 styles.outer, 250 { 251 + borderColor: theme.palette.contrast_100, 252 paddingBottom: 253 isThreadLastChild || (!isThreadChild && !isThreadParent) 254 ? 8 ··· 309 styles.replyLine, 310 { 311 flexGrow: 1, 312 + backgroundColor: 313 + colorMode === 'light' 314 + ? theme.palette.contrast_100 315 + : theme.palette.contrast_200, 316 marginBottom: 4, 317 }, 318 ]} ··· 325 <Link href={reason.href}> 326 <Text 327 type="sm-bold" 328 + style={{ 329 + color: 330 + colorMode === 'dark' 331 + ? theme.palette.contrast_600 332 + : theme.palette.contrast_700, 333 + }} 334 lineHeight={1.2} 335 numberOfLines={1}> 336 <Trans context="from-feed"> ··· 341 href={reason.href} 342 lineHeight={1.2} 343 numberOfLines={1} 344 + style={{ 345 + color: 346 + colorMode === 'dark' 347 + ? theme.palette.contrast_600 348 + : theme.palette.contrast_700, 349 + }} 350 /> 351 </Trans> 352 </Text> ··· 366 } 367 onBeforePress={onOpenReposter}> 368 <RepostIcon 369 + style={{ 370 + color: 371 + colorMode === 'dark' 372 + ? theme.palette.contrast_600 373 + : theme.palette.contrast_700, 374 + marginRight: 3, 375 + }} 376 width={13} 377 height={13} 378 /> 379 <Text 380 type="sm-bold" 381 + style={{ 382 + color: 383 + colorMode === 'dark' 384 + ? theme.palette.contrast_600 385 + : theme.palette.contrast_700, 386 + }} 387 lineHeight={1.2} 388 numberOfLines={1}> 389 {isOwner ? ( 390 + <Trans> Reposted by you </Trans> 391 ) : ( 392 <Trans> 393 Reposted by{' '} 394 <ProfileHoverCard did={reason.by.did}> 395 <TextLinkOnWebOnly 396 type="sm-bold" 397 + style={{ 398 + color: 399 + colorMode === 'dark' 400 + ? theme.palette.contrast_600 401 + : theme.palette.contrast_700, 402 + }} 403 lineHeight={1.2} 404 numberOfLines={1} 405 text={ 406 <Text 407 emoji 408 type="sm-bold" 409 + style={{ 410 + color: 411 + colorMode === 'dark' 412 + ? theme.palette.contrast_600 413 + : theme.palette.contrast_700, 414 + }} 415 lineHeight={1.2}> 416 {sanitizeDisplayName( 417 reason.by.displayName || ··· 431 ) : AppBskyFeedDefs.isReasonPin(reason) ? ( 432 <View style={styles.includeReason}> 433 <PinIcon 434 + style={{ 435 + color: 436 + colorMode === 'dark' 437 + ? theme.palette.contrast_600 438 + : theme.palette.contrast_700, 439 + marginRight: 3, 440 + }} 441 width={13} 442 height={13} 443 /> 444 <Text 445 type="sm-bold" 446 + style={{ 447 + color: 448 + colorMode === 'dark' 449 + ? theme.palette.contrast_600 450 + : theme.palette.contrast_700, 451 + }} 452 lineHeight={1.2} 453 numberOfLines={1}> 454 + <Trans>Pinned </Trans> 455 </Text> 456 </View> 457 ) : null} ··· 474 styles.replyLine, 475 { 476 flexGrow: 1, 477 + backgroundColor: 478 + colorMode === 'light' 479 + ? theme.palette.contrast_100 480 + : theme.palette.contrast_200, 481 marginTop: live ? 8 : 4, 482 }, 483 ]}
+18 -7
src/view/com/posts/ViewFullThread.tsx
··· 5 import {msg} from '@lingui/macro' 6 import {useLingui} from '@lingui/react' 7 8 - import {usePalette} from '#/lib/hooks/usePalette' 9 import {makeProfileLink} from '#/lib/routes/links' 10 import {useInteractionState} from '#/components/hooks/useInteractionState' 11 import {SubtleWebHover} from '#/components/SubtleWebHover' 12 import {Link} from '../util/Link' ··· 18 onIn: onHoverIn, 19 onOut: onHoverOut, 20 } = useInteractionState() 21 - const pal = usePalette('default') 22 const itemHref = React.useMemo(() => { 23 const urip = new AtUri(uri) 24 return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey) ··· 45 y1="0" 46 x2="2" 47 y2="15" 48 - stroke={pal.colors.replyLine} 49 strokeWidth="2" 50 /> 51 - <Circle cx="2" cy="22" r="1.5" fill={pal.colors.replyLineDot} /> 52 - <Circle cx="2" cy="28" r="1.5" fill={pal.colors.replyLineDot} /> 53 - <Circle cx="2" cy="34" r="1.5" fill={pal.colors.replyLineDot} /> 54 </Svg> 55 </View> 56 57 - <Text type="md" style={[pal.link, {paddingTop: 18, paddingBottom: 4}]}> 58 {/* HACKFIX: Trans isn't working after SDK 53 upgrade -sfn */} 59 {_(msg`View full thread`)} 60 </Text>
··· 5 import {msg} from '@lingui/macro' 6 import {useLingui} from '@lingui/react' 7 8 import {makeProfileLink} from '#/lib/routes/links' 9 + import {useTheme} from '#/alf' 10 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 11 import {useInteractionState} from '#/components/hooks/useInteractionState' 12 import {SubtleWebHover} from '#/components/SubtleWebHover' 13 import {Link} from '../util/Link' ··· 19 onIn: onHoverIn, 20 onOut: onHoverOut, 21 } = useInteractionState() 22 + const theme = useTheme() 23 + const colorMode = useColorModeTheme() 24 const itemHref = React.useMemo(() => { 25 const urip = new AtUri(uri) 26 return makeProfileLink({did: urip.hostname, handle: ''}, 'post', urip.rkey) ··· 47 y1="0" 48 x2="2" 49 y2="15" 50 + stroke={ 51 + colorMode === 'light' 52 + ? theme.palette.contrast_100 53 + : theme.palette.contrast_200 54 + } 55 strokeWidth="2" 56 /> 57 + <Circle cx="2" cy="22" r="1.5" fill={theme.palette.contrast_200} /> 58 + <Circle cx="2" cy="28" r="1.5" fill={theme.palette.contrast_200} /> 59 + <Circle cx="2" cy="34" r="1.5" fill={theme.palette.contrast_200} /> 60 </Svg> 61 </View> 62 63 + <Text 64 + type="md" 65 + style={[ 66 + {color: theme.palette.primary_500}, 67 + {paddingTop: 18, paddingBottom: 4}, 68 + ]}> 69 {/* HACKFIX: Trans isn't working after SDK 53 upgrade -sfn */} 70 {_(msg`View full thread`)} 71 </Text>
+23 -8
src/view/com/profile/ProfileSubpageHeader.tsx
··· 1 import React from 'react' 2 - import {Pressable, View} from 'react-native' 3 import Animated, { 4 measure, 5 type MeasuredDimensions, ··· 12 import {useLingui} from '@lingui/react' 13 import {useNavigation} from '@react-navigation/native' 14 15 - import {usePalette} from '#/lib/hooks/usePalette' 16 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 17 import {makeProfileLink} from '#/lib/routes/links' 18 import {type NavigationProp} from '#/lib/routes/types' ··· 23 import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 24 import {Text} from '#/view/com/util/text/Text' 25 import {UserAvatar, type UserAvatarType} from '#/view/com/util/UserAvatar' 26 import {StarterPack} from '#/components/icons/StarterPack' 27 import * as Layout from '#/components/Layout' 28 ··· 55 const {_} = useLingui() 56 const {isMobile} = useWebMediaQueries() 57 const {openLightbox} = useLightboxControls() 58 - const pal = usePalette('default') 59 const canGoBack = navigation.canGoBack() 60 const aviRef = useAnimatedRef() 61 62 const _openLightbox = React.useCallback( 63 (uri: string, thumbRect: MeasuredDimensions | null) => { ··· 142 testID="headerTitle" 143 type="title-xl" 144 href={href} 145 - style={[pal.text, {fontWeight: '600'}]} 146 text={title || ''} 147 onPress={emitSoftReset} 148 numberOfLines={4} ··· 152 {isLoading || !creator ? ( 153 <LoadingPlaceholder width={50} height={8} /> 154 ) : ( 155 - <Text type="lg" style={[pal.textLight]} numberOfLines={1}> 156 {purpose === 'app.bsky.graph.defs#curatelist' ? ( 157 isOwner ? ( 158 <Trans>List by you</Trans> ··· 162 <TextLink 163 text={sanitizeHandle(creator.handle || '', '@')} 164 href={makeProfileLink(creator)} 165 - style={pal.textLight} 166 /> 167 </Trans> 168 ) ··· 175 <TextLink 176 text={sanitizeHandle(creator.handle || '', '@')} 177 href={makeProfileLink(creator)} 178 - style={pal.textLight} 179 /> 180 </Trans> 181 ) ··· 188 <TextLink 189 text={sanitizeHandle(creator.handle || '', '@')} 190 href={makeProfileLink(creator)} 191 - style={pal.textLight} 192 /> 193 </Trans> 194 )
··· 1 import React from 'react' 2 + import {Pressable, type TextStyle, View} from 'react-native' 3 import Animated, { 4 measure, 5 type MeasuredDimensions, ··· 12 import {useLingui} from '@lingui/react' 13 import {useNavigation} from '@react-navigation/native' 14 15 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 16 import {makeProfileLink} from '#/lib/routes/links' 17 import {type NavigationProp} from '#/lib/routes/types' ··· 22 import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 23 import {Text} from '#/view/com/util/text/Text' 24 import {UserAvatar, type UserAvatarType} from '#/view/com/util/UserAvatar' 25 + import {useTheme} from '#/alf' 26 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 27 import {StarterPack} from '#/components/icons/StarterPack' 28 import * as Layout from '#/components/Layout' 29 ··· 56 const {_} = useLingui() 57 const {isMobile} = useWebMediaQueries() 58 const {openLightbox} = useLightboxControls() 59 + const theme = useTheme() 60 + const colorMode = useColorModeTheme() 61 const canGoBack = navigation.canGoBack() 62 const aviRef = useAnimatedRef() 63 + 64 + const textLightStyle: TextStyle = { 65 + color: 66 + colorMode === 'dark' 67 + ? theme.palette.contrast_600 68 + : theme.palette.contrast_700, 69 + } 70 71 const _openLightbox = React.useCallback( 72 (uri: string, thumbRect: MeasuredDimensions | null) => { ··· 151 testID="headerTitle" 152 type="title-xl" 153 href={href} 154 + style={{ 155 + fontWeight: '600', 156 + color: 157 + colorMode === 'light' 158 + ? theme.palette.black 159 + : theme.palette.white, 160 + }} 161 text={title || ''} 162 onPress={emitSoftReset} 163 numberOfLines={4} ··· 167 {isLoading || !creator ? ( 168 <LoadingPlaceholder width={50} height={8} /> 169 ) : ( 170 + <Text type="lg" style={textLightStyle} numberOfLines={1}> 171 {purpose === 'app.bsky.graph.defs#curatelist' ? ( 172 isOwner ? ( 173 <Trans>List by you</Trans> ··· 177 <TextLink 178 text={sanitizeHandle(creator.handle || '', '@')} 179 href={makeProfileLink(creator)} 180 + style={textLightStyle} 181 /> 182 </Trans> 183 ) ··· 190 <TextLink 191 text={sanitizeHandle(creator.handle || '', '@')} 192 href={makeProfileLink(creator)} 193 + style={textLightStyle} 194 /> 195 </Trans> 196 ) ··· 203 <TextLink 204 text={sanitizeHandle(creator.handle || '', '@')} 205 href={makeProfileLink(creator)} 206 + style={textLightStyle} 207 /> 208 </Trans> 209 )
+10 -6
src/view/com/util/EmptyState.tsx
··· 5 type FontAwesomeIconStyle, 6 } from '@fortawesome/react-native-fontawesome' 7 8 - import {usePalette} from '#/lib/hooks/usePalette' 9 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 10 import {UserGroupIcon} from '#/lib/icons' 11 import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth' 12 import {Text} from './text/Text' 13 ··· 22 message: string 23 style?: StyleProp<ViewStyle> 24 }) { 25 - const pal = usePalette('default') 26 const {isTabletOrDesktop} = useWebMediaQueries() 27 const iconSize = isTabletOrDesktop ? 64 : 48 28 return ( ··· 31 style={[ 32 styles.iconContainer, 33 isTabletOrDesktop && styles.iconContainerBig, 34 - pal.viewLight, 35 ]}> 36 {icon === 'user-group' ? ( 37 <UserGroupIcon size={iconSize} /> 38 ) : icon === 'growth' ? ( 39 - <Growth width={iconSize} fill={pal.colors.emptyStateIcon} /> 40 ) : ( 41 <FontAwesomeIcon 42 icon={icon} 43 size={iconSize} 44 - style={[{color: pal.colors.emptyStateIcon} as FontAwesomeIconStyle]} 45 /> 46 )} 47 </View> 48 - <Text type="xl" style={[{color: pal.colors.textLight}, styles.text]}> 49 {message} 50 </Text> 51 </View>
··· 5 type FontAwesomeIconStyle, 6 } from '@fortawesome/react-native-fontawesome' 7 8 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 9 import {UserGroupIcon} from '#/lib/icons' 10 + import {useTheme} from '#/alf' 11 import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth' 12 import {Text} from './text/Text' 13 ··· 22 message: string 23 style?: StyleProp<ViewStyle> 24 }) { 25 + const theme = useTheme() 26 const {isTabletOrDesktop} = useWebMediaQueries() 27 const iconSize = isTabletOrDesktop ? 64 : 48 28 return ( ··· 31 style={[ 32 styles.iconContainer, 33 isTabletOrDesktop && styles.iconContainerBig, 34 + {backgroundColor: theme.palette.contrast_25}, 35 ]}> 36 {icon === 'user-group' ? ( 37 <UserGroupIcon size={iconSize} /> 38 ) : icon === 'growth' ? ( 39 + <Growth width={iconSize} fill={theme.palette.contrast_300} /> 40 ) : ( 41 <FontAwesomeIcon 42 icon={icon} 43 size={iconSize} 44 + style={[ 45 + {color: theme.palette.contrast_300} as FontAwesomeIconStyle, 46 + ]} 47 /> 48 )} 49 </View> 50 + <Text 51 + type="xl" 52 + style={[{color: theme.palette.contrast_600}, styles.text]}> 53 {message} 54 </Text> 55 </View>
+17 -8
src/view/com/util/EmptyStateWithButton.tsx
··· 1 - import {StyleSheet, View} from 'react-native' 2 import {type IconProp} from '@fortawesome/fontawesome-svg-core' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, 6 } from '@fortawesome/react-native-fontawesome' 7 8 - import {usePalette} from '#/lib/hooks/usePalette' 9 import {s} from '#/lib/styles' 10 import {Button} from './forms/Button' 11 import {Text} from './text/Text' 12 ··· 19 } 20 21 export function EmptyStateWithButton(props: Props) { 22 - const pal = usePalette('default') 23 - const palInverted = usePalette('inverted') 24 25 return ( 26 <View testID={props.testID} style={styles.container}> 27 <View style={styles.iconContainer}> 28 <FontAwesomeIcon 29 icon={props.icon} 30 - style={[styles.icon, pal.text]} 31 size={62} 32 /> 33 </View> 34 - <Text type="xl-medium" style={[s.textCenter, pal.text]}> 35 {props.message} 36 </Text> 37 <View style={styles.btns}> ··· 42 onPress={props.onPress}> 43 <FontAwesomeIcon 44 icon="plus" 45 - style={palInverted.text as FontAwesomeIconStyle} 46 size={14} 47 /> 48 - <Text type="lg-medium" style={palInverted.text}> 49 {props.buttonLabel} 50 </Text> 51 </Button>
··· 1 + import {StyleSheet, type TextStyle, View} from 'react-native' 2 import {type IconProp} from '@fortawesome/fontawesome-svg-core' 3 import { 4 FontAwesomeIcon, 5 type FontAwesomeIconStyle, 6 } from '@fortawesome/react-native-fontawesome' 7 8 import {s} from '#/lib/styles' 9 + import {useTheme} from '#/alf' 10 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 11 import {Button} from './forms/Button' 12 import {Text} from './text/Text' 13 ··· 20 } 21 22 export function EmptyStateWithButton(props: Props) { 23 + const theme = useTheme() 24 + const colorMode = useColorModeTheme() 25 + 26 + const textStyle: TextStyle = { 27 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 28 + } 29 + 30 + const textStyleInverted: TextStyle = { 31 + color: colorMode === 'light' ? theme.palette.white : theme.palette.black, 32 + } 33 34 return ( 35 <View testID={props.testID} style={styles.container}> 36 <View style={styles.iconContainer}> 37 <FontAwesomeIcon 38 icon={props.icon} 39 + style={[styles.icon, textStyle]} 40 size={62} 41 /> 42 </View> 43 + <Text type="xl-medium" style={[s.textCenter, textStyle]}> 44 {props.message} 45 </Text> 46 <View style={styles.btns}> ··· 51 onPress={props.onPress}> 52 <FontAwesomeIcon 53 icon="plus" 54 + style={textStyleInverted as FontAwesomeIconStyle} 55 size={14} 56 /> 57 + <Text type="lg-medium" style={textStyleInverted}> 58 {props.buttonLabel} 59 </Text> 60 </Button>
+25 -4
src/view/com/util/LoadMoreRetryBtn.tsx
··· 4 type FontAwesomeIconStyle, 5 } from '@fortawesome/react-native-fontawesome' 6 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import {Button} from './forms/Button' 9 import {Text} from './text/Text' 10 ··· 15 label: string 16 onPress: () => void 17 }) { 18 - const pal = usePalette('default') 19 return ( 20 <Button type="default-light" onPress={onPress} style={styles.loadMoreRetry}> 21 <FontAwesomeIcon 22 icon="arrow-rotate-left" 23 - style={pal.textLight as FontAwesomeIconStyle} 24 size={18} 25 /> 26 - <Text style={[pal.textLight, styles.label]}>{label}</Text> 27 </Button> 28 ) 29 }
··· 4 type FontAwesomeIconStyle, 5 } from '@fortawesome/react-native-fontawesome' 6 7 + import {useTheme} from '#/alf' 8 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 9 import {Button} from './forms/Button' 10 import {Text} from './text/Text' 11 ··· 16 label: string 17 onPress: () => void 18 }) { 19 + const theme = useTheme() 20 + const colorMode = useColorModeTheme() 21 return ( 22 <Button type="default-light" onPress={onPress} style={styles.loadMoreRetry}> 23 <FontAwesomeIcon 24 icon="arrow-rotate-left" 25 + style={ 26 + { 27 + color: 28 + colorMode === 'dark' 29 + ? theme.palette.contrast_600 30 + : theme.palette.contrast_700, 31 + } as FontAwesomeIconStyle 32 + } 33 size={18} 34 /> 35 + <Text 36 + style={[ 37 + { 38 + color: 39 + colorMode === 'dark' 40 + ? theme.palette.contrast_600 41 + : theme.palette.contrast_700, 42 + }, 43 + styles.label, 44 + ]}> 45 + {' '} 46 + {label}{' '} 47 + </Text> 48 </Button> 49 ) 50 }
+54 -23
src/view/com/util/LoadingPlaceholder.tsx
··· 7 type ViewStyle, 8 } from 'react-native' 9 10 - import {usePalette} from '#/lib/hooks/usePalette' 11 import {s} from '#/lib/styles' 12 - import {useTheme} from '#/lib/ThemeContext' 13 - import {atoms as a, useTheme as useTheme_NEW} from '#/alf' 14 import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble' 15 import { 16 Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, ··· 35 { 36 width, 37 height, 38 - backgroundColor: theme.palette.default.backgroundLight, 39 }, 40 style, 41 ]} ··· 48 }: { 49 style?: StyleProp<ViewStyle> 50 }) { 51 - const t = useTheme_NEW() 52 - const pal = usePalette('default') 53 return ( 54 - <View style={[styles.post, pal.view, style]}> 55 <LoadingPlaceholder 56 width={42} 57 height={42} ··· 74 <Bubble 75 style={[ 76 { 77 - color: t.palette.contrast_500, 78 }, 79 {pointerEvents: 'none'}, 80 ]} ··· 87 <Repost 88 style={[ 89 { 90 - color: t.palette.contrast_500, 91 }, 92 {pointerEvents: 'none'}, 93 ]} ··· 100 <HeartIconOutline 101 style={[ 102 { 103 - color: t.palette.contrast_500, 104 }, 105 {pointerEvents: 'none'}, 106 ]} ··· 137 }: { 138 style?: StyleProp<ViewStyle> 139 }) { 140 - const pal = usePalette('default') 141 return ( 142 - <View style={[styles.notification, pal.view, style]}> 143 <View style={[{width: 60}, a.align_end, a.pr_sm, a.pt_2xs]}> 144 - <HeartIconFilled 145 - size="xl" 146 - style={{color: pal.colors.backgroundLight}} 147 - /> 148 </View> 149 <View style={{flex: 1}}> 150 <View style={[a.flex_row, s.mb10]}> ··· 184 }: { 185 style?: StyleProp<ViewStyle> 186 }) { 187 - const pal = usePalette('default') 188 return ( 189 - <View style={[styles.profileCard, pal.view, style]}> 190 <LoadingPlaceholder 191 width={40} 192 height={40} ··· 228 showTopBorder?: boolean 229 showLowerPlaceholder?: boolean 230 }) { 231 - const pal = usePalette('default') 232 return ( 233 <View 234 style={[ ··· 236 padding: 16, 237 borderTopWidth: showTopBorder ? StyleSheet.hairlineWidth : 0, 238 }, 239 - pal.border, 240 style, 241 ]}> 242 - <View style={[pal.view, {flexDirection: 'row'}]}> 243 <LoadingPlaceholder 244 width={36} 245 height={36} ··· 282 }: { 283 style?: StyleProp<ViewStyle> 284 }) { 285 - const t = useTheme_NEW() 286 const random = useMemo(() => Math.random(), []) 287 return ( 288 - <View style={[a.flex_row, a.gap_md, a.px_lg, a.mt_lg, t.atoms.bg, style]}> 289 <LoadingPlaceholder width={52} height={52} style={a.rounded_full} /> 290 <View> 291 <LoadingPlaceholder width={140} height={12} style={a.mt_xs} />
··· 7 type ViewStyle, 8 } from 'react-native' 9 10 import {s} from '#/lib/styles' 11 + import {atoms as a, useTheme} from '#/alf' 12 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 13 import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble' 14 import { 15 Heart2_Filled_Stroke2_Corner0_Rounded as HeartIconFilled, ··· 34 { 35 width, 36 height, 37 + backgroundColor: theme.palette.contrast_25, 38 }, 39 style, 40 ]} ··· 47 }: { 48 style?: StyleProp<ViewStyle> 49 }) { 50 + const theme = useTheme() 51 + const colorMode = useColorModeTheme() 52 return ( 53 + <View 54 + style={[ 55 + styles.post, 56 + { 57 + backgroundColor: 58 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 59 + }, 60 + style, 61 + ]}> 62 <LoadingPlaceholder 63 width={42} 64 height={42} ··· 81 <Bubble 82 style={[ 83 { 84 + color: theme.palette.contrast_500, 85 }, 86 {pointerEvents: 'none'}, 87 ]} ··· 94 <Repost 95 style={[ 96 { 97 + color: theme.palette.contrast_500, 98 }, 99 {pointerEvents: 'none'}, 100 ]} ··· 107 <HeartIconOutline 108 style={[ 109 { 110 + color: theme.palette.contrast_500, 111 }, 112 {pointerEvents: 'none'}, 113 ]} ··· 144 }: { 145 style?: StyleProp<ViewStyle> 146 }) { 147 + const theme = useTheme() 148 + const colorMode = useColorModeTheme() 149 return ( 150 + <View 151 + style={[ 152 + styles.notification, 153 + { 154 + backgroundColor: 155 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 156 + }, 157 + style, 158 + ]}> 159 <View style={[{width: 60}, a.align_end, a.pr_sm, a.pt_2xs]}> 160 + <HeartIconFilled size="xl" style={{color: theme.palette.contrast_25}} /> 161 </View> 162 <View style={{flex: 1}}> 163 <View style={[a.flex_row, s.mb10]}> ··· 197 }: { 198 style?: StyleProp<ViewStyle> 199 }) { 200 + const theme = useTheme() 201 + const colorMode = useColorModeTheme() 202 return ( 203 + <View 204 + style={[ 205 + styles.profileCard, 206 + { 207 + backgroundColor: 208 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 209 + }, 210 + style, 211 + ]}> 212 <LoadingPlaceholder 213 width={40} 214 height={40} ··· 250 showTopBorder?: boolean 251 showLowerPlaceholder?: boolean 252 }) { 253 + const theme = useTheme() 254 + const colorMode = useColorModeTheme() 255 return ( 256 <View 257 style={[ ··· 259 padding: 16, 260 borderTopWidth: showTopBorder ? StyleSheet.hairlineWidth : 0, 261 }, 262 + {borderColor: theme.palette.contrast_100}, 263 style, 264 ]}> 265 + <View 266 + style={[ 267 + { 268 + backgroundColor: 269 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 270 + }, 271 + {flexDirection: 'row'}, 272 + ]}> 273 <LoadingPlaceholder 274 width={36} 275 height={36} ··· 312 }: { 313 style?: StyleProp<ViewStyle> 314 }) { 315 + const theme = useTheme() 316 const random = useMemo(() => Math.random(), []) 317 return ( 318 + <View 319 + style={[a.flex_row, a.gap_md, a.px_lg, a.mt_lg, theme.atoms.bg, style]}> 320 <LoadingPlaceholder width={52} height={52} style={a.rounded_full} /> 321 <View> 322 <LoadingPlaceholder width={140} height={12} style={a.mt_xs} />
+39 -8
src/view/com/util/ViewSelector.tsx
··· 10 } from 'react-native' 11 12 import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' 13 - import {usePalette} from '#/lib/hooks/usePalette' 14 import {clamp} from '#/lib/numbers' 15 import {colors, s} from '#/lib/styles' 16 import {isAndroid} from '#/platform/detection' 17 import {Text} from './text/Text' 18 import {FlatList_INTERNAL} from './Views' 19 ··· 59 }, 60 ref, 61 ) { 62 - const pal = usePalette('default') 63 const [selectedIndex, setSelectedIndex] = useState<number>(0) 64 const flatListRef = React.useRef<FlatList_INTERNAL>(null) 65 ··· 127 <RefreshControl 128 refreshing={refreshing!} 129 onRefresh={onRefresh} 130 - tintColor={pal.colors.text} 131 /> 132 } 133 onEndReachedThreshold={0.6} ··· 147 items: string[] 148 onSelect?: (index: number) => void 149 }) { 150 - const pal = usePalette('default') 151 const borderColor = useColorSchemeStyle( 152 {borderColor: colors.black}, 153 {borderColor: colors.white}, ··· 161 <View 162 style={{ 163 width: '100%', 164 - backgroundColor: pal.colors.background, 165 }}> 166 <ScrollView 167 testID="selector" 168 horizontal 169 showsHorizontalScrollIndicator={false}> 170 - <View style={[pal.view, styles.outer]}> 171 {items.map((item, i) => { 172 const selected = i === selectedIndex 173 return ( ··· 189 <Text 190 style={ 191 selected 192 - ? [styles.labelSelected, pal.text] 193 - : [styles.label, pal.textLight] 194 }> 195 {item} 196 </Text>
··· 10 } from 'react-native' 11 12 import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' 13 import {clamp} from '#/lib/numbers' 14 import {colors, s} from '#/lib/styles' 15 import {isAndroid} from '#/platform/detection' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 import {Text} from './text/Text' 19 import {FlatList_INTERNAL} from './Views' 20 ··· 60 }, 61 ref, 62 ) { 63 + const theme = useTheme() 64 + const colorMode = useColorModeTheme() 65 const [selectedIndex, setSelectedIndex] = useState<number>(0) 66 const flatListRef = React.useRef<FlatList_INTERNAL>(null) 67 ··· 129 <RefreshControl 130 refreshing={refreshing!} 131 onRefresh={onRefresh} 132 + tintColor={ 133 + colorMode === 'light' ? theme.palette.black : theme.palette.white 134 + } 135 /> 136 } 137 onEndReachedThreshold={0.6} ··· 151 items: string[] 152 onSelect?: (index: number) => void 153 }) { 154 + const theme = useTheme() 155 + const colorMode = useColorModeTheme() 156 const borderColor = useColorSchemeStyle( 157 {borderColor: colors.black}, 158 {borderColor: colors.white}, ··· 166 <View 167 style={{ 168 width: '100%', 169 + backgroundColor: 170 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 171 }}> 172 <ScrollView 173 testID="selector" 174 horizontal 175 showsHorizontalScrollIndicator={false}> 176 + <View 177 + style={[ 178 + { 179 + backgroundColor: 180 + colorMode === 'light' 181 + ? theme.palette.white 182 + : theme.palette.black, 183 + }, 184 + styles.outer, 185 + ]}> 186 {items.map((item, i) => { 187 const selected = i === selectedIndex 188 return ( ··· 204 <Text 205 style={ 206 selected 207 + ? [ 208 + styles.labelSelected, 209 + { 210 + color: 211 + colorMode === 'light' 212 + ? theme.palette.black 213 + : theme.palette.white, 214 + }, 215 + ] 216 + : [ 217 + styles.label, 218 + { 219 + color: 220 + colorMode === 'light' 221 + ? theme.palette.white 222 + : theme.palette.black, 223 + }, 224 + ] 225 }> 226 {item} 227 </Text>
+5 -4
src/view/com/util/Views.web.tsx
··· 23 } from 'react-native' 24 import Animated from 'react-native-reanimated' 25 26 - import {usePalette} from '#/lib/hooks/usePalette' 27 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 28 import {addStyle} from '#/lib/styles' 29 - import {useLayoutBreakpoints} from '#/alf' 30 import {useDialogContext} from '#/components/Dialog' 31 import {CENTER_COLUMN_OFFSET} from '#/components/Layout' 32 ··· 47 >, 48 ref: React.Ref<View>, 49 ) { 50 - const pal = usePalette('default') 51 const {isMobile} = useWebMediaQueries() 52 const {centerColumnOffset} = useLayoutBreakpoints() 53 const {isWithinDialog} = useDialogContext() ··· 61 style = addStyle(style, { 62 borderTopWidth: 1, 63 }) 64 - style = addStyle(style, pal.border) 65 } 66 return <View ref={ref} style={style} {...props} /> 67 })
··· 23 } from 'react-native' 24 import Animated from 'react-native-reanimated' 25 26 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 27 import {addStyle} from '#/lib/styles' 28 + import {useLayoutBreakpoints, useTheme} from '#/alf' 29 import {useDialogContext} from '#/components/Dialog' 30 import {CENTER_COLUMN_OFFSET} from '#/components/Layout' 31 ··· 46 >, 47 ref: React.Ref<View>, 48 ) { 49 + const theme = useTheme() 50 const {isMobile} = useWebMediaQueries() 51 const {centerColumnOffset} = useLayoutBreakpoints() 52 const {isWithinDialog} = useDialogContext() ··· 60 style = addStyle(style, { 61 borderTopWidth: 1, 62 }) 63 + style = addStyle(style, { 64 + borderColor: theme.palette.contrast_100, 65 + }) 66 } 67 return <View ref={ref} style={style} {...props} /> 68 })
+12 -8
src/view/com/util/error/ErrorMessage.tsx
··· 12 import {msg} from '@lingui/macro' 13 import {useLingui} from '@lingui/react' 14 15 - import {usePalette} from '#/lib/hooks/usePalette' 16 - import {useTheme} from '#/lib/ThemeContext' 17 import * as Layout from '#/components/Layout' 18 import {Text} from '../text/Text' 19 ··· 29 onPressTryAgain?: () => void 30 }) { 31 const theme = useTheme() 32 - const pal = usePalette('error') 33 const {_} = useLingui() 34 return ( 35 <Layout.Center> 36 - <View testID="errorMessageView" style={[styles.outer, pal.view, style]}> 37 <View 38 style={[ 39 styles.errorIcon, 40 - {backgroundColor: theme.palette.error.icon}, 41 ]}> 42 <FontAwesomeIcon 43 icon="exclamation" 44 - style={pal.text as FontAwesomeIconStyle} 45 size={16} 46 /> 47 </View> 48 <Text 49 type="sm-medium" 50 - style={[styles.message, pal.text]} 51 numberOfLines={numberOfLines}> 52 {message} 53 </Text> ··· 63 )}> 64 <FontAwesomeIcon 65 icon="arrows-rotate" 66 - style={{color: theme.palette.error.icon}} 67 size={18} 68 /> 69 </TouchableOpacity>
··· 12 import {msg} from '@lingui/macro' 13 import {useLingui} from '@lingui/react' 14 15 + import {useTheme} from '#/alf' 16 import * as Layout from '#/components/Layout' 17 import {Text} from '../text/Text' 18 ··· 28 onPressTryAgain?: () => void 29 }) { 30 const theme = useTheme() 31 const {_} = useLingui() 32 return ( 33 <Layout.Center> 34 + <View 35 + testID="errorMessageView" 36 + style={[ 37 + styles.outer, 38 + {backgroundColor: theme.palette.negative_300}, 39 + style, 40 + ]}> 41 <View 42 style={[ 43 styles.errorIcon, 44 + {backgroundColor: theme.palette.negative_400}, 45 ]}> 46 <FontAwesomeIcon 47 icon="exclamation" 48 + style={{color: theme.palette.negative_400} as FontAwesomeIconStyle} 49 size={16} 50 /> 51 </View> 52 <Text 53 type="sm-medium" 54 + style={[styles.message, {color: theme.palette.white}]} 55 numberOfLines={numberOfLines}> 56 {message} 57 </Text> ··· 67 )}> 68 <FontAwesomeIcon 69 icon="arrows-rotate" 70 + style={{color: theme.palette.negative_400}} 71 size={18} 72 /> 73 </TouchableOpacity>
+11 -6
src/view/com/util/error/ErrorScreen.tsx
··· 6 import {msg, Trans} from '@lingui/macro' 7 import {useLingui} from '@lingui/react' 8 9 - import {usePalette} from '#/lib/hooks/usePalette' 10 import {atoms as a, useTheme} from '#/alf' 11 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 12 import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ArrowRotateCounterClockwiseIcon} from '#/components/icons/ArrowRotateCounterClockwise' 13 import * as Layout from '#/components/Layout' ··· 29 showHeader?: boolean 30 }) { 31 const t = useTheme() 32 - const pal = usePalette('default') 33 const {_} = useLingui() 34 35 return ( ··· 39 <Layout.Header.BackButton /> 40 <Layout.Header.Content> 41 <Layout.Header.TitleText> 42 - <Trans>Error</Trans> 43 </Layout.Header.TitleText> 44 </Layout.Header.Content> 45 <Layout.Header.Slot /> ··· 57 ]}> 58 <FontAwesomeIcon 59 icon="exclamation" 60 - style={pal.textInverted as FontAwesomeIconStyle} 61 size={24} 62 /> 63 </View> ··· 65 <Text style={[a.text_center, a.font_heavy, a.text_2xl, a.mb_md]}> 66 {title} 67 </Text> 68 - <Text style={[a.text_center, a.text_md, a.mb_xl]}>{message}</Text> 69 {details && ( 70 <View 71 style={[ ··· 100 )}> 101 <ButtonIcon icon={ArrowRotateCounterClockwiseIcon} /> 102 <ButtonText> 103 - <Trans context="action">Try again</Trans> 104 </ButtonText> 105 </Button> 106 </View>
··· 6 import {msg, Trans} from '@lingui/macro' 7 import {useLingui} from '@lingui/react' 8 9 import {atoms as a, useTheme} from '#/alf' 10 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 11 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 12 import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ArrowRotateCounterClockwiseIcon} from '#/components/icons/ArrowRotateCounterClockwise' 13 import * as Layout from '#/components/Layout' ··· 29 showHeader?: boolean 30 }) { 31 const t = useTheme() 32 + const colorMode = useColorModeTheme() 33 const {_} = useLingui() 34 35 return ( ··· 39 <Layout.Header.BackButton /> 40 <Layout.Header.Content> 41 <Layout.Header.TitleText> 42 + <Trans>Error </Trans> 43 </Layout.Header.TitleText> 44 </Layout.Header.Content> 45 <Layout.Header.Slot /> ··· 57 ]}> 58 <FontAwesomeIcon 59 icon="exclamation" 60 + style={ 61 + { 62 + color: 63 + colorMode === 'light' ? t.palette.white : t.palette.black, 64 + } as FontAwesomeIconStyle 65 + } 66 size={24} 67 /> 68 </View> ··· 70 <Text style={[a.text_center, a.font_heavy, a.text_2xl, a.mb_md]}> 71 {title} 72 </Text> 73 + <Text style={[a.text_center, a.text_md, a.mb_xl]}> {message} </Text> 74 {details && ( 75 <View 76 style={[ ··· 105 )}> 106 <ButtonIcon icon={ArrowRotateCounterClockwiseIcon} /> 107 <ButtonText> 108 + <Trans context="action"> Try again </Trans> 109 </ButtonText> 110 </Button> 111 </View>
+37 -13
src/view/com/util/forms/NativeDropdown.tsx
··· 3 Platform, 4 Pressable, 5 StyleSheet, 6 View, 7 type ViewStyle, 8 } from 'react-native' ··· 11 import * as DropdownMenu from 'zeego/dropdown-menu' 12 import {type MenuItemCommonProps} from 'zeego/lib/typescript/menu' 13 14 - import {usePalette} from '#/lib/hooks/usePalette' 15 - import {useTheme} from '#/lib/ThemeContext' 16 import {isIOS} from '#/platform/detection' 17 import {Portal} from '#/components/Portal' 18 19 // Custom Dropdown Menu Components ··· 43 export const DropdownMenuTrigger = DropdownMenu.create( 44 (props: TriggerProps) => { 45 const theme = useTheme() 46 - const defaultCtrlColor = theme.palette.default.postCtrl 47 48 return ( 49 // This Pressable doesn't actually do anything other than ··· 80 export const DropdownMenuItem = DropdownMenu.create( 81 (props: ItemProps & {testID?: string}) => { 82 const theme = useTheme() 83 const [focused, setFocused] = React.useState(false) 84 - const backgroundColor = theme.colorScheme === 'dark' ? '#fff1' : '#0001' 85 86 return ( 87 <DropdownMenu.Item ··· 107 */ 108 export const DropdownMenuItemTitle = DropdownMenu.create( 109 (props: TitleProps) => { 110 - const pal = usePalette('default') 111 return ( 112 <DropdownMenu.ItemTitle 113 {...props} 114 - style={[props.style, pal.text, styles.itemTitle]} 115 /> 116 ) 117 }, ··· 132 */ 133 export const DropdownMenuSeparator = DropdownMenu.create( 134 (props: SeparatorProps) => { 135 - const pal = usePalette('default') 136 const theme = useTheme() 137 const {borderColor: separatorColor} = 138 - theme.colorScheme === 'dark' ? pal.borderDark : pal.border 139 return ( 140 <DropdownMenu.Separator 141 {...props} ··· 185 accessibilityLabel, 186 accessibilityHint, 187 }: React.PropsWithChildren<Props>) { 188 - const pal = usePalette('default') 189 const theme = useTheme() 190 const [isOpen, setIsOpen] = React.useState(false) 191 - const dropDownBackgroundColor = 192 - theme.colorScheme === 'dark' ? pal.btn : pal.viewLight 193 194 return ( 195 <> ··· 234 <FontAwesomeIcon 235 icon={item.icon.web} 236 size={20} 237 - style={[pal.text]} 238 /> 239 </DropdownMenuItemIcon> 240 )} ··· 255 <FontAwesomeIcon 256 icon={item.icon.web} 257 size={20} 258 - style={[pal.text]} 259 /> 260 </DropdownMenuItemIcon> 261 )}
··· 3 Platform, 4 Pressable, 5 StyleSheet, 6 + type TextStyle, 7 View, 8 type ViewStyle, 9 } from 'react-native' ··· 12 import * as DropdownMenu from 'zeego/dropdown-menu' 13 import {type MenuItemCommonProps} from 'zeego/lib/typescript/menu' 14 15 import {isIOS} from '#/platform/detection' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 import {Portal} from '#/components/Portal' 19 20 // Custom Dropdown Menu Components ··· 44 export const DropdownMenuTrigger = DropdownMenu.create( 45 (props: TriggerProps) => { 46 const theme = useTheme() 47 + 48 + const defaultCtrlColor = theme.palette.contrast_500 49 50 return ( 51 // This Pressable doesn't actually do anything other than ··· 82 export const DropdownMenuItem = DropdownMenu.create( 83 (props: ItemProps & {testID?: string}) => { 84 const theme = useTheme() 85 + const colorMode = useColorModeTheme() 86 const [focused, setFocused] = React.useState(false) 87 + const backgroundColor = 88 + colorMode === 'light' ? theme.palette.black : theme.palette.white 89 90 return ( 91 <DropdownMenu.Item ··· 111 */ 112 export const DropdownMenuItemTitle = DropdownMenu.create( 113 (props: TitleProps) => { 114 + const theme = useTheme() 115 + const colorMode = useColorModeTheme() 116 + 117 return ( 118 <DropdownMenu.ItemTitle 119 {...props} 120 + style={[ 121 + props.style, 122 + { 123 + color: 124 + colorMode === 'light' ? theme.palette.black : theme.palette.white, 125 + }, 126 + styles.itemTitle, 127 + ]} 128 /> 129 ) 130 }, ··· 145 */ 146 export const DropdownMenuSeparator = DropdownMenu.create( 147 (props: SeparatorProps) => { 148 const theme = useTheme() 149 + const colorMode = useColorModeTheme() 150 const {borderColor: separatorColor} = 151 + colorMode === 'dark' 152 + ? { 153 + borderColor: theme.palette.contrast_200, 154 + } 155 + : { 156 + borderColor: theme.palette.contrast_100, 157 + } 158 return ( 159 <DropdownMenu.Separator 160 {...props} ··· 204 accessibilityLabel, 205 accessibilityHint, 206 }: React.PropsWithChildren<Props>) { 207 const theme = useTheme() 208 + const colorMode = useColorModeTheme() 209 const [isOpen, setIsOpen] = React.useState(false) 210 + const dropDownBackgroundColor = { 211 + backgroundColor: theme.palette.contrast_25, 212 + } 213 + 214 + const textStyle: TextStyle = { 215 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 216 + } 217 218 return ( 219 <> ··· 258 <FontAwesomeIcon 259 icon={item.icon.web} 260 size={20} 261 + style={[textStyle]} 262 /> 263 </DropdownMenuItemIcon> 264 )} ··· 279 <FontAwesomeIcon 280 icon={item.icon.web} 281 size={20} 282 + style={[textStyle]} 283 /> 284 </DropdownMenuItemIcon> 285 )}
+39 -11
src/view/com/util/forms/NativeDropdown.web.tsx
··· 3 Pressable, 4 StyleSheet, 5 Text, 6 type View, 7 type ViewStyle, 8 } from 'react-native' ··· 12 import {type MenuItemCommonProps} from 'zeego/lib/typescript/menu' 13 14 import {HITSLOP_10} from '#/lib/constants' 15 - import {usePalette} from '#/lib/hooks/usePalette' 16 - import {useTheme} from '#/lib/ThemeContext' 17 18 // Custom Dropdown Menu Components 19 // == ··· 22 23 type ItemProps = React.ComponentProps<(typeof DropdownMenu)['Item']> 24 export const DropdownMenuItem = (props: ItemProps & {testID?: string}) => { 25 const theme = useTheme() 26 - const [focused, setFocused] = React.useState(false) 27 - const backgroundColor = theme.colorScheme === 'dark' ? '#fff1' : '#0001' 28 29 return ( 30 <DropdownMenu.Item ··· 163 items: DropdownItem[] 164 menuRef: React.RefObject<HTMLDivElement | null> 165 }) { 166 - const pal = usePalette('default') 167 const theme = useTheme() 168 const dropDownBackgroundColor = 169 - theme.colorScheme === 'dark' ? pal.btn : pal.view 170 const {borderColor: separatorColor} = 171 - theme.colorScheme === 'dark' ? pal.borderDark : pal.border 172 173 return ( 174 <DropdownMenu.Content ··· 200 <DropdownMenuItem 201 key={getKey(item.label, index, item.testID)} 202 onSelect={item.onPress}> 203 - <Text selectable={false} style={[pal.text, styles.itemTitle]}> 204 {item.label} 205 </Text> 206 {item.icon && ( 207 <FontAwesomeIcon 208 icon={item.icon.web} 209 size={20} 210 - color={pal.colors.textLight} 211 /> 212 )} 213 </DropdownMenuItem> ··· 218 <DropdownMenuItem 219 key={getKey(item.label, index, item.testID)} 220 onSelect={item.onPress}> 221 - <Text selectable={false} style={[pal.text, styles.itemTitle]}> 222 {item.label} 223 </Text> 224 {item.icon && ( 225 <FontAwesomeIcon 226 icon={item.icon.web} 227 size={20} 228 - color={pal.colors.textLight} 229 /> 230 )} 231 </DropdownMenuItem>
··· 3 Pressable, 4 StyleSheet, 5 Text, 6 + type TextStyle, 7 type View, 8 type ViewStyle, 9 } from 'react-native' ··· 13 import {type MenuItemCommonProps} from 'zeego/lib/typescript/menu' 14 15 import {HITSLOP_10} from '#/lib/constants' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 19 // Custom Dropdown Menu Components 20 // == ··· 23 24 type ItemProps = React.ComponentProps<(typeof DropdownMenu)['Item']> 25 export const DropdownMenuItem = (props: ItemProps & {testID?: string}) => { 26 + const [focused, setFocused] = React.useState(false) 27 const theme = useTheme() 28 + const colorMode = useColorModeTheme() 29 + const backgroundColor = 30 + colorMode === 'light' ? theme.palette.black : theme.palette.white 31 32 return ( 33 <DropdownMenu.Item ··· 166 items: DropdownItem[] 167 menuRef: React.RefObject<HTMLDivElement | null> 168 }) { 169 const theme = useTheme() 170 + const colorMode = useColorModeTheme() 171 const dropDownBackgroundColor = 172 + colorMode === 'dark' 173 + ? { 174 + backgroundColor: theme.palette.contrast_25, 175 + } 176 + : { 177 + backgroundColor: 178 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 179 + } 180 const {borderColor: separatorColor} = 181 + colorMode === 'dark' 182 + ? { 183 + borderColor: theme.palette.contrast_200, 184 + } 185 + : { 186 + borderColor: theme.palette.contrast_100, 187 + } 188 + 189 + const textStyle: TextStyle = { 190 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 191 + } 192 193 return ( 194 <DropdownMenu.Content ··· 220 <DropdownMenuItem 221 key={getKey(item.label, index, item.testID)} 222 onSelect={item.onPress}> 223 + <Text selectable={false} style={[textStyle, styles.itemTitle]}> 224 {item.label} 225 </Text> 226 {item.icon && ( 227 <FontAwesomeIcon 228 icon={item.icon.web} 229 size={20} 230 + color={ 231 + colorMode === 'light' 232 + ? theme.palette.white 233 + : theme.palette.black 234 + } 235 /> 236 )} 237 </DropdownMenuItem> ··· 242 <DropdownMenuItem 243 key={getKey(item.label, index, item.testID)} 244 onSelect={item.onPress}> 245 + <Text selectable={false} style={[textStyle, styles.itemTitle]}> 246 {item.label} 247 </Text> 248 {item.icon && ( 249 <FontAwesomeIcon 250 icon={item.icon.web} 251 size={20} 252 + color={ 253 + colorMode === 'light' 254 + ? theme.palette.white 255 + : theme.palette.black 256 + } 257 /> 258 )} 259 </DropdownMenuItem>
+35 -11
src/view/com/util/layouts/LoggedOutLayout.tsx
··· 1 - import {ScrollView, StyleSheet, View} from 'react-native' 2 import type React from 'react' 3 4 import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' 5 import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' 6 - import {usePalette} from '#/lib/hooks/usePalette' 7 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 8 import {isWeb} from '#/platform/detection' 9 - import {atoms as a} from '#/alf' 10 import {Text} from '../text/Text' 11 12 export const LoggedOutLayout = ({ ··· 22 scrollable?: boolean 23 }>) => { 24 const {isMobile, isTabletOrMobile} = useWebMediaQueries() 25 - const pal = usePalette('default') 26 - const sideBg = useColorSchemeStyle(pal.viewLight, pal.view) 27 - const contentBg = useColorSchemeStyle(pal.view, { 28 - backgroundColor: pal.colors.background, 29 - borderColor: pal.colors.border, 30 borderLeftWidth: 1, 31 }) 32 ··· 54 <View style={[styles.side, sideBg]}> 55 <Text 56 style={[ 57 - pal.textLight, 58 styles.leadinText, 59 isTabletOrMobile && styles.leadinTextSmall, 60 ]}> ··· 62 </Text> 63 <Text 64 style={[ 65 - pal.link, 66 styles.titleText, 67 isTabletOrMobile && styles.titleTextSmall, 68 ]}> 69 {title} 70 </Text> 71 - <Text type="2xl-medium" style={[pal.textLight, styles.descriptionText]}> 72 {description} 73 </Text> 74 </View>
··· 1 + import {ScrollView, StyleSheet, View, type ViewStyle} from 'react-native' 2 import type React from 'react' 3 4 import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle' 5 import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible' 6 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 7 import {isWeb} from '#/platform/detection' 8 + import {atoms as a, useTheme} from '#/alf' 9 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 10 import {Text} from '../text/Text' 11 12 export const LoggedOutLayout = ({ ··· 22 scrollable?: boolean 23 }>) => { 24 const {isMobile, isTabletOrMobile} = useWebMediaQueries() 25 + const theme = useTheme() 26 + const colorMode = useColorModeTheme() 27 + const viewStyle: ViewStyle = { 28 + backgroundColor: 29 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 30 + } 31 + const sideBg = useColorSchemeStyle( 32 + {backgroundColor: theme.palette.contrast_25}, 33 + viewStyle, 34 + ) 35 + const contentBg = useColorSchemeStyle(viewStyle, { 36 + backgroundColor: 37 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 38 + borderColor: theme.palette.contrast_25, 39 borderLeftWidth: 1, 40 }) 41 ··· 63 <View style={[styles.side, sideBg]}> 64 <Text 65 style={[ 66 + { 67 + color: 68 + colorMode === 'dark' 69 + ? theme.palette.contrast_600 70 + : theme.palette.contrast_700, 71 + }, 72 styles.leadinText, 73 isTabletOrMobile && styles.leadinTextSmall, 74 ]}> ··· 76 </Text> 77 <Text 78 style={[ 79 + {color: theme.palette.primary_500}, 80 styles.titleText, 81 isTabletOrMobile && styles.titleTextSmall, 82 ]}> 83 {title} 84 </Text> 85 + <Text 86 + type="2xl-medium" 87 + style={[ 88 + { 89 + color: 90 + colorMode === 'dark' 91 + ? theme.palette.contrast_600 92 + : theme.palette.contrast_700, 93 + }, 94 + styles.descriptionText, 95 + ]}> 96 {description} 97 </Text> 98 </View>
+9 -3
src/view/icons/Logomark.tsx
··· 1 import Svg, {Path, type PathProps, type SvgProps} from 'react-native-svg' 2 3 - import {usePalette} from '#/lib/hooks/usePalette' 4 5 const ratio = 54 / 61 6 ··· 8 fill, 9 ...rest 10 }: {fill?: PathProps['fill']} & SvgProps) { 11 - const pal = usePalette('default') 12 // @ts-ignore it's fiiiiine 13 const size = parseInt(rest.width || 32) 14 ··· 20 width={size} 21 height={Number(size) * ratio}> 22 <Path 23 - fill={fill || pal.text.color} 24 d="M13.223 3.602C20.215 8.832 27.738 19.439 30.5 25.13c2.762-5.691 10.284-16.297 17.278-21.528C52.824-.172 61-3.093 61 6.2c0 1.856-1.068 15.59-1.694 17.82-2.178 7.752-10.112 9.73-17.17 8.532 12.337 2.092 15.475 9.021 8.697 15.95-12.872 13.159-18.5-3.302-19.943-7.52-.264-.773-.388-1.135-.39-.827-.002-.308-.126.054-.39.827-1.442 4.218-7.071 20.679-19.943 7.52-6.778-6.929-3.64-13.858 8.697-15.95-7.058 1.197-14.992-.78-17.17-8.532C1.068 21.79 0 8.056 0 6.2 0-3.093 8.176-.172 13.223 3.602Z" 25 /> 26 </Svg>
··· 1 import Svg, {Path, type PathProps, type SvgProps} from 'react-native-svg' 2 3 + import {useTheme} from '#/alf' 4 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 5 6 const ratio = 54 / 61 7 ··· 9 fill, 10 ...rest 11 }: {fill?: PathProps['fill']} & SvgProps) { 12 + const theme = useTheme() 13 + const colorMode = useColorModeTheme() 14 // @ts-ignore it's fiiiiine 15 const size = parseInt(rest.width || 32) 16 ··· 22 width={size} 23 height={Number(size) * ratio}> 24 <Path 25 + fill={ 26 + fill || colorMode === 'light' 27 + ? theme.palette.black 28 + : theme.palette.white 29 + } 30 d="M13.223 3.602C20.215 8.832 27.738 19.439 30.5 25.13c2.762-5.691 10.284-16.297 17.278-21.528C52.824-.172 61-3.093 61 6.2c0 1.856-1.068 15.59-1.694 17.82-2.178 7.752-10.112 9.73-17.17 8.532 12.337 2.092 15.475 9.021 8.697 15.95-12.872 13.159-18.5-3.302-19.943-7.52-.264-.773-.388-1.135-.39-.827-.002-.308-.126.054-.39.827-1.442 4.218-7.071 20.679-19.943 7.52-6.778-6.929-3.64-13.858 8.697-15.95-7.058 1.197-14.992-.78-17.17-8.532C1.068 21.79 0 8.056 0 6.2 0-3.093 8.176-.172 13.223 3.602Z" 31 /> 32 </Svg>
+9 -3
src/view/icons/Logotype.tsx
··· 1 import Svg, {Path, type PathProps, type SvgProps} from 'react-native-svg' 2 3 - import {usePalette} from '#/lib/hooks/usePalette' 4 5 const ratio = 17 / 64 6 ··· 8 fill, 9 ...rest 10 }: {fill?: PathProps['fill']} & SvgProps) { 11 - const pal = usePalette('default') 12 // @ts-ignore it's fiiiiine 13 const size = parseInt(rest.width || 32) 14 ··· 20 width={size} 21 height={Number(size) * ratio}> 22 <Path 23 - fill={fill || pal.text.color} 24 d="M8.478 6.252c1.503.538 2.3 1.78 2.3 3.172 0 2.356-1.576 3.785-4.6 3.785H0V0h5.974c2.875 0 4.267 1.466 4.267 3.413 0 1.3-.594 2.245-1.763 2.839Zm-2.69-4.193H2.504v3.45h3.284c1.28 0 1.967-.667 1.967-1.78 0-1.02-.705-1.67-1.967-1.67Zm-3.284 9.072h3.544c1.41 0 2.17-.65 2.17-1.818 0-1.224-.723-1.837-2.17-1.837H2.504v3.655ZM14.251 13.209h-2.337V0h2.337v13.209ZM22.001 8.998V3.636h2.338v9.573h-2.263v-1.392c-.724 1.076-1.726 1.614-3.006 1.614-2.022 0-3.34-1.224-3.34-3.45V3.636h2.338v5.955c0 1.206.594 1.818 1.8 1.818 1.132 0 2.133-.835 2.133-2.411ZM34.979 8.59v.556h-7.161c.167 1.651 1.076 2.467 2.486 2.467 1.076 0 1.8-.463 2.189-1.372h2.244c-.5 1.947-2.17 3.19-4.452 3.19-1.428 0-2.579-.463-3.45-1.372-.872-.91-1.318-2.115-1.318-3.637 0-1.502.427-2.708 1.299-3.636.872-.909 2.004-1.372 3.432-1.372 1.447 0 2.597.482 3.45 1.428.854.946 1.28 2.208 1.28 3.747Zm-4.75-3.358c-1.28 0-2.17.742-2.393 2.281h4.805c-.204-1.391-1.057-2.281-2.411-2.281ZM40.16 13.469c-2.783 0-4.249-1.095-4.379-3.303h2.282c.13 1.188.724 1.633 2.134 1.633 1.261 0 1.892-.39 1.892-1.15 0-.687-.445-1.02-1.874-1.262l-1.094-.185c-2.097-.353-3.136-1.318-3.136-2.894 0-1.8 1.429-2.894 3.97-2.894 2.728 0 4.138 1.075 4.23 3.246h-2.207c-.056-1.169-.742-1.577-2.023-1.577-1.113 0-1.67.371-1.67 1.113 0 .668.483.965 1.596 1.169l1.206.186c2.32.426 3.32 1.28 3.32 2.912 0 1.93-1.557 3.006-4.247 3.006ZM54.667 13.209h-2.671l-2.783-4.453-1.447 1.447v3.006h-2.3V0h2.3v7.606l3.896-3.97h2.783l-3.618 3.618 3.84 5.955ZM60.772 6.048l.78-2.412H64l-3.692 10.352c-.39 1.057-.872 1.818-1.484 2.245-.612.426-1.484.63-2.634.63-.39 0-.724-.018-1.02-.055V14.97h.89c1.057 0 1.577-.65 1.577-1.54 0-.445-.149-1.094-.446-1.929l-2.746-7.866h2.487l.779 2.393c.575 1.8 1.076 3.58 1.521 5.343.408-1.521.928-3.302 1.54-5.324Z" 25 /> 26 </Svg>
··· 1 import Svg, {Path, type PathProps, type SvgProps} from 'react-native-svg' 2 3 + import {useTheme} from '#/alf' 4 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 5 6 const ratio = 17 / 64 7 ··· 9 fill, 10 ...rest 11 }: {fill?: PathProps['fill']} & SvgProps) { 12 + const theme = useTheme() 13 + const colorMode = useColorModeTheme() 14 // @ts-ignore it's fiiiiine 15 const size = parseInt(rest.width || 32) 16 ··· 22 width={size} 23 height={Number(size) * ratio}> 24 <Path 25 + fill={ 26 + fill || colorMode === 'light' 27 + ? theme.palette.black 28 + : theme.palette.white 29 + } 30 d="M8.478 6.252c1.503.538 2.3 1.78 2.3 3.172 0 2.356-1.576 3.785-4.6 3.785H0V0h5.974c2.875 0 4.267 1.466 4.267 3.413 0 1.3-.594 2.245-1.763 2.839Zm-2.69-4.193H2.504v3.45h3.284c1.28 0 1.967-.667 1.967-1.78 0-1.02-.705-1.67-1.967-1.67Zm-3.284 9.072h3.544c1.41 0 2.17-.65 2.17-1.818 0-1.224-.723-1.837-2.17-1.837H2.504v3.655ZM14.251 13.209h-2.337V0h2.337v13.209ZM22.001 8.998V3.636h2.338v9.573h-2.263v-1.392c-.724 1.076-1.726 1.614-3.006 1.614-2.022 0-3.34-1.224-3.34-3.45V3.636h2.338v5.955c0 1.206.594 1.818 1.8 1.818 1.132 0 2.133-.835 2.133-2.411ZM34.979 8.59v.556h-7.161c.167 1.651 1.076 2.467 2.486 2.467 1.076 0 1.8-.463 2.189-1.372h2.244c-.5 1.947-2.17 3.19-4.452 3.19-1.428 0-2.579-.463-3.45-1.372-.872-.91-1.318-2.115-1.318-3.637 0-1.502.427-2.708 1.299-3.636.872-.909 2.004-1.372 3.432-1.372 1.447 0 2.597.482 3.45 1.428.854.946 1.28 2.208 1.28 3.747Zm-4.75-3.358c-1.28 0-2.17.742-2.393 2.281h4.805c-.204-1.391-1.057-2.281-2.411-2.281ZM40.16 13.469c-2.783 0-4.249-1.095-4.379-3.303h2.282c.13 1.188.724 1.633 2.134 1.633 1.261 0 1.892-.39 1.892-1.15 0-.687-.445-1.02-1.874-1.262l-1.094-.185c-2.097-.353-3.136-1.318-3.136-2.894 0-1.8 1.429-2.894 3.97-2.894 2.728 0 4.138 1.075 4.23 3.246h-2.207c-.056-1.169-.742-1.577-2.023-1.577-1.113 0-1.67.371-1.67 1.113 0 .668.483.965 1.596 1.169l1.206.186c2.32.426 3.32 1.28 3.32 2.912 0 1.93-1.557 3.006-4.247 3.006ZM54.667 13.209h-2.671l-2.783-4.453-1.447 1.447v3.006h-2.3V0h2.3v7.606l3.896-3.97h2.783l-3.618 3.618 3.84 5.955ZM60.772 6.048l.78-2.412H64l-3.692 10.352c-.39 1.057-.872 1.818-1.484 2.245-.612.426-1.484.63-2.634.63-.39 0-.724-.018-1.02-.055V14.97h.89c1.057 0 1.577-.65 1.577-1.54 0-.445-.149-1.094-.446-1.929l-2.746-7.866h2.487l.779 2.393c.575 1.8 1.076 3.58 1.521 5.343.408-1.521.928-3.302 1.54-5.324Z" 31 /> 32 </Svg>
+22 -5
src/view/screens/CommunityGuidelines.tsx
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import { 9 type CommonNavigatorParams, 10 type NativeStackScreenProps, ··· 14 import {TextLink} from '#/view/com/util/Link' 15 import {Text} from '#/view/com/util/text/Text' 16 import {ScrollView} from '#/view/com/util/Views' 17 import * as Layout from '#/components/Layout' 18 import {ViewHeader} from '../com/util/ViewHeader' 19 ··· 22 'CommunityGuidelines' 23 > 24 export const CommunityGuidelinesScreen = (_props: Props) => { 25 - const pal = usePalette('default') 26 const {_} = useLingui() 27 const setMinimalShellMode = useSetMinimalShellMode() 28 ··· 35 return ( 36 <Layout.Screen> 37 <ViewHeader title={_(msg`Community Guidelines`)} /> 38 - <ScrollView style={[s.hContentRegion, pal.view]}> 39 <View style={[s.p20]}> 40 - <Text style={pal.text}> 41 <Trans> 42 The Community Guidelines have been moved to{' '} 43 <TextLink 44 - style={pal.link} 45 href="https://bsky.social/about/support/community-guidelines" 46 text="bsky.social/about/support/community-guidelines" 47 />
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 import { 8 type CommonNavigatorParams, 9 type NativeStackScreenProps, ··· 13 import {TextLink} from '#/view/com/util/Link' 14 import {Text} from '#/view/com/util/text/Text' 15 import {ScrollView} from '#/view/com/util/Views' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 import * as Layout from '#/components/Layout' 19 import {ViewHeader} from '../com/util/ViewHeader' 20 ··· 23 'CommunityGuidelines' 24 > 25 export const CommunityGuidelinesScreen = (_props: Props) => { 26 + const theme = useTheme() 27 + const colorMode = useColorModeTheme() 28 const {_} = useLingui() 29 const setMinimalShellMode = useSetMinimalShellMode() 30 ··· 37 return ( 38 <Layout.Screen> 39 <ViewHeader title={_(msg`Community Guidelines`)} /> 40 + <ScrollView 41 + style={[ 42 + s.hContentRegion, 43 + { 44 + backgroundColor: 45 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 46 + }, 47 + ]}> 48 <View style={[s.p20]}> 49 + <Text 50 + style={{ 51 + color: 52 + colorMode === 'light' 53 + ? theme.palette.black 54 + : theme.palette.white, 55 + }}> 56 <Trans> 57 The Community Guidelines have been moved to{' '} 58 <TextLink 59 + style={{ 60 + color: theme.palette.primary_500, 61 + }} 62 href="https://bsky.social/about/support/community-guidelines" 63 text="bsky.social/about/support/community-guidelines" 64 />
+22 -5
src/view/screens/CopyrightPolicy.tsx
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import { 9 type CommonNavigatorParams, 10 type NativeStackScreenProps, ··· 14 import {TextLink} from '#/view/com/util/Link' 15 import {Text} from '#/view/com/util/text/Text' 16 import {ScrollView} from '#/view/com/util/Views' 17 import * as Layout from '#/components/Layout' 18 import {ViewHeader} from '../com/util/ViewHeader' 19 20 type Props = NativeStackScreenProps<CommonNavigatorParams, 'CopyrightPolicy'> 21 export const CopyrightPolicyScreen = (_props: Props) => { 22 - const pal = usePalette('default') 23 const {_} = useLingui() 24 const setMinimalShellMode = useSetMinimalShellMode() 25 ··· 32 return ( 33 <Layout.Screen> 34 <ViewHeader title={_(msg`Copyright Policy`)} /> 35 - <ScrollView style={[s.hContentRegion, pal.view]}> 36 <View style={[s.p20]}> 37 - <Text style={pal.text}> 38 <Trans> 39 The Copyright Policy has been moved to{' '} 40 <TextLink 41 - style={pal.link} 42 href="https://bsky.social/about/support/copyright" 43 text="bsky.social/about/support/copyright" 44 />
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 import { 8 type CommonNavigatorParams, 9 type NativeStackScreenProps, ··· 13 import {TextLink} from '#/view/com/util/Link' 14 import {Text} from '#/view/com/util/text/Text' 15 import {ScrollView} from '#/view/com/util/Views' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 import * as Layout from '#/components/Layout' 19 import {ViewHeader} from '../com/util/ViewHeader' 20 21 type Props = NativeStackScreenProps<CommonNavigatorParams, 'CopyrightPolicy'> 22 export const CopyrightPolicyScreen = (_props: Props) => { 23 + const theme = useTheme() 24 + const colorMode = useColorModeTheme() 25 const {_} = useLingui() 26 const setMinimalShellMode = useSetMinimalShellMode() 27 ··· 34 return ( 35 <Layout.Screen> 36 <ViewHeader title={_(msg`Copyright Policy`)} /> 37 + <ScrollView 38 + style={[ 39 + s.hContentRegion, 40 + { 41 + backgroundColor: 42 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 43 + }, 44 + ]}> 45 <View style={[s.p20]}> 46 + <Text 47 + style={{ 48 + color: 49 + colorMode === 'light' 50 + ? theme.palette.black 51 + : theme.palette.white, 52 + }}> 53 <Trans> 54 The Copyright Policy has been moved to{' '} 55 <TextLink 56 + style={{ 57 + color: theme.palette.primary_500, 58 + }} 59 href="https://bsky.social/about/support/copyright" 60 text="bsky.social/about/support/copyright" 61 />
+110 -72
src/view/screens/Debug.tsx
··· 1 import React from 'react' 2 - import {ScrollView, View} from 'react-native' 3 import {msg} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 - import {usePalette} from '#/lib/hooks/usePalette' 7 import { 8 type CommonNavigatorParams, 9 type NativeStackScreenProps, 10 } from '#/lib/routes/types' 11 import {s} from '#/lib/styles' 12 - import {type PaletteColorName, ThemeProvider} from '#/lib/ThemeContext' 13 import {EmptyState} from '#/view/com/util/EmptyState' 14 import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 15 import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' ··· 20 import * as Toast from '#/view/com/util/Toast' 21 import {ViewHeader} from '#/view/com/util/ViewHeader' 22 import {ViewSelector} from '#/view/com/util/ViewSelector' 23 import * as Layout from '#/components/Layout' 24 25 const MAIN_VIEWS = ['Base', 'Controls', 'Error', 'Notifs'] ··· 54 onToggleColorScheme: () => void 55 }) { 56 const [currentView, setCurrentView] = React.useState<number>(0) 57 - const pal = usePalette('default') 58 const {_} = useLingui() 59 60 const renderItem = (item: any) => { ··· 84 const items = [{currentView}] 85 86 return ( 87 - <View style={[s.hContentRegion, pal.view]}> 88 <ViewHeader title={_(msg`Debug panel`)} /> 89 <ViewSelector 90 swipeEnabled ··· 98 } 99 100 function Heading({label}: {label: string}) { 101 - const pal = usePalette('default') 102 return ( 103 <View style={[s.pt10, s.pb5]}> 104 - <Text type="title-lg" style={pal.text}> 105 {label} 106 </Text> 107 </View> ··· 114 <Heading label="Typography" /> 115 <TypographyView /> 116 <Heading label="Palettes" /> 117 - <PaletteView palette="default" /> 118 - <PaletteView palette="primary" /> 119 - <PaletteView palette="secondary" /> 120 - <PaletteView palette="inverted" /> 121 - <PaletteView palette="error" /> 122 <Heading label="Empty state" /> 123 <EmptyStateView /> 124 <Heading label="Loading placeholders" /> ··· 197 </View> 198 ) 199 } 200 - 201 - function PaletteView({palette}: {palette: PaletteColorName}) { 202 - const defaultPal = usePalette('default') 203 - const pal = usePalette(palette) 204 - return ( 205 - <View style={[pal.view, pal.border, s.p10, s.mb5, s.border1]}> 206 - <Text style={[pal.text]}>{palette} colors</Text> 207 - <Text style={[pal.textLight]}>Light text</Text> 208 - <Text style={[pal.link]}>Link text</Text> 209 - {palette !== 'default' && ( 210 - <View style={[defaultPal.view]}> 211 - <Text style={[pal.textInverted]}>Inverted text</Text> 212 - </View> 213 - )} 214 - </View> 215 - ) 216 - } 217 218 function TypographyView() { 219 - const pal = usePalette('default') 220 return ( 221 - <View style={[pal.view]}> 222 - <Text type="2xl-thin" style={[pal.text]}> 223 '2xl-thin' lorem ipsum dolor 224 </Text> 225 - <Text type="2xl" style={[pal.text]}> 226 '2xl' lorem ipsum dolor 227 </Text> 228 - <Text type="2xl-medium" style={[pal.text]}> 229 '2xl-medium' lorem ipsum dolor 230 </Text> 231 - <Text type="2xl-bold" style={[pal.text]}> 232 '2xl-bold' lorem ipsum dolor 233 </Text> 234 - <Text type="2xl-heavy" style={[pal.text]}> 235 '2xl-heavy' lorem ipsum dolor 236 </Text> 237 - <Text type="xl-thin" style={[pal.text]}> 238 'xl-thin' lorem ipsum dolor 239 </Text> 240 - <Text type="xl" style={[pal.text]}> 241 'xl' lorem ipsum dolor 242 </Text> 243 - <Text type="xl-medium" style={[pal.text]}> 244 'xl-medium' lorem ipsum dolor 245 </Text> 246 - <Text type="xl-bold" style={[pal.text]}> 247 'xl-bold' lorem ipsum dolor 248 </Text> 249 - <Text type="xl-heavy" style={[pal.text]}> 250 'xl-heavy' lorem ipsum dolor 251 </Text> 252 - <Text type="lg-thin" style={[pal.text]}> 253 'lg-thin' lorem ipsum dolor 254 </Text> 255 - <Text type="lg" style={[pal.text]}> 256 'lg' lorem ipsum dolor 257 </Text> 258 - <Text type="lg-medium" style={[pal.text]}> 259 'lg-medium' lorem ipsum dolor 260 </Text> 261 - <Text type="lg-bold" style={[pal.text]}> 262 'lg-bold' lorem ipsum dolor 263 </Text> 264 - <Text type="lg-heavy" style={[pal.text]}> 265 'lg-heavy' lorem ipsum dolor 266 </Text> 267 - <Text type="md-thin" style={[pal.text]}> 268 'md-thin' lorem ipsum dolor 269 </Text> 270 - <Text type="md" style={[pal.text]}> 271 'md' lorem ipsum dolor 272 </Text> 273 - <Text type="md-medium" style={[pal.text]}> 274 'md-medium' lorem ipsum dolor 275 </Text> 276 - <Text type="md-bold" style={[pal.text]}> 277 'md-bold' lorem ipsum dolor 278 </Text> 279 - <Text type="md-heavy" style={[pal.text]}> 280 'md-heavy' lorem ipsum dolor 281 </Text> 282 - <Text type="sm-thin" style={[pal.text]}> 283 'sm-thin' lorem ipsum dolor 284 </Text> 285 - <Text type="sm" style={[pal.text]}> 286 'sm' lorem ipsum dolor 287 </Text> 288 - <Text type="sm-medium" style={[pal.text]}> 289 'sm-medium' lorem ipsum dolor 290 </Text> 291 - <Text type="sm-bold" style={[pal.text]}> 292 'sm-bold' lorem ipsum dolor 293 </Text> 294 - <Text type="sm-heavy" style={[pal.text]}> 295 'sm-heavy' lorem ipsum dolor 296 </Text> 297 - <Text type="xs-thin" style={[pal.text]}> 298 'xs-thin' lorem ipsum dolor 299 </Text> 300 - <Text type="xs" style={[pal.text]}> 301 'xs' lorem ipsum dolor 302 </Text> 303 - <Text type="xs-medium" style={[pal.text]}> 304 'xs-medium' lorem ipsum dolor 305 </Text> 306 - <Text type="xs-bold" style={[pal.text]}> 307 'xs-bold' lorem ipsum dolor 308 </Text> 309 - <Text type="xs-heavy" style={[pal.text]}> 310 'xs-heavy' lorem ipsum dolor 311 </Text> 312 313 - <Text type="title-2xl" style={[pal.text]}> 314 'title-2xl' lorem ipsum dolor 315 </Text> 316 - <Text type="title-xl" style={[pal.text]}> 317 'title-xl' lorem ipsum dolor 318 </Text> 319 - <Text type="title-lg" style={[pal.text]}> 320 'title-lg' lorem ipsum dolor 321 </Text> 322 - <Text type="title" style={[pal.text]}> 323 'title' lorem ipsum dolor 324 </Text> 325 - <Text type="button" style={[pal.text]}> 326 Button 327 </Text> 328 - <Text type="button-lg" style={[pal.text]}> 329 - Button-lg 330 </Text> 331 </View> 332 ) ··· 346 } 347 348 function ButtonsView() { 349 - const defaultPal = usePalette('default') 350 const buttonStyles = {marginRight: 5} 351 return ( 352 - <View style={[defaultPal.view]}> 353 <View style={[s.flexRow, s.mb5]}> 354 <Button type="primary" label="Primary solid" style={buttonStyles} /> 355 <Button type="secondary" label="Secondary solid" style={buttonStyles} /> ··· 394 } 395 396 function ToggleButtonsView() { 397 - const defaultPal = usePalette('default') 398 const buttonStyles = s.mb5 399 const [isSelected, setIsSelected] = React.useState(false) 400 const onToggle = () => setIsSelected(!isSelected) 401 return ( 402 - <View style={[defaultPal.view]}> 403 <ToggleButton 404 type="primary" 405 label="Primary solid"
··· 1 import React from 'react' 2 + import {ScrollView, type TextStyle, View} from 'react-native' 3 import {msg} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 import { 7 type CommonNavigatorParams, 8 type NativeStackScreenProps, 9 } from '#/lib/routes/types' 10 import {s} from '#/lib/styles' 11 + import {ThemeProvider} from '#/lib/ThemeContext' 12 import {EmptyState} from '#/view/com/util/EmptyState' 13 import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 14 import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' ··· 19 import * as Toast from '#/view/com/util/Toast' 20 import {ViewHeader} from '#/view/com/util/ViewHeader' 21 import {ViewSelector} from '#/view/com/util/ViewSelector' 22 + import {useTheme} from '#/alf' 23 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 24 import * as Layout from '#/components/Layout' 25 26 const MAIN_VIEWS = ['Base', 'Controls', 'Error', 'Notifs'] ··· 55 onToggleColorScheme: () => void 56 }) { 57 const [currentView, setCurrentView] = React.useState<number>(0) 58 + const theme = useTheme() 59 + const colorMode = useColorModeTheme() 60 const {_} = useLingui() 61 62 const renderItem = (item: any) => { ··· 86 const items = [{currentView}] 87 88 return ( 89 + <View 90 + style={[ 91 + s.hContentRegion, 92 + { 93 + backgroundColor: 94 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 95 + }, 96 + ]}> 97 <ViewHeader title={_(msg`Debug panel`)} /> 98 <ViewSelector 99 swipeEnabled ··· 107 } 108 109 function Heading({label}: {label: string}) { 110 + const theme = useTheme() 111 + const colorMode = useColorModeTheme() 112 + 113 return ( 114 <View style={[s.pt10, s.pb5]}> 115 + <Text 116 + type="title-lg" 117 + style={{ 118 + color: 119 + colorMode === 'light' ? theme.palette.black : theme.palette.white, 120 + }}> 121 {label} 122 </Text> 123 </View> ··· 130 <Heading label="Typography" /> 131 <TypographyView /> 132 <Heading label="Palettes" /> 133 + {/* <PaletteView /> */} 134 <Heading label="Empty state" /> 135 <EmptyStateView /> 136 <Heading label="Loading placeholders" /> ··· 209 </View> 210 ) 211 } 212 + // TODO: provide some way to view all the colours in a given theme. 213 + // since the concept of a 'palette' is mostly deprecated, we need to rethink this debug screen. 214 + // function PaletteView() { 215 + // const defaultTheme = useTheme() 216 + // const currColorMode = useColorModeTheme() 217 + // const pal = usePalette(palette) 218 + // return ( 219 + // <View style={[pal.view, pal.border, s.p10, s.mb5, s.border1]}> 220 + // <Text style={[pal.text]}> {palette} colors </Text> 221 + // <Text style={[pal.textLight]}> Light text </Text> 222 + // <Text style={[pal.link]}> Link text </Text> 223 + // {palette !== 'default' && ( 224 + // <View 225 + // style={{ 226 + // backgroundColor: 227 + // currColorMode === 'light' 228 + // ? defaultTheme.palette.white 229 + // : defaultTheme.palette.black, 230 + // }}> 231 + // <Text style={[pal.textInverted]}> Inverted text </Text> 232 + // </View> 233 + // )} 234 + // </View> 235 + // ) 236 + // } 237 238 function TypographyView() { 239 + const theme = useTheme() 240 + const colorMode = useColorModeTheme() 241 + const textStyle: TextStyle = { 242 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 243 + } 244 return ( 245 + <View 246 + style={{ 247 + backgroundColor: 248 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 249 + }}> 250 + <Text type="2xl-thin" style={textStyle}> 251 '2xl-thin' lorem ipsum dolor 252 </Text> 253 + <Text type="2xl" style={textStyle}> 254 '2xl' lorem ipsum dolor 255 </Text> 256 + <Text type="2xl-medium" style={textStyle}> 257 '2xl-medium' lorem ipsum dolor 258 </Text> 259 + <Text type="2xl-bold" style={textStyle}> 260 '2xl-bold' lorem ipsum dolor 261 </Text> 262 + <Text type="2xl-heavy" style={textStyle}> 263 '2xl-heavy' lorem ipsum dolor 264 </Text> 265 + <Text type="xl-thin" style={textStyle}> 266 'xl-thin' lorem ipsum dolor 267 </Text> 268 + <Text type="xl" style={textStyle}> 269 'xl' lorem ipsum dolor 270 </Text> 271 + <Text type="xl-medium" style={textStyle}> 272 'xl-medium' lorem ipsum dolor 273 </Text> 274 + <Text type="xl-bold" style={textStyle}> 275 'xl-bold' lorem ipsum dolor 276 </Text> 277 + <Text type="xl-heavy" style={textStyle}> 278 'xl-heavy' lorem ipsum dolor 279 </Text> 280 + <Text type="lg-thin" style={textStyle}> 281 'lg-thin' lorem ipsum dolor 282 </Text> 283 + <Text type="lg" style={textStyle}> 284 'lg' lorem ipsum dolor 285 </Text> 286 + <Text type="lg-medium" style={textStyle}> 287 'lg-medium' lorem ipsum dolor 288 </Text> 289 + <Text type="lg-bold" style={textStyle}> 290 'lg-bold' lorem ipsum dolor 291 </Text> 292 + <Text type="lg-heavy" style={textStyle}> 293 'lg-heavy' lorem ipsum dolor 294 </Text> 295 + <Text type="md-thin" style={textStyle}> 296 'md-thin' lorem ipsum dolor 297 </Text> 298 + <Text type="md" style={textStyle}> 299 'md' lorem ipsum dolor 300 </Text> 301 + <Text type="md-medium" style={textStyle}> 302 'md-medium' lorem ipsum dolor 303 </Text> 304 + <Text type="md-bold" style={textStyle}> 305 'md-bold' lorem ipsum dolor 306 </Text> 307 + <Text type="md-heavy" style={textStyle}> 308 'md-heavy' lorem ipsum dolor 309 </Text> 310 + <Text type="sm-thin" style={textStyle}> 311 'sm-thin' lorem ipsum dolor 312 </Text> 313 + <Text type="sm" style={textStyle}> 314 'sm' lorem ipsum dolor 315 </Text> 316 + <Text type="sm-medium" style={textStyle}> 317 'sm-medium' lorem ipsum dolor 318 </Text> 319 + <Text type="sm-bold" style={textStyle}> 320 'sm-bold' lorem ipsum dolor 321 </Text> 322 + <Text type="sm-heavy" style={textStyle}> 323 'sm-heavy' lorem ipsum dolor 324 </Text> 325 + <Text type="xs-thin" style={textStyle}> 326 'xs-thin' lorem ipsum dolor 327 </Text> 328 + <Text type="xs" style={textStyle}> 329 'xs' lorem ipsum dolor 330 </Text> 331 + <Text type="xs-medium" style={textStyle}> 332 'xs-medium' lorem ipsum dolor 333 </Text> 334 + <Text type="xs-bold" style={textStyle}> 335 'xs-bold' lorem ipsum dolor 336 </Text> 337 + <Text type="xs-heavy" style={textStyle}> 338 'xs-heavy' lorem ipsum dolor 339 </Text> 340 341 + <Text type="title-2xl" style={textStyle}> 342 'title-2xl' lorem ipsum dolor 343 </Text> 344 + <Text type="title-xl" style={textStyle}> 345 'title-xl' lorem ipsum dolor 346 </Text> 347 + <Text type="title-lg" style={textStyle}> 348 'title-lg' lorem ipsum dolor 349 </Text> 350 + <Text type="title" style={textStyle}> 351 'title' lorem ipsum dolor 352 </Text> 353 + <Text type="button" style={textStyle}> 354 Button 355 </Text> 356 + <Text type="button-lg" style={textStyle}> 357 + Button - lg 358 </Text> 359 </View> 360 ) ··· 374 } 375 376 function ButtonsView() { 377 + const theme = useTheme() 378 + const colorMode = useColorModeTheme() 379 const buttonStyles = {marginRight: 5} 380 return ( 381 + <View 382 + style={{ 383 + backgroundColor: 384 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 385 + }}> 386 <View style={[s.flexRow, s.mb5]}> 387 <Button type="primary" label="Primary solid" style={buttonStyles} /> 388 <Button type="secondary" label="Secondary solid" style={buttonStyles} /> ··· 427 } 428 429 function ToggleButtonsView() { 430 + const theme = useTheme() 431 + const colorMode = useColorModeTheme() 432 const buttonStyles = s.mb5 433 const [isSelected, setIsSelected] = React.useState(false) 434 const onToggle = () => setIsSelected(!isSelected) 435 return ( 436 + <View 437 + style={{ 438 + backgroundColor: 439 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 440 + }}> 441 <ToggleButton 442 type="primary" 443 label="Primary solid"
+19 -11
src/view/screens/Feeds.tsx
··· 7 import debounce from 'lodash.debounce' 8 9 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 10 - import {usePalette} from '#/lib/hooks/usePalette' 11 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 12 import {ComposeIcon2} from '#/lib/icons' 13 import { ··· 33 import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed' 34 import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType' 35 import {atoms as a, useTheme} from '#/alf' 36 import {ButtonIcon} from '#/components/Button' 37 import {Divider} from '#/components/Divider' 38 import * as FeedCard from '#/components/FeedCard' ··· 104 } 105 106 export function FeedsScreen(_props: Props) { 107 - const pal = usePalette('default') 108 const {openComposer} = useOpenComposer() 109 const {isMobile} = useWebMediaQueries() 110 const [query, setQuery] = React.useState('') ··· 423 return ( 424 <View 425 style={[ 426 - pal.border, 427 { 428 borderBottomWidth: 1, 429 }, 430 ]}> 431 <NoSavedFeedsOfAnyType /> ··· 469 paddingTop: 10, 470 paddingBottom: '150%', 471 }}> 472 - <Text type="lg" style={pal.textLight}> 473 - <Trans>No results found for "{query}"</Trans> 474 </Text> 475 </View> 476 ) ··· 478 return ( 479 <View 480 style={[ 481 - pal.border, 482 { 483 borderBottomWidth: 1, 484 }, 485 ]}> 486 <NoFollowingFeed /> ··· 490 return null 491 }, 492 [ 493 _, 494 - pal.border, 495 - pal.textLight, 496 query, 497 onChangeQuery, 498 onPressCancelSearch, ··· 508 <Layout.Header.BackButton /> 509 <Layout.Header.Content> 510 <Layout.Header.TitleText> 511 - <Trans>Feeds</Trans> 512 </Layout.Header.TitleText> 513 </Layout.Header.Content> 514 <Layout.Header.Slot> ··· 701 <IconCircle icon={ListSparkle_Stroke2_Corner0_Rounded} size="lg" /> 702 <View style={[a.flex_1, a.gap_xs]}> 703 <Text style={[a.flex_1, a.text_2xl, a.font_heavy, t.atoms.text]}> 704 - <Trans>My Feeds</Trans> 705 </Text> 706 <Text style={[t.atoms.text_contrast_high]}> 707 <Trans>All the feeds you've saved, right in one place.</Trans> ··· 727 /> 728 <View style={[a.flex_1, a.gap_sm]}> 729 <Text style={[a.flex_1, a.text_2xl, a.font_heavy, t.atoms.text]}> 730 - <Trans>Discover New Feeds</Trans> 731 </Text> 732 <Text style={[t.atoms.text_contrast_high]}> 733 <Trans>
··· 7 import debounce from 'lodash.debounce' 8 9 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 10 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 11 import {ComposeIcon2} from '#/lib/icons' 12 import { ··· 32 import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed' 33 import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType' 34 import {atoms as a, useTheme} from '#/alf' 35 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 36 import {ButtonIcon} from '#/components/Button' 37 import {Divider} from '#/components/Divider' 38 import * as FeedCard from '#/components/FeedCard' ··· 104 } 105 106 export function FeedsScreen(_props: Props) { 107 + const theme = useTheme() 108 + const colorMode = useColorModeTheme() 109 const {openComposer} = useOpenComposer() 110 const {isMobile} = useWebMediaQueries() 111 const [query, setQuery] = React.useState('') ··· 424 return ( 425 <View 426 style={[ 427 { 428 borderBottomWidth: 1, 429 + borderColor: theme.palette.contrast_100, 430 }, 431 ]}> 432 <NoSavedFeedsOfAnyType /> ··· 470 paddingTop: 10, 471 paddingBottom: '150%', 472 }}> 473 + <Text 474 + type="lg" 475 + style={{ 476 + color: 477 + colorMode === 'dark' 478 + ? theme.palette.contrast_600 479 + : theme.palette.contrast_700, 480 + }}> 481 + <Trans>No results found for "{query}" </Trans> 482 </Text> 483 </View> 484 ) ··· 486 return ( 487 <View 488 style={[ 489 { 490 borderBottomWidth: 1, 491 + borderColor: theme.palette.contrast_100, 492 }, 493 ]}> 494 <NoFollowingFeed /> ··· 498 return null 499 }, 500 [ 501 + colorMode, 502 + theme, 503 _, 504 query, 505 onChangeQuery, 506 onPressCancelSearch, ··· 516 <Layout.Header.BackButton /> 517 <Layout.Header.Content> 518 <Layout.Header.TitleText> 519 + <Trans>Feeds </Trans> 520 </Layout.Header.TitleText> 521 </Layout.Header.Content> 522 <Layout.Header.Slot> ··· 709 <IconCircle icon={ListSparkle_Stroke2_Corner0_Rounded} size="lg" /> 710 <View style={[a.flex_1, a.gap_xs]}> 711 <Text style={[a.flex_1, a.text_2xl, a.font_heavy, t.atoms.text]}> 712 + <Trans>My Feeds </Trans> 713 </Text> 714 <Text style={[t.atoms.text_contrast_high]}> 715 <Trans>All the feeds you've saved, right in one place.</Trans> ··· 735 /> 736 <View style={[a.flex_1, a.gap_sm]}> 737 <Text style={[a.flex_1, a.text_2xl, a.font_heavy, t.atoms.text]}> 738 + <Trans>Discover New Feeds </Trans> 739 </Text> 740 <Text style={[t.atoms.text_contrast_high]}> 741 <Trans>
+27 -5
src/view/screens/NotFound.tsx
··· 8 useNavigation, 9 } from '@react-navigation/native' 10 11 - import {usePalette} from '#/lib/hooks/usePalette' 12 import {type NavigationProp} from '#/lib/routes/types' 13 import {s} from '#/lib/styles' 14 import {useSetMinimalShellMode} from '#/state/shell' 15 import {Button} from '#/view/com/util/forms/Button' 16 import {Text} from '#/view/com/util/text/Text' 17 import {ViewHeader} from '#/view/com/util/ViewHeader' 18 import * as Layout from '#/components/Layout' 19 20 export const NotFoundScreen = () => { 21 - const pal = usePalette('default') 22 const {_} = useLingui() 23 const navigation = useNavigation<NavigationProp>() 24 const setMinimalShellMode = useSetMinimalShellMode() ··· 43 <Layout.Screen testID="notFoundView"> 44 <ViewHeader title={_(msg`Page Not Found`)} /> 45 <View style={styles.container}> 46 - <Text type="title-2xl" style={[pal.text, s.mb10]}> 47 - <Trans>Page not found</Trans> 48 </Text> 49 - <Text type="md" style={[pal.text, s.mb10]}> 50 <Trans> 51 We're sorry! We can't find the page you were looking for. 52 </Trans>
··· 8 useNavigation, 9 } from '@react-navigation/native' 10 11 import {type NavigationProp} from '#/lib/routes/types' 12 import {s} from '#/lib/styles' 13 import {useSetMinimalShellMode} from '#/state/shell' 14 import {Button} from '#/view/com/util/forms/Button' 15 import {Text} from '#/view/com/util/text/Text' 16 import {ViewHeader} from '#/view/com/util/ViewHeader' 17 + import {useTheme} from '#/alf' 18 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 19 import * as Layout from '#/components/Layout' 20 21 export const NotFoundScreen = () => { 22 + const theme = useTheme() 23 + const colorMode = useColorModeTheme() 24 const {_} = useLingui() 25 const navigation = useNavigation<NavigationProp>() 26 const setMinimalShellMode = useSetMinimalShellMode() ··· 45 <Layout.Screen testID="notFoundView"> 46 <ViewHeader title={_(msg`Page Not Found`)} /> 47 <View style={styles.container}> 48 + <Text 49 + type="title-2xl" 50 + style={[ 51 + { 52 + color: 53 + colorMode === 'light' 54 + ? theme.palette.black 55 + : theme.palette.white, 56 + }, 57 + s.mb10, 58 + ]}> 59 + <Trans>Page not found </Trans> 60 </Text> 61 + <Text 62 + type="md" 63 + style={[ 64 + { 65 + color: 66 + colorMode === 'light' 67 + ? theme.palette.black 68 + : theme.palette.white, 69 + }, 70 + s.mb10, 71 + ]}> 72 <Trans> 73 We're sorry! We can't find the page you were looking for. 74 </Trans>
+20 -5
src/view/screens/PrivacyPolicy.tsx
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import { 9 type CommonNavigatorParams, 10 type NativeStackScreenProps, ··· 14 import {TextLink} from '#/view/com/util/Link' 15 import {Text} from '#/view/com/util/text/Text' 16 import {ScrollView} from '#/view/com/util/Views' 17 import * as Layout from '#/components/Layout' 18 import {ViewHeader} from '../com/util/ViewHeader' 19 20 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'> 21 export const PrivacyPolicyScreen = (_props: Props) => { 22 - const pal = usePalette('default') 23 const {_} = useLingui() 24 const setMinimalShellMode = useSetMinimalShellMode() 25 ··· 32 return ( 33 <Layout.Screen> 34 <ViewHeader title={_(msg`Privacy Policy`)} /> 35 - <ScrollView style={[s.hContentRegion, pal.view]}> 36 <View style={[s.p20]}> 37 - <Text style={pal.text}> 38 <Trans> 39 The Privacy Policy has been moved to{' '} 40 <TextLink 41 - style={pal.link} 42 href="https://bsky.social/about/support/privacy-policy" 43 text="bsky.social/about/support/privacy-policy" 44 />
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 import { 8 type CommonNavigatorParams, 9 type NativeStackScreenProps, ··· 13 import {TextLink} from '#/view/com/util/Link' 14 import {Text} from '#/view/com/util/text/Text' 15 import {ScrollView} from '#/view/com/util/Views' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 import * as Layout from '#/components/Layout' 19 import {ViewHeader} from '../com/util/ViewHeader' 20 21 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PrivacyPolicy'> 22 export const PrivacyPolicyScreen = (_props: Props) => { 23 + const theme = useTheme() 24 + const colorMode = useColorModeTheme() 25 const {_} = useLingui() 26 const setMinimalShellMode = useSetMinimalShellMode() 27 ··· 34 return ( 35 <Layout.Screen> 36 <ViewHeader title={_(msg`Privacy Policy`)} /> 37 + <ScrollView 38 + style={[ 39 + s.hContentRegion, 40 + { 41 + backgroundColor: 42 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 43 + }, 44 + ]}> 45 <View style={[s.p20]}> 46 + <Text 47 + style={{ 48 + color: 49 + colorMode === 'light' 50 + ? theme.palette.black 51 + : theme.palette.white, 52 + }}> 53 <Trans> 54 The Privacy Policy has been moved to{' '} 55 <TextLink 56 + style={{color: theme.palette.primary_500}} 57 href="https://bsky.social/about/support/privacy-policy" 58 text="bsky.social/about/support/privacy-policy" 59 />
+29 -7
src/view/screens/Support.tsx
··· 4 import {useFocusEffect} from '@react-navigation/native' 5 6 import {HELP_DESK_URL} from '#/lib/constants' 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import { 9 type CommonNavigatorParams, 10 type NativeStackScreenProps, ··· 15 import {Text} from '#/view/com/util/text/Text' 16 import {ViewHeader} from '#/view/com/util/ViewHeader' 17 import {CenteredView} from '#/view/com/util/Views' 18 import * as Layout from '#/components/Layout' 19 20 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'> 21 export const SupportScreen = (_props: Props) => { 22 - const pal = usePalette('default') 23 const setMinimalShellMode = useSetMinimalShellMode() 24 const {_} = useLingui() 25 ··· 33 <Layout.Screen> 34 <ViewHeader title={_(msg`Support`)} /> 35 <CenteredView> 36 - <Text type="title-xl" style={[pal.text, s.p20, s.pb5]}> 37 - <Trans>Support</Trans> 38 </Text> 39 - <Text style={[pal.text, s.p20]}> 40 <Trans> 41 - The support form has been moved. If you need help, please{' '} 42 <TextLink 43 href={HELP_DESK_URL} 44 text={_(msg`click here`)} 45 - style={pal.link} 46 />{' '} 47 or visit {HELP_DESK_URL} to get in touch with us. 48 </Trans>
··· 4 import {useFocusEffect} from '@react-navigation/native' 5 6 import {HELP_DESK_URL} from '#/lib/constants' 7 import { 8 type CommonNavigatorParams, 9 type NativeStackScreenProps, ··· 14 import {Text} from '#/view/com/util/text/Text' 15 import {ViewHeader} from '#/view/com/util/ViewHeader' 16 import {CenteredView} from '#/view/com/util/Views' 17 + import {useTheme} from '#/alf' 18 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 19 import * as Layout from '#/components/Layout' 20 21 type Props = NativeStackScreenProps<CommonNavigatorParams, 'Support'> 22 export const SupportScreen = (_props: Props) => { 23 + const theme = useTheme() 24 + const colorMode = useColorModeTheme() 25 const setMinimalShellMode = useSetMinimalShellMode() 26 const {_} = useLingui() 27 ··· 35 <Layout.Screen> 36 <ViewHeader title={_(msg`Support`)} /> 37 <CenteredView> 38 + <Text 39 + type="title-xl" 40 + style={[ 41 + { 42 + color: 43 + colorMode === 'light' 44 + ? theme.palette.black 45 + : theme.palette.white, 46 + }, 47 + s.p20, 48 + s.pb5, 49 + ]}> 50 + <Trans>Support </Trans> 51 </Text> 52 + <Text 53 + style={[ 54 + { 55 + color: 56 + colorMode === 'light' 57 + ? theme.palette.black 58 + : theme.palette.white, 59 + }, 60 + s.p20, 61 + ]}> 62 <Trans> 63 + The support form has been moved.If you need help, please{' '} 64 <TextLink 65 href={HELP_DESK_URL} 66 text={_(msg`click here`)} 67 + style={{color: theme.palette.primary_500}} 68 />{' '} 69 or visit {HELP_DESK_URL} to get in touch with us. 70 </Trans>
+21 -6
src/view/screens/TermsOfService.tsx
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 - import {usePalette} from '#/lib/hooks/usePalette' 8 import { 9 type CommonNavigatorParams, 10 type NativeStackScreenProps, ··· 14 import {TextLink} from '#/view/com/util/Link' 15 import {Text} from '#/view/com/util/text/Text' 16 import {ScrollView} from '#/view/com/util/Views' 17 import * as Layout from '#/components/Layout' 18 import {ViewHeader} from '../com/util/ViewHeader' 19 20 type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'> 21 export const TermsOfServiceScreen = (_props: Props) => { 22 - const pal = usePalette('default') 23 const setMinimalShellMode = useSetMinimalShellMode() 24 const {_} = useLingui() 25 ··· 32 return ( 33 <Layout.Screen> 34 <ViewHeader title={_(msg`Terms of Service`)} /> 35 - <ScrollView style={[s.hContentRegion, pal.view]}> 36 <View style={[s.p20]}> 37 - <Text style={pal.text}> 38 - <Trans>The Terms of Service have been moved to</Trans>{' '} 39 <TextLink 40 - style={pal.link} 41 href="https://bsky.social/about/support/tos" 42 text="bsky.social/about/support/tos" 43 />
··· 4 import {useLingui} from '@lingui/react' 5 import {useFocusEffect} from '@react-navigation/native' 6 7 import { 8 type CommonNavigatorParams, 9 type NativeStackScreenProps, ··· 13 import {TextLink} from '#/view/com/util/Link' 14 import {Text} from '#/view/com/util/text/Text' 15 import {ScrollView} from '#/view/com/util/Views' 16 + import {useTheme} from '#/alf' 17 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 18 import * as Layout from '#/components/Layout' 19 import {ViewHeader} from '../com/util/ViewHeader' 20 21 type Props = NativeStackScreenProps<CommonNavigatorParams, 'TermsOfService'> 22 export const TermsOfServiceScreen = (_props: Props) => { 23 + const theme = useTheme() 24 + const colorMode = useColorModeTheme() 25 const setMinimalShellMode = useSetMinimalShellMode() 26 const {_} = useLingui() 27 ··· 34 return ( 35 <Layout.Screen> 36 <ViewHeader title={_(msg`Terms of Service`)} /> 37 + <ScrollView 38 + style={[ 39 + s.hContentRegion, 40 + { 41 + backgroundColor: 42 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 43 + }, 44 + ]}> 45 <View style={[s.p20]}> 46 + <Text 47 + style={{ 48 + color: 49 + colorMode === 'light' 50 + ? theme.palette.black 51 + : theme.palette.white, 52 + }}> 53 + <Trans>The Terms of Service have been moved to </Trans>{' '} 54 <TextLink 55 + style={{color: theme.palette.primary_500}} 56 href="https://bsky.social/about/support/tos" 57 text="bsky.social/about/support/tos" 58 />
+35 -20
src/view/shell/bottom-bar/BottomBar.tsx
··· 1 import {type JSX, useCallback} from 'react' 2 - import {type GestureResponderEvent, View} from 'react-native' 3 import Animated from 'react-native-reanimated' 4 import {useSafeAreaInsets} from 'react-native-safe-area-context' 5 import {msg, plural, Trans} from '@lingui/macro' ··· 15 import {useHideBottomBarBorder} from '#/lib/hooks/useHideBottomBarBorder' 16 import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform' 17 import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState' 18 - import {usePalette} from '#/lib/hooks/usePalette' 19 import {clamp} from '#/lib/numbers' 20 import {getTabState, TabState} from '#/lib/routes/helpers' 21 import {useGate} from '#/lib/statsig/statsig' ··· 32 import {UserAvatar} from '#/view/com/util/UserAvatar' 33 import {Logo} from '#/view/icons/Logo' 34 import {Logotype} from '#/view/icons/Logotype' 35 - import {atoms as a} from '#/alf' 36 import {Button, ButtonText} from '#/components/Button' 37 import {useDialogControl} from '#/components/Dialog' 38 import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount' ··· 57 58 export function BottomBar({navigation}: BottomTabBarProps) { 59 const {hasSession, currentAccount} = useSession() 60 - const pal = usePalette('default') 61 const {_} = useLingui() 62 const safeAreaInsets = useSafeAreaInsets() 63 const {footerHeight} = useShellLayout() ··· 76 const gate = useGate() 77 const hideBorder = useHideBottomBarBorder() 78 const iconWidth = 28 79 80 const showSignIn = useCallback(() => { 81 closeAllActiveElements() ··· 147 <Animated.View 148 style={[ 149 styles.bottomBar, 150 - pal.view, 151 - hideBorder ? {borderColor: pal.view.backgroundColor} : pal.border, 152 {paddingBottom: clamp(safeAreaInsets.bottom, 15, 60)}, 153 footerMinimalShellTransform, 154 ]} ··· 163 isAtHome ? ( 164 <HomeFilled 165 width={iconWidth + 1} 166 - style={[styles.ctrlIcon, pal.text, styles.homeIcon]} 167 /> 168 ) : ( 169 <Home 170 width={iconWidth + 1} 171 - style={[styles.ctrlIcon, pal.text, styles.homeIcon]} 172 /> 173 ) 174 } ··· 183 isAtSearch ? ( 184 <MagnifyingGlassFilled 185 width={iconWidth + 2} 186 - style={[styles.ctrlIcon, pal.text, styles.searchIcon]} 187 /> 188 ) : ( 189 <MagnifyingGlass 190 testID="bottomBarSearchBtn" 191 width={iconWidth + 2} 192 - style={[styles.ctrlIcon, pal.text, styles.searchIcon]} 193 /> 194 ) 195 } ··· 204 isAtMessages ? ( 205 <MessageFilled 206 width={iconWidth - 1} 207 - style={[styles.ctrlIcon, pal.text, styles.feedsIcon]} 208 /> 209 ) : ( 210 <Message 211 width={iconWidth - 1} 212 - style={[styles.ctrlIcon, pal.text, styles.feedsIcon]} 213 /> 214 ) 215 } ··· 236 isAtNotifications ? ( 237 <BellFilled 238 width={iconWidth} 239 - style={[styles.ctrlIcon, pal.text, styles.bellIcon]} 240 /> 241 ) : ( 242 <Bell 243 width={iconWidth} 244 - style={[styles.ctrlIcon, pal.text, styles.bellIcon]} 245 /> 246 ) 247 } ··· 269 <View 270 style={[ 271 styles.ctrlIcon, 272 - pal.text, 273 styles.profileIcon, 274 styles.onProfile, 275 { 276 - borderColor: pal.text.color, 277 borderWidth: live ? 0 : 1, 278 }, 279 ]}> ··· 291 <View 292 style={[ 293 styles.ctrlIcon, 294 - pal.text, 295 styles.profileIcon, 296 { 297 borderWidth: live ? 0 : 1, ··· 335 style={{flexDirection: 'row', alignItems: 'center', gap: 8}}> 336 <Logo width={28} /> 337 <View style={{paddingTop: 4}}> 338 - <Logotype width={80} fill={pal.text.color} /> 339 </View> 340 </View> 341 ··· 347 variant="solid" 348 color="primary"> 349 <ButtonText> 350 - <Trans>Create account</Trans> 351 </ButtonText> 352 </Button> 353 <Button ··· 357 variant="solid" 358 color="secondary"> 359 <ButtonText> 360 - <Trans>Sign in</Trans> 361 </ButtonText> 362 </Button> 363 </View>
··· 1 import {type JSX, useCallback} from 'react' 2 + import {type GestureResponderEvent, type TextStyle, View} from 'react-native' 3 import Animated from 'react-native-reanimated' 4 import {useSafeAreaInsets} from 'react-native-safe-area-context' 5 import {msg, plural, Trans} from '@lingui/macro' ··· 15 import {useHideBottomBarBorder} from '#/lib/hooks/useHideBottomBarBorder' 16 import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform' 17 import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState' 18 import {clamp} from '#/lib/numbers' 19 import {getTabState, TabState} from '#/lib/routes/helpers' 20 import {useGate} from '#/lib/statsig/statsig' ··· 31 import {UserAvatar} from '#/view/com/util/UserAvatar' 32 import {Logo} from '#/view/icons/Logo' 33 import {Logotype} from '#/view/icons/Logotype' 34 + import {atoms as a, useTheme} from '#/alf' 35 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 36 import {Button, ButtonText} from '#/components/Button' 37 import {useDialogControl} from '#/components/Dialog' 38 import {SwitchAccountDialog} from '#/components/dialogs/SwitchAccount' ··· 57 58 export function BottomBar({navigation}: BottomTabBarProps) { 59 const {hasSession, currentAccount} = useSession() 60 + const theme = useTheme() 61 + const colorMode = useColorModeTheme() 62 const {_} = useLingui() 63 const safeAreaInsets = useSafeAreaInsets() 64 const {footerHeight} = useShellLayout() ··· 77 const gate = useGate() 78 const hideBorder = useHideBottomBarBorder() 79 const iconWidth = 28 80 + 81 + const textStyle: TextStyle = { 82 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 83 + } 84 85 const showSignIn = useCallback(() => { 86 closeAllActiveElements() ··· 152 <Animated.View 153 style={[ 154 styles.bottomBar, 155 + { 156 + backgroundColor: 157 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 158 + }, 159 + hideBorder 160 + ? { 161 + borderColor: 162 + colorMode === 'light' 163 + ? theme.palette.white 164 + : theme.palette.black, 165 + } 166 + : {borderColor: theme.palette.contrast_100}, 167 {paddingBottom: clamp(safeAreaInsets.bottom, 15, 60)}, 168 footerMinimalShellTransform, 169 ]} ··· 178 isAtHome ? ( 179 <HomeFilled 180 width={iconWidth + 1} 181 + style={[styles.ctrlIcon, textStyle, styles.homeIcon]} 182 /> 183 ) : ( 184 <Home 185 width={iconWidth + 1} 186 + style={[styles.ctrlIcon, textStyle, styles.homeIcon]} 187 /> 188 ) 189 } ··· 198 isAtSearch ? ( 199 <MagnifyingGlassFilled 200 width={iconWidth + 2} 201 + style={[styles.ctrlIcon, textStyle, styles.searchIcon]} 202 /> 203 ) : ( 204 <MagnifyingGlass 205 testID="bottomBarSearchBtn" 206 width={iconWidth + 2} 207 + style={[styles.ctrlIcon, textStyle, styles.searchIcon]} 208 /> 209 ) 210 } ··· 219 isAtMessages ? ( 220 <MessageFilled 221 width={iconWidth - 1} 222 + style={[styles.ctrlIcon, textStyle, styles.feedsIcon]} 223 /> 224 ) : ( 225 <Message 226 width={iconWidth - 1} 227 + style={[styles.ctrlIcon, textStyle, styles.feedsIcon]} 228 /> 229 ) 230 } ··· 251 isAtNotifications ? ( 252 <BellFilled 253 width={iconWidth} 254 + style={[styles.ctrlIcon, textStyle, styles.bellIcon]} 255 /> 256 ) : ( 257 <Bell 258 width={iconWidth} 259 + style={[styles.ctrlIcon, textStyle, styles.bellIcon]} 260 /> 261 ) 262 } ··· 284 <View 285 style={[ 286 styles.ctrlIcon, 287 + textStyle, 288 styles.profileIcon, 289 styles.onProfile, 290 { 291 + borderColor: textStyle.color, 292 borderWidth: live ? 0 : 1, 293 }, 294 ]}> ··· 306 <View 307 style={[ 308 styles.ctrlIcon, 309 + textStyle, 310 styles.profileIcon, 311 { 312 borderWidth: live ? 0 : 1, ··· 350 style={{flexDirection: 'row', alignItems: 'center', gap: 8}}> 351 <Logo width={28} /> 352 <View style={{paddingTop: 4}}> 353 + <Logotype width={80} fill={textStyle.color} /> 354 </View> 355 </View> 356 ··· 362 variant="solid" 363 color="primary"> 364 <ButtonText> 365 + <Trans>Create account </Trans> 366 </ButtonText> 367 </Button> 368 <Button ··· 372 variant="solid" 373 color="secondary"> 374 <ButtonText> 375 + <Trans>Sign in </Trans> 376 </ButtonText> 377 </Button> 378 </View>
+32 -22
src/view/shell/desktop/LeftNav.tsx
··· 1 import {type JSX, useCallback, useMemo, useState} from 'react' 2 - import {StyleSheet, View} from 'react-native' 3 import {type AppBskyActorDefs} from '@atproto/api' 4 import {msg, plural, Trans} from '@lingui/macro' 5 import {useLingui} from '@lingui/react' ··· 8 import {useActorStatus} from '#/lib/actor-status' 9 import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' 10 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 11 - import {usePalette} from '#/lib/hooks/usePalette' 12 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 13 import {getCurrentRoute, isTab} from '#/lib/routes/helpers' 14 import {makeProfileLink} from '#/lib/routes/links' ··· 33 import {UserAvatar} from '#/view/com/util/UserAvatar' 34 import {NavSignupCard} from '#/view/shell/NavSignupCard' 35 import {atoms as a, tokens, useLayoutBreakpoints, useTheme, web} from '#/alf' 36 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 37 import {type DialogControlProps} from '#/components/Dialog' 38 import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' ··· 585 } 586 587 function ChatNavItem() { 588 - const pal = usePalette('default') 589 const {_} = useLingui() 590 const numUnreadMessages = useUnreadMessageCount() 591 592 return ( 593 <NavItem ··· 595 count={numUnreadMessages.numUnread} 596 hasNew={numUnreadMessages.hasNew} 597 icon={ 598 - <Message style={pal.text} aria-hidden={true} width={NAV_ICON_WIDTH} /> 599 } 600 iconFilled={ 601 <MessageFilled 602 - style={pal.text} 603 aria-hidden={true} 604 width={NAV_ICON_WIDTH} 605 /> ··· 611 612 export function DesktopLeftNav() { 613 const {hasSession, currentAccount} = useSession() 614 - const pal = usePalette('default') 615 const {_} = useLingui() 616 const {isDesktop} = useWebMediaQueries() 617 const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints() 618 const numUnreadNotifications = useUnreadNotifications() 619 const hasHomeBadge = useHomeBadge() 620 const gate = useGate() 621 622 if (!hasSession && !isDesktop) { 623 return null ··· 658 <Home 659 aria-hidden={true} 660 width={NAV_ICON_WIDTH} 661 - style={pal.text} 662 /> 663 } 664 iconFilled={ 665 <HomeFilled 666 aria-hidden={true} 667 width={NAV_ICON_WIDTH} 668 - style={pal.text} 669 /> 670 } 671 label={_(msg`Home`)} ··· 674 href="/search" 675 icon={ 676 <MagnifyingGlass 677 - style={pal.text} 678 aria-hidden={true} 679 width={NAV_ICON_WIDTH} 680 /> 681 } 682 iconFilled={ 683 <MagnifyingGlassFilled 684 - style={pal.text} 685 aria-hidden={true} 686 width={NAV_ICON_WIDTH} 687 /> ··· 695 <Bell 696 aria-hidden={true} 697 width={NAV_ICON_WIDTH} 698 - style={pal.text} 699 /> 700 } 701 iconFilled={ 702 <BellFilled 703 aria-hidden={true} 704 width={NAV_ICON_WIDTH} 705 - style={pal.text} 706 /> 707 } 708 label={_(msg`Notifications`)} ··· 712 href="/feeds" 713 icon={ 714 <Hashtag 715 - style={pal.text} 716 aria-hidden={true} 717 width={NAV_ICON_WIDTH} 718 /> 719 } 720 iconFilled={ 721 <HashtagFilled 722 - style={pal.text} 723 aria-hidden={true} 724 width={NAV_ICON_WIDTH} 725 /> ··· 730 href="/lists" 731 icon={ 732 <List 733 - style={pal.text} 734 aria-hidden={true} 735 width={NAV_ICON_WIDTH} 736 /> 737 } 738 iconFilled={ 739 <ListFilled 740 - style={pal.text} 741 aria-hidden={true} 742 width={NAV_ICON_WIDTH} 743 /> ··· 748 href="/saved" 749 icon={ 750 <Bookmark 751 - style={pal.text} 752 aria-hidden={true} 753 width={NAV_ICON_WIDTH} 754 /> 755 } 756 iconFilled={ 757 <BookmarkFilled 758 - style={pal.text} 759 aria-hidden={true} 760 width={NAV_ICON_WIDTH} 761 /> ··· 773 <UserCircle 774 aria-hidden={true} 775 width={NAV_ICON_WIDTH} 776 - style={pal.text} 777 /> 778 } 779 iconFilled={ 780 <UserCircleFilled 781 aria-hidden={true} 782 width={NAV_ICON_WIDTH} 783 - style={pal.text} 784 /> 785 } 786 label={_(msg`Profile`)} ··· 791 <Settings 792 aria-hidden={true} 793 width={NAV_ICON_WIDTH} 794 - style={pal.text} 795 /> 796 } 797 iconFilled={ 798 <SettingsFilled 799 aria-hidden={true} 800 width={NAV_ICON_WIDTH} 801 - style={pal.text} 802 /> 803 } 804 label={_(msg`Settings`)}
··· 1 import {type JSX, useCallback, useMemo, useState} from 'react' 2 + import {StyleSheet, type TextStyle, View} from 'react-native' 3 import {type AppBskyActorDefs} from '@atproto/api' 4 import {msg, plural, Trans} from '@lingui/macro' 5 import {useLingui} from '@lingui/react' ··· 8 import {useActorStatus} from '#/lib/actor-status' 9 import {useAccountSwitcher} from '#/lib/hooks/useAccountSwitcher' 10 import {useOpenComposer} from '#/lib/hooks/useOpenComposer' 11 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 12 import {getCurrentRoute, isTab} from '#/lib/routes/helpers' 13 import {makeProfileLink} from '#/lib/routes/links' ··· 32 import {UserAvatar} from '#/view/com/util/UserAvatar' 33 import {NavSignupCard} from '#/view/shell/NavSignupCard' 34 import {atoms as a, tokens, useLayoutBreakpoints, useTheme, web} from '#/alf' 35 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 36 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 37 import {type DialogControlProps} from '#/components/Dialog' 38 import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft' ··· 585 } 586 587 function ChatNavItem() { 588 + const theme = useTheme() 589 + const colorMode = useColorModeTheme() 590 const {_} = useLingui() 591 const numUnreadMessages = useUnreadMessageCount() 592 + 593 + const textStyle: TextStyle = { 594 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 595 + } 596 597 return ( 598 <NavItem ··· 600 count={numUnreadMessages.numUnread} 601 hasNew={numUnreadMessages.hasNew} 602 icon={ 603 + <Message style={textStyle} aria-hidden={true} width={NAV_ICON_WIDTH} /> 604 } 605 iconFilled={ 606 <MessageFilled 607 + style={textStyle} 608 aria-hidden={true} 609 width={NAV_ICON_WIDTH} 610 /> ··· 616 617 export function DesktopLeftNav() { 618 const {hasSession, currentAccount} = useSession() 619 + const theme = useTheme() 620 + const colorMode = useColorModeTheme() 621 const {_} = useLingui() 622 const {isDesktop} = useWebMediaQueries() 623 const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints() 624 const numUnreadNotifications = useUnreadNotifications() 625 const hasHomeBadge = useHomeBadge() 626 const gate = useGate() 627 + 628 + const textStyle: TextStyle = { 629 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 630 + } 631 632 if (!hasSession && !isDesktop) { 633 return null ··· 668 <Home 669 aria-hidden={true} 670 width={NAV_ICON_WIDTH} 671 + style={textStyle} 672 /> 673 } 674 iconFilled={ 675 <HomeFilled 676 aria-hidden={true} 677 width={NAV_ICON_WIDTH} 678 + style={textStyle} 679 /> 680 } 681 label={_(msg`Home`)} ··· 684 href="/search" 685 icon={ 686 <MagnifyingGlass 687 + style={textStyle} 688 aria-hidden={true} 689 width={NAV_ICON_WIDTH} 690 /> 691 } 692 iconFilled={ 693 <MagnifyingGlassFilled 694 + style={textStyle} 695 aria-hidden={true} 696 width={NAV_ICON_WIDTH} 697 /> ··· 705 <Bell 706 aria-hidden={true} 707 width={NAV_ICON_WIDTH} 708 + style={textStyle} 709 /> 710 } 711 iconFilled={ 712 <BellFilled 713 aria-hidden={true} 714 width={NAV_ICON_WIDTH} 715 + style={textStyle} 716 /> 717 } 718 label={_(msg`Notifications`)} ··· 722 href="/feeds" 723 icon={ 724 <Hashtag 725 + style={textStyle} 726 aria-hidden={true} 727 width={NAV_ICON_WIDTH} 728 /> 729 } 730 iconFilled={ 731 <HashtagFilled 732 + style={textStyle} 733 aria-hidden={true} 734 width={NAV_ICON_WIDTH} 735 /> ··· 740 href="/lists" 741 icon={ 742 <List 743 + style={textStyle} 744 aria-hidden={true} 745 width={NAV_ICON_WIDTH} 746 /> 747 } 748 iconFilled={ 749 <ListFilled 750 + style={textStyle} 751 aria-hidden={true} 752 width={NAV_ICON_WIDTH} 753 /> ··· 758 href="/saved" 759 icon={ 760 <Bookmark 761 + style={textStyle} 762 aria-hidden={true} 763 width={NAV_ICON_WIDTH} 764 /> 765 } 766 iconFilled={ 767 <BookmarkFilled 768 + style={textStyle} 769 aria-hidden={true} 770 width={NAV_ICON_WIDTH} 771 /> ··· 783 <UserCircle 784 aria-hidden={true} 785 width={NAV_ICON_WIDTH} 786 + style={textStyle} 787 /> 788 } 789 iconFilled={ 790 <UserCircleFilled 791 aria-hidden={true} 792 width={NAV_ICON_WIDTH} 793 + style={textStyle} 794 /> 795 } 796 label={_(msg`Profile`)} ··· 801 <Settings 802 aria-hidden={true} 803 width={NAV_ICON_WIDTH} 804 + style={textStyle} 805 /> 806 } 807 iconFilled={ 808 <SettingsFilled 809 aria-hidden={true} 810 width={NAV_ICON_WIDTH} 811 + style={textStyle} 812 /> 813 } 814 label={_(msg`Settings`)}
+34 -11
src/view/shell/desktop/Search.tsx
··· 2 import { 3 ActivityIndicator, 4 StyleSheet, 5 TouchableOpacity, 6 View, 7 type ViewStyle, ··· 10 import {useLingui} from '@lingui/react' 11 import {StackActions, useNavigation} from '@react-navigation/native' 12 13 - import {usePalette} from '#/lib/hooks/usePalette' 14 import {type NavigationProp} from '#/lib/routes/types' 15 import {useModerationOpts} from '#/state/preferences/moderation-opts' 16 import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' 17 import {Link} from '#/view/com/util/Link' 18 import {Text} from '#/view/com/util/text/Text' 19 import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard' 20 - import {atoms as a} from '#/alf' 21 import {SearchInput} from '#/components/forms/SearchInput' 22 23 let SearchLinkCard = ({ ··· 31 onPress?: () => void 32 style?: ViewStyle 33 }): React.ReactNode => { 34 - const pal = usePalette('default') 35 36 const inner = ( 37 <View 38 - style={[pal.border, {paddingVertical: 16, paddingHorizontal: 12}, style]}> 39 - <Text type="md" style={[pal.text]}> 40 {label} 41 </Text> 42 </View> ··· 57 <Link href={to} asAnchor anchorNoUnderline> 58 <View 59 style={[ 60 - pal.border, 61 {paddingVertical: 16, paddingHorizontal: 12}, 62 style, 63 ]}> 64 - <Text type="md" style={[pal.text]}> 65 {label} 66 </Text> 67 </View> ··· 73 74 export function DesktopSearch() { 75 const {_} = useLingui() 76 - const pal = usePalette('default') 77 const navigation = useNavigation<NavigationProp>() 78 const [isActive, setIsActive] = React.useState<boolean>(false) 79 const [query, setQuery] = React.useState<string>('') ··· 106 }, []) 107 108 return ( 109 - <View style={[styles.container, pal.view]}> 110 <SearchInput 111 value={query} 112 onChangeText={onChangeText} ··· 116 {query !== '' && isActive && moderationOpts && ( 117 <View 118 style={[ 119 - pal.view, 120 - pal.borderDark, 121 styles.resultsContainer, 122 a.overflow_hidden, 123 ]}>
··· 2 import { 3 ActivityIndicator, 4 StyleSheet, 5 + type TextStyle, 6 TouchableOpacity, 7 View, 8 type ViewStyle, ··· 11 import {useLingui} from '@lingui/react' 12 import {StackActions, useNavigation} from '@react-navigation/native' 13 14 import {type NavigationProp} from '#/lib/routes/types' 15 import {useModerationOpts} from '#/state/preferences/moderation-opts' 16 import {useActorAutocompleteQuery} from '#/state/queries/actor-autocomplete' 17 import {Link} from '#/view/com/util/Link' 18 import {Text} from '#/view/com/util/text/Text' 19 import {SearchProfileCard} from '#/screens/Search/components/SearchProfileCard' 20 + import {atoms as a, useTheme} from '#/alf' 21 + import {useColorModeTheme} from '#/alf/util/useColorModeTheme' 22 import {SearchInput} from '#/components/forms/SearchInput' 23 24 let SearchLinkCard = ({ ··· 32 onPress?: () => void 33 style?: ViewStyle 34 }): React.ReactNode => { 35 + const theme = useTheme() 36 + const colorMode = useColorModeTheme() 37 + 38 + const textStyle: TextStyle = { 39 + color: colorMode === 'light' ? theme.palette.black : theme.palette.white, 40 + } 41 42 const inner = ( 43 <View 44 + style={[ 45 + {borderColor: theme.palette.contrast_100}, 46 + {paddingVertical: 16, paddingHorizontal: 12}, 47 + style, 48 + ]}> 49 + <Text type="md" style={[textStyle]}> 50 {label} 51 </Text> 52 </View> ··· 67 <Link href={to} asAnchor anchorNoUnderline> 68 <View 69 style={[ 70 + {borderColor: theme.palette.contrast_100}, 71 {paddingVertical: 16, paddingHorizontal: 12}, 72 style, 73 ]}> 74 + <Text type="md" style={[textStyle]}> 75 {label} 76 </Text> 77 </View> ··· 83 84 export function DesktopSearch() { 85 const {_} = useLingui() 86 + const theme = useTheme() 87 + const colorMode = useColorModeTheme() 88 const navigation = useNavigation<NavigationProp>() 89 const [isActive, setIsActive] = React.useState<boolean>(false) 90 const [query, setQuery] = React.useState<string>('') ··· 117 }, []) 118 119 return ( 120 + <View 121 + style={[ 122 + styles.container, 123 + { 124 + backgroundColor: 125 + colorMode === 'light' ? theme.palette.white : theme.palette.black, 126 + }, 127 + ]}> 128 <SearchInput 129 value={query} 130 onChangeText={onChangeText} ··· 134 {query !== '' && isActive && moderationOpts && ( 135 <View 136 style={[ 137 + { 138 + backgroundColor: 139 + colorMode === 'light' 140 + ? theme.palette.white 141 + : theme.palette.black, 142 + borderColor: theme.palette.contrast_200, 143 + }, 144 styles.resultsContainer, 145 a.overflow_hidden, 146 ]}>