Bluesky app fork with some witchin' additions 💫
0
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"