Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client
117
fork

Configure Feed

Select the types of activity you want to include in your feed.

Material You theme for Android

authored by

uwx and committed by
xan.lol
206acfa4 db6e9ad2

+344 -6
+1
package.json
··· 80 80 "icons:optimize": "svgo -f ./assets/icons" 81 81 }, 82 82 "dependencies": { 83 + "@assembless/react-native-material-you": "^1.0.0-beta.4", 83 84 "@atproto/api": "^0.19.6", 84 85 "@atproto/oauth-client-browser": "^0.3.41", 85 86 "@bitdrift/react-native": "^0.6.8",
+13 -1
src/alf/index.tsx
··· 17 17 deerscheme, 18 18 evergardenscheme, 19 19 kittyscheme, 20 + material3scheme, 20 21 type Palette, 21 22 reddwarfscheme, 22 23 themes, 23 24 witchskyscheme, 24 25 zeppelinscheme, 25 26 } from '#/alf/themes' 27 + import {IS_ANDROID} from '#/env' 26 28 import {type Device} from '#/storage' 29 + import {MaterialYouPaletteProvider} from './util/materialYou' 27 30 28 31 export { 29 32 type TextStyleProp, ··· 170 173 return catppuccinscheme 171 174 case 'evergarden': 172 175 return evergardenscheme 176 + case 'material3': 177 + if (IS_ANDROID) { 178 + return material3scheme 179 + } 180 + return witchskyscheme 173 181 default: 174 182 return themes 175 183 } ··· 233 241 setFontFamilyAndPersist, 234 242 ]) 235 243 236 - return <Context.Provider value={value}>{children}</Context.Provider> 244 + return ( 245 + <MaterialYouPaletteProvider> 246 + <Context.Provider value={value}>{children}</Context.Provider> 247 + </MaterialYouPaletteProvider> 248 + ) 237 249 } 238 250 239 251 export function useAlf() {
+179
src/alf/themes.ts
··· 24 24 GREEN_HUE as DEER_GREEN_HUE, 25 25 RED_HUE as DEER_RED_HUE, 26 26 } from '#/alf/util/deerColorGeneration' 27 + import { 28 + getMaterialYouColor, 29 + onMaterialYouPaletteChange, 30 + } from './util/materialYou' 27 31 28 32 export type Palette = { 29 33 white: string ··· 1386 1390 dark: EVERGARDEN_THEMES.dark, 1387 1391 dim: EVERGARDEN_THEMES.dim, 1388 1392 } 1393 + 1394 + function getMaterial3ColorScheme() { 1395 + const MATERIAL_3_PALETTE: Palette = { 1396 + white: STATIC_VALUES.white, 1397 + black: STATIC_VALUES.black, 1398 + pink: STATIC_VALUES.pink, 1399 + yellow: STATIC_VALUES.yellow, 1400 + like: STATIC_VALUES.pink, 1401 + 1402 + contrast_0: getMaterialYouColor('system_neutral1', 0), 1403 + contrast_25: getMaterialYouColor('system_neutral1', 10), 1404 + contrast_50: getMaterialYouColor('system_neutral1', 50), 1405 + contrast_100: getMaterialYouColor('system_neutral1', 100), 1406 + contrast_200: getMaterialYouColor('system_neutral1', 200), 1407 + contrast_300: getMaterialYouColor('system_neutral1', 300), 1408 + contrast_400: getMaterialYouColor('system_neutral1', 400), 1409 + contrast_500: getMaterialYouColor('system_neutral1', 500), 1410 + contrast_600: getMaterialYouColor('system_neutral1', 600), 1411 + contrast_700: getMaterialYouColor('system_neutral1', 700), 1412 + contrast_800: getMaterialYouColor('system_neutral1', 800), 1413 + contrast_900: getMaterialYouColor('system_neutral1', 900), 1414 + contrast_950: getMaterialYouColor('system_neutral1', 900), 1415 + contrast_975: getMaterialYouColor('system_neutral1', 900), 1416 + contrast_1000: getMaterialYouColor('system_neutral1', 1000), 1417 + 1418 + primary_25: getMaterialYouColor('system_accent1', 10), 1419 + primary_50: getMaterialYouColor('system_accent1', 50), 1420 + primary_100: getMaterialYouColor('system_accent1', 100), 1421 + primary_200: getMaterialYouColor('system_accent1', 200), 1422 + primary_300: getMaterialYouColor('system_accent1', 300), 1423 + primary_400: getMaterialYouColor('system_accent1', 400), 1424 + primary_500: getMaterialYouColor('system_accent1', 500), 1425 + primary_600: getMaterialYouColor('system_accent1', 600), 1426 + primary_700: getMaterialYouColor('system_accent1', 700), 1427 + primary_800: getMaterialYouColor('system_accent1', 800), 1428 + primary_900: getMaterialYouColor('system_accent1', 900), 1429 + primary_950: getMaterialYouColor('system_accent1', 900), 1430 + primary_975: getMaterialYouColor('system_accent1', 900), 1431 + 1432 + positive_25: getMaterialYouColor('system_accent3', 10), 1433 + positive_50: getMaterialYouColor('system_accent3', 50), 1434 + positive_100: getMaterialYouColor('system_accent3', 100), 1435 + positive_200: getMaterialYouColor('system_accent3', 200), 1436 + positive_300: getMaterialYouColor('system_accent3', 300), 1437 + positive_400: getMaterialYouColor('system_accent3', 400), 1438 + positive_500: getMaterialYouColor('system_accent3', 500), 1439 + positive_600: getMaterialYouColor('system_accent3', 600), 1440 + positive_700: getMaterialYouColor('system_accent3', 700), 1441 + positive_800: getMaterialYouColor('system_accent3', 800), 1442 + positive_900: getMaterialYouColor('system_accent3', 900), 1443 + positive_950: getMaterialYouColor('system_accent3', 900), 1444 + positive_975: getMaterialYouColor('system_accent3', 900), 1445 + 1446 + negative_25: '#FFF5F7', 1447 + negative_50: '#FEE7EC', 1448 + negative_100: '#FDD3DD', 1449 + negative_200: '#FBBBCA', 1450 + negative_300: '#F891A9', 1451 + negative_400: '#F65A7F', 1452 + negative_500: '#E91646', 1453 + negative_600: '#CA123D', 1454 + negative_700: '#A71134', 1455 + negative_800: '#7F0B26', 1456 + negative_900: '#5F071C', 1457 + negative_950: '#430413', 1458 + negative_975: '#30030D', 1459 + } 1460 + 1461 + const MATERIAL_3_SUBDUED_PALETTE: Palette = { 1462 + white: STATIC_VALUES.white, 1463 + black: STATIC_VALUES.black, 1464 + pink: STATIC_VALUES.pink, 1465 + yellow: STATIC_VALUES.yellow, 1466 + like: STATIC_VALUES.pink, 1467 + 1468 + contrast_0: getMaterialYouColor('system_neutral2', 0), 1469 + contrast_25: getMaterialYouColor('system_neutral2', 10), 1470 + contrast_50: getMaterialYouColor('system_neutral2', 50), 1471 + contrast_100: getMaterialYouColor('system_neutral2', 100), 1472 + contrast_200: getMaterialYouColor('system_neutral2', 200), 1473 + contrast_300: getMaterialYouColor('system_neutral2', 300), 1474 + contrast_400: getMaterialYouColor('system_neutral2', 400), 1475 + contrast_500: getMaterialYouColor('system_neutral2', 500), 1476 + contrast_600: getMaterialYouColor('system_neutral2', 600), 1477 + contrast_700: getMaterialYouColor('system_neutral2', 700), 1478 + contrast_800: getMaterialYouColor('system_neutral2', 800), 1479 + contrast_900: getMaterialYouColor('system_neutral2', 800), 1480 + contrast_950: getMaterialYouColor('system_neutral2', 800), 1481 + contrast_975: getMaterialYouColor('system_neutral2', 800), 1482 + contrast_1000: getMaterialYouColor('system_neutral2', 900), 1483 + 1484 + primary_25: getMaterialYouColor('system_accent2', 10), 1485 + primary_50: getMaterialYouColor('system_accent2', 10), 1486 + primary_100: getMaterialYouColor('system_accent2', 50), 1487 + primary_200: getMaterialYouColor('system_accent2', 100), 1488 + primary_300: getMaterialYouColor('system_accent2', 200), 1489 + primary_400: getMaterialYouColor('system_accent2', 300), 1490 + primary_500: getMaterialYouColor('system_accent2', 400), 1491 + primary_600: getMaterialYouColor('system_accent2', 500), 1492 + primary_700: getMaterialYouColor('system_accent2', 600), 1493 + primary_800: getMaterialYouColor('system_accent2', 700), 1494 + primary_900: getMaterialYouColor('system_accent2', 800), 1495 + primary_950: getMaterialYouColor('system_accent2', 900), 1496 + primary_975: getMaterialYouColor('system_accent2', 900), 1497 + 1498 + positive_25: getMaterialYouColor('system_accent3', 10), 1499 + positive_50: getMaterialYouColor('system_accent3', 50), 1500 + positive_100: getMaterialYouColor('system_accent3', 100), 1501 + positive_200: getMaterialYouColor('system_accent3', 200), 1502 + positive_300: getMaterialYouColor('system_accent3', 300), 1503 + positive_400: getMaterialYouColor('system_accent3', 400), 1504 + positive_500: getMaterialYouColor('system_accent3', 500), 1505 + positive_600: getMaterialYouColor('system_accent3', 600), 1506 + positive_700: getMaterialYouColor('system_accent3', 700), 1507 + positive_800: getMaterialYouColor('system_accent3', 800), 1508 + positive_900: getMaterialYouColor('system_accent3', 900), 1509 + positive_950: getMaterialYouColor('system_accent3', 900), 1510 + positive_975: getMaterialYouColor('system_accent3', 900), 1511 + 1512 + negative_25: '#FFF5F7', 1513 + negative_50: '#FEEBEF', 1514 + negative_100: '#FDD8E1', 1515 + negative_200: '#FCC0CE', 1516 + negative_300: '#F99AB0', 1517 + negative_400: '#F76486', 1518 + negative_500: '#EB2452', 1519 + negative_600: '#D81341', 1520 + negative_700: '#BA1239', 1521 + negative_800: '#910D2C', 1522 + negative_900: '#6F0B22', 1523 + negative_950: '#500B1C', 1524 + negative_975: '#3E0915', 1525 + } 1526 + 1527 + const MATERIAL_3_THEMES = createThemes({ 1528 + defaultPalette: MATERIAL_3_PALETTE, 1529 + subduedPalette: MATERIAL_3_SUBDUED_PALETTE, 1530 + }) 1531 + 1532 + // special case for disabled (primary) button text. we have a hack for primary_subtle in Button.tsx 1533 + MATERIAL_3_THEMES.dark.atoms.text_inverted.color = 1534 + MATERIAL_3_THEMES.dark.palette.primary_400 1535 + MATERIAL_3_THEMES.dim.atoms.text_inverted.color = 1536 + MATERIAL_3_THEMES.dim.palette.primary_400 1537 + 1538 + const material3scheme = { 1539 + lightPalette: MATERIAL_3_THEMES.light.palette, 1540 + darkPalette: MATERIAL_3_THEMES.dark.palette, 1541 + dimPalette: MATERIAL_3_THEMES.dim.palette, 1542 + light: MATERIAL_3_THEMES.light, 1543 + dark: MATERIAL_3_THEMES.dark, 1544 + dim: MATERIAL_3_THEMES.dim, 1545 + } 1546 + 1547 + return { 1548 + regular: MATERIAL_3_PALETTE, 1549 + subdued: MATERIAL_3_SUBDUED_PALETTE, 1550 + scheme: material3scheme, 1551 + } 1552 + } 1553 + 1554 + let material3colorSchemeData = getMaterial3ColorScheme() 1555 + 1556 + export let MATERIAL_3_PALETTE: Palette = material3colorSchemeData.regular 1557 + export let MATERIAL_3_SUBDUED_PALETTE: Palette = 1558 + material3colorSchemeData.subdued 1559 + export let material3scheme = material3colorSchemeData.scheme 1560 + 1561 + onMaterialYouPaletteChange(() => { 1562 + material3colorSchemeData = getMaterial3ColorScheme() 1563 + 1564 + MATERIAL_3_PALETTE = material3colorSchemeData.regular 1565 + MATERIAL_3_SUBDUED_PALETTE = material3colorSchemeData.subdued 1566 + material3scheme = material3colorSchemeData.scheme 1567 + }) 1389 1568 1390 1569 /** 1391 1570 * @deprecated use ALF and access palette from `useTheme()`
+83
src/alf/util/materialYou.android.tsx
··· 1 + import {createContext, useEffect, useState} from 'react' 2 + import {AppState} from 'react-native' 3 + import { 4 + getPalette, 5 + getPaletteSync, 6 + type MaterialYouPalette, 7 + } from '@assembless/react-native-material-you' 8 + 9 + let palette = getPaletteSync() as MaterialYouPalette 10 + export function getMaterialYouColor( 11 + color: [ 12 + 'system_accent1', 13 + 'system_accent2', 14 + 'system_accent3', 15 + 'system_neutral1', 16 + 'system_neutral2', 17 + ][number], 18 + shade: [0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000][number], 19 + ): string { 20 + const shades = [0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000] 21 + let shadeIndex = shades.findIndex(s => s === shade) 22 + if (shadeIndex === -1) { 23 + throw new Error( 24 + `Invalid shade: ${shade}. Valid shades are: ${shades.join(', ')}`, 25 + ) 26 + } 27 + 28 + return palette[color][shadeIndex] 29 + } 30 + 31 + const colorsChangedCallbacks = new Set<() => void>() 32 + 33 + AppState.addEventListener('focus', () => { 34 + getPalette() 35 + .then((newPalette: MaterialYouPalette) => { 36 + // check if colors changed 37 + const colorsChanged = Object.keys(newPalette).some(key => { 38 + const colorKey = key as keyof MaterialYouPalette 39 + return newPalette[colorKey].some( 40 + (color, index) => color !== palette[colorKey][index], 41 + ) 42 + }) 43 + if (colorsChanged) { 44 + palette = newPalette 45 + for (const callback of colorsChangedCallbacks) { 46 + callback() 47 + } 48 + } 49 + }) 50 + .catch(err => { 51 + console.error('Failed to get Material You palette:', err) 52 + }) 53 + }) 54 + 55 + export function onMaterialYouPaletteChange(callback: () => void) { 56 + colorsChangedCallbacks.add(callback) 57 + return () => { 58 + colorsChangedCallbacks.delete(callback) 59 + } 60 + } 61 + 62 + const PaletteProvider = createContext(palette) 63 + 64 + // context forces a rerender when palette changes even if nothing uses it (because unfortunately, we're forced to use it 65 + // via Alf) 66 + export function MaterialYouPaletteProvider({ 67 + children, 68 + }: React.PropsWithChildren) { 69 + const [_palette, setPalette] = useState(palette) 70 + useEffect(() => { 71 + const unsubscribe = onMaterialYouPaletteChange(() => { 72 + setPalette(() => palette) 73 + }) 74 + 75 + return unsubscribe 76 + }, []) 77 + 78 + return ( 79 + <PaletteProvider.Provider value={_palette}> 80 + {children} 81 + </PaletteProvider.Provider> 82 + ) 83 + }
+36
src/alf/util/materialYou.tsx
··· 1 + import {type JSX} from 'react' 2 + 3 + export function getMaterialYouColor( 4 + _color: [ 5 + 'system_accent1', 6 + 'system_accent2', 7 + 'system_accent3', 8 + 'system_neutral1', 9 + 'system_neutral2', 10 + ][number], 11 + _shade: [ 12 + 0, 13 + 10, 14 + 50, 15 + 100, 16 + 200, 17 + 300, 18 + 400, 19 + 500, 20 + 600, 21 + 700, 22 + 800, 23 + 900, 24 + 1000, 25 + ][number], 26 + ): string { 27 + return '#000000' 28 + } 29 + 30 + export function onMaterialYouPaletteChange(_callback: () => void) {} 31 + 32 + export function MaterialYouPaletteProvider({ 33 + children, 34 + }: React.PropsWithChildren): JSX.Element { 35 + return <>{children}</> 36 + }
+14 -4
src/components/Button.tsx
··· 22 22 } from 'react-native' 23 23 24 24 import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 25 + import {useThemePrefs} from '#/state/shell' 25 26 import {atoms as a, flatten, select, useTheme} from '#/alf' 26 27 import {type Props as SVGIconProps} from '#/components/icons/common' 27 28 import {Text} from '#/components/Typography' ··· 591 592 export function useSharedButtonTextStyles() { 592 593 const t = useTheme() 593 594 const {color, variant, disabled, size} = useButtonContext() 595 + const {colorScheme} = useThemePrefs() 596 + 594 597 return useMemo(() => { 595 598 const baseStyles: TextStyle[] = [] 596 599 ··· 640 643 color: t.palette.primary_600, 641 644 }) 642 645 } else { 643 - baseStyles.push({ 644 - color: t.palette.primary_200, 645 - }) 646 + if (colorScheme === 'material3') { 647 + // ugly hack! really alf should export these colors as atoms 648 + baseStyles.push({ 649 + color: t.palette.primary_400, 650 + }) 651 + } else { 652 + baseStyles.push({ 653 + color: t.palette.primary_200, 654 + }) 655 + } 646 656 } 647 657 } else if (color === 'negative_subtle') { 648 658 if (!disabled) { ··· 762 772 } 763 773 764 774 return flatten(baseStyles) 765 - }, [t, variant, color, size, disabled]) 775 + }, [t, variant, color, size, disabled, colorScheme]) 766 776 } 767 777 768 778 export function ButtonText({children, style, ...rest}: ButtonTextProps) {
+12 -1
src/screens/Settings/AppearanceSettings.tsx
··· 34 34 DEFAULT_PALETTE, 35 35 EVERGARDEN_PALETTE, 36 36 KITTY_PALETTE, 37 + MATERIAL_3_PALETTE, 37 38 REDDWARF_PALETTE, 38 39 ZEPPELIN_PALETTE, 39 40 } from '#/alf/themes' ··· 54 55 import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase' 55 56 import * as Layout from '#/components/Layout' 56 57 import {Text} from '#/components/Typography' 57 - import {IS_INTERNAL, IS_NATIVE} from '#/env' 58 + import {IS_ANDROID, IS_INTERNAL, IS_NATIVE} from '#/env' 58 59 import * as SettingsList from './components/SettingsList' 59 60 60 61 type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'> ··· 69 70 | 'reddwarf' 70 71 | 'catppuccin' 71 72 | 'evergarden' 73 + | 'material3' 72 74 73 75 type ColorSchemeOption = { 74 76 name: ColorSchemeName ··· 175 177 label: _(msg`Evergarden`), 176 178 primary: EVERGARDEN_PALETTE.primary_500, 177 179 }, 180 + ...(IS_ANDROID 181 + ? [ 182 + { 183 + name: 'material3', 184 + label: _(msg`Material You`), 185 + primary: MATERIAL_3_PALETTE.primary_500, 186 + } satisfies ColorSchemeOption, 187 + ] 188 + : []), 178 189 ] 179 190 180 191 return (
+1
src/state/persisted/schema.ts
··· 61 61 'reddwarf', 62 62 'catppuccin', 63 63 'evergarden', 64 + 'material3', 64 65 ]), 65 66 hue: z.number(), 66 67 session: z.object({
+5
yarn.lock
··· 20 20 "@jridgewell/gen-mapping" "^0.3.0" 21 21 "@jridgewell/trace-mapping" "^0.3.9" 22 22 23 + "@assembless/react-native-material-you@^1.0.0-beta.4": 24 + version "1.0.0-beta.4" 25 + resolved "https://registry.yarnpkg.com/@assembless/react-native-material-you/-/react-native-material-you-1.0.0-beta.4.tgz#fe327641614fe890b3a69ea3612796182a588c14" 26 + integrity sha512-sgQZPu4Qzc6xfbbIpWcujh6F2Ibx0aJdge9ajQTA1N2BknNny8mDnLOMNycXKaXlk1+sjcNv+oe/h48iElSfLg== 27 + 23 28 "@atproto-labs/did-resolver@0.2.6", "@atproto-labs/did-resolver@^0.2.6": 24 29 version "0.2.6" 25 30 resolved "https://registry.yarnpkg.com/@atproto-labs/did-resolver/-/did-resolver-0.2.6.tgz#15f0beab797187a67279389f6503f87a257cd898"