tangled mirror of catsky-🐱 Soothing soft social-app fork with all the niche toggles! (Unofficial); for issues and PRs please put them on github:NekoDrone/catsky-social

Add kawaii mode (#3773)

authored by samuel.fm and committed by GitHub 81ae7e42 181e61be

Changed files
+150 -28
.github
assets
bskyweb
cmd
bskyweb
static
media
src
state
persisted
preferences
view
+4 -4
.github/workflows/golang-test-lint.yml
··· 20 uses: actions/setup-go@v3 21 with: 22 go-version: '1.21' 23 - - name: Dummy JS File 24 - run: touch bskyweb/static/js/blah.js 25 - name: Check 26 run: cd bskyweb/ && make check 27 - name: Build (binary) ··· 37 uses: actions/setup-go@v3 38 with: 39 go-version: '1.21' 40 - - name: Dummy JS File 41 - run: touch bskyweb/static/js/blah.js 42 - name: Lint 43 run: cd bskyweb/ && make lint
··· 20 uses: actions/setup-go@v3 21 with: 22 go-version: '1.21' 23 + - name: Dummy Static Files 24 + run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt 25 - name: Check 26 run: cd bskyweb/ && make check 27 - name: Build (binary) ··· 37 uses: actions/setup-go@v3 38 with: 39 go-version: '1.21' 40 + - name: Dummy Static Files 41 + run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt 42 - name: Lint 43 run: cd bskyweb/ && make lint
+1
Dockerfile.embedr
··· 40 41 # hack around issue with empty directory and go:embed 42 RUN touch bskyweb/static/js/empty.txt 43 44 # 45 # Generate the embedr Go binary.
··· 40 41 # hack around issue with empty directory and go:embed 42 RUN touch bskyweb/static/js/empty.txt 43 + RUN touch bskyweb/static/media/empty.txt 44 45 # 46 # Generate the embedr Go binary.
assets/kawaii.png

This is a binary file and will not be displayed.

assets/kawaii_smol.png

This is a binary file and will not be displayed.

+2
bskyweb/.gitignore
··· 10 static/js/*.map 11 static/js/*.js.LICENSE.txt 12 static/js/empty.txt 13 templates/scripts.html 14 templates/*-embed.html 15 static/embed/*.html
··· 10 static/js/*.map 11 static/js/*.js.LICENSE.txt 12 static/js/empty.txt 13 + static/media/*.png 14 + static/media/empty.txt 15 templates/scripts.html 16 templates/*-embed.html 17 static/embed/*.html
+1 -1
bskyweb/cmd/bskyweb/server.go
··· 158 159 // Cache javascript and images files for 1 week, which works because 160 // they're always versioned (e.g. /static/js/main.64c14927.js) 161 - if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/images/") { 162 maxAge = 7 * (60 * 60 * 24) // 1 week 163 } 164
··· 158 159 // Cache javascript and images files for 1 week, which works because 160 // they're always versioned (e.g. /static/js/main.64c14927.js) 161 + if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/images/") || strings.HasPrefix(path, "/static/media/") { 162 maxAge = 7 * (60 * 60 * 24) // 1 week 163 } 164
bskyweb/static/media/.gitkeep

This is a binary file and will not be displayed.

+1 -1
package.json
··· 15 "web": "expo start --web", 16 "use-build-number": "./scripts/useBuildNumberEnv.sh", 17 "use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh", 18 - "build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/", 19 "build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all", 20 "build-ios": "yarn use-build-number-with-bump eas build -p ios", 21 "build-android": "yarn use-build-number-with-bump eas build -p android",
··· 15 "web": "expo start --web", 16 "use-build-number": "./scripts/useBuildNumberEnv.sh", 17 "use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh", 18 + "build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/ && cp -v ./web-build/static/media/*.png ./bskyweb/static/media/", 19 "build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all", 20 "build-ios": "yarn use-build-number-with-bump eas build -p ios", 21 "build-android": "yarn use-build-number-with-bump eas build -p android",
+4 -4
src/state/persisted/index.ts
··· 1 import EventEmitter from 'eventemitter3' 2 import {logger} from '#/logger' 3 - import {defaults, Schema} from '#/state/persisted/schema' 4 import {migrate} from '#/state/persisted/legacy' 5 import * as store from '#/state/persisted/store' 6 - import BroadcastChannel from '#/lib/broadcast' 7 - 8 - export type {Schema, PersistedAccount} from '#/state/persisted/schema' 9 export {defaults} from '#/state/persisted/schema' 10 11 const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL')
··· 1 import EventEmitter from 'eventemitter3' 2 + 3 + import BroadcastChannel from '#/lib/broadcast' 4 import {logger} from '#/logger' 5 import {migrate} from '#/state/persisted/legacy' 6 + import {defaults, Schema} from '#/state/persisted/schema' 7 import * as store from '#/state/persisted/store' 8 + export type {PersistedAccount, Schema} from '#/state/persisted/schema' 9 export {defaults} from '#/state/persisted/schema' 10 11 const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL')
+2
src/state/persisted/schema.ts
··· 80 pdsAddressHistory: z.array(z.string()).optional(), 81 disableHaptics: z.boolean().optional(), 82 disableAutoplay: z.boolean().optional(), 83 }) 84 export type Schema = z.infer<typeof schema> 85 ··· 117 pdsAddressHistory: [], 118 disableHaptics: false, 119 disableAutoplay: prefersReducedMotion, 120 }
··· 80 pdsAddressHistory: z.array(z.string()).optional(), 81 disableHaptics: z.boolean().optional(), 82 disableAutoplay: z.boolean().optional(), 83 + kawaii: z.boolean().optional(), 84 }) 85 export type Schema = z.infer<typeof schema> 86 ··· 118 pdsAddressHistory: [], 119 disableHaptics: false, 120 disableAutoplay: prefersReducedMotion, 121 + kawaii: false, 122 }
+4 -1
src/state/preferences/index.tsx
··· 7 import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' 8 import {Provider as HiddenPostsProvider} from './hidden-posts' 9 import {Provider as InAppBrowserProvider} from './in-app-browser' 10 import {Provider as LanguagesProvider} from './languages' 11 12 export { ··· 32 <InAppBrowserProvider> 33 <DisableHapticsProvider> 34 <AutoplayProvider> 35 - <DmServiceUrlProvider>{children}</DmServiceUrlProvider> 36 </AutoplayProvider> 37 </DisableHapticsProvider> 38 </InAppBrowserProvider>
··· 7 import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs' 8 import {Provider as HiddenPostsProvider} from './hidden-posts' 9 import {Provider as InAppBrowserProvider} from './in-app-browser' 10 + import {Provider as KawaiiProvider} from './kawaii' 11 import {Provider as LanguagesProvider} from './languages' 12 13 export { ··· 33 <InAppBrowserProvider> 34 <DisableHapticsProvider> 35 <AutoplayProvider> 36 + <DmServiceUrlProvider> 37 + <KawaiiProvider>{children}</KawaiiProvider> 38 + </DmServiceUrlProvider> 39 </AutoplayProvider> 40 </DisableHapticsProvider> 41 </InAppBrowserProvider>
+50
src/state/preferences/kawaii.tsx
···
··· 1 + import React from 'react' 2 + 3 + import {isWeb} from '#/platform/detection' 4 + import * as persisted from '#/state/persisted' 5 + 6 + type StateContext = persisted.Schema['kawaii'] 7 + 8 + const stateContext = React.createContext<StateContext>( 9 + persisted.defaults.kawaii, 10 + ) 11 + 12 + export function Provider({children}: React.PropsWithChildren<{}>) { 13 + const [state, setState] = React.useState(persisted.get('kawaii')) 14 + 15 + const setStateWrapped = React.useCallback( 16 + (kawaii: persisted.Schema['kawaii']) => { 17 + setState(kawaii) 18 + persisted.write('kawaii', kawaii) 19 + }, 20 + [setState], 21 + ) 22 + 23 + React.useEffect(() => { 24 + return persisted.onUpdate(() => { 25 + setState(persisted.get('kawaii')) 26 + }) 27 + }, [setStateWrapped]) 28 + 29 + React.useEffect(() => { 30 + // dumb and stupid but it's web only so just refresh the page if you want to change it 31 + 32 + if (isWeb) { 33 + const kawaii = new URLSearchParams(window.location.search).get('kawaii') 34 + switch (kawaii) { 35 + case 'true': 36 + setStateWrapped(true) 37 + break 38 + case 'false': 39 + setStateWrapped(false) 40 + break 41 + } 42 + } 43 + }, [setStateWrapped]) 44 + 45 + return <stateContext.Provider value={state}>{children}</stateContext.Provider> 46 + } 47 + 48 + export function useKawaiiMode() { 49 + return React.useContext(stateContext) 50 + }
+9 -4
src/view/com/auth/SplashScreen.web.tsx
··· 4 import {msg, Trans} from '@lingui/macro' 5 import {useLingui} from '@lingui/react' 6 7 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 8 import {Logo} from '#/view/icons/Logo' 9 import {Logotype} from '#/view/icons/Logotype' ··· 27 const {_} = useLingui() 28 const t = useTheme() 29 const {isTabletOrMobile: isMobileWeb} = useWebMediaQueries() 30 31 return ( 32 <> ··· 66 ]}> 67 <ErrorBoundary> 68 <View style={[a.justify_center, a.align_center]}> 69 - <Logo width={92} fill="sky" /> 70 71 - <View style={[a.pb_sm, a.pt_5xl]}> 72 - <Logotype width={161} fill={t.atoms.text.color} /> 73 - </View> 74 75 <Text 76 style={[
··· 4 import {msg, Trans} from '@lingui/macro' 5 import {useLingui} from '@lingui/react' 6 7 + import {useKawaiiMode} from '#/state/preferences/kawaii' 8 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 9 import {Logo} from '#/view/icons/Logo' 10 import {Logotype} from '#/view/icons/Logotype' ··· 28 const {_} = useLingui() 29 const t = useTheme() 30 const {isTabletOrMobile: isMobileWeb} = useWebMediaQueries() 31 + 32 + const kawaii = useKawaiiMode() 33 34 return ( 35 <> ··· 69 ]}> 70 <ErrorBoundary> 71 <View style={[a.justify_center, a.align_center]}> 72 + <Logo width={kawaii ? 300 : 92} fill="sky" /> 73 74 + {!kawaii && ( 75 + <View style={[a.pb_sm, a.pt_5xl]}> 76 + <Logotype width={161} fill={t.atoms.text.color} /> 77 + </View> 78 + )} 79 80 <Text 81 style={[
+12 -2
src/view/com/home/HomeHeaderLayout.web.tsx
··· 15 import {usePalette} from 'lib/hooks/usePalette' 16 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 17 import {Logo} from '#/view/icons/Logo' 18 import {Link} from '../util/Link' 19 import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' 20 ··· 43 const {hasSession} = useSession() 44 const {_} = useLingui() 45 46 return ( 47 <> 48 {hasSession && ( 49 - <View style={[pal.view, pal.border, styles.bar, styles.topBar]}> 50 <Link 51 href="/settings/following-feed" 52 hitSlop={10} ··· 58 style={pal.textLight as FontAwesomeIconStyle} 59 /> 60 </Link> 61 - <Logo width={28} /> 62 <Link 63 href="/settings/saved-feeds" 64 hitSlop={10}
··· 15 import {usePalette} from 'lib/hooks/usePalette' 16 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 17 import {Logo} from '#/view/icons/Logo' 18 + import {useKawaiiMode} from '../../../state/preferences/kawaii' 19 import {Link} from '../util/Link' 20 import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile' 21 ··· 44 const {hasSession} = useSession() 45 const {_} = useLingui() 46 47 + const kawaii = useKawaiiMode() 48 + 49 return ( 50 <> 51 {hasSession && ( 52 + <View 53 + style={[ 54 + pal.view, 55 + pal.border, 56 + styles.bar, 57 + styles.topBar, 58 + kawaii && {paddingTop: 4, paddingBottom: 0}, 59 + ]}> 60 <Link 61 href="/settings/following-feed" 62 hitSlop={10} ··· 68 style={pal.textLight as FontAwesomeIconStyle} 69 /> 70 </Link> 71 + <Logo width={kawaii ? 60 : 28} /> 72 <Link 73 href="/settings/saved-feeds" 74 hitSlop={10}
+23 -2
src/view/icons/Logo.tsx
··· 1 import React from 'react' 2 import {StyleSheet, TextProps} from 'react-native' 3 import Svg, { 4 - Path, 5 Defs, 6 LinearGradient, 7 Stop, 8 SvgProps, 9 - PathProps, 10 } from 'react-native-svg' 11 12 import {colors} from '#/lib/styles' 13 14 const ratio = 57 / 64 15 ··· 25 const _fill = gradient ? 'url(#sky)' : fill || styles?.color || colors.blue3 26 // @ts-ignore it's fiiiiine 27 const size = parseInt(rest.width || 32) 28 return ( 29 <Svg 30 fill="none"
··· 1 import React from 'react' 2 import {StyleSheet, TextProps} from 'react-native' 3 import Svg, { 4 Defs, 5 LinearGradient, 6 + Path, 7 + PathProps, 8 Stop, 9 SvgProps, 10 } from 'react-native-svg' 11 + import {Image} from 'expo-image' 12 13 import {colors} from '#/lib/styles' 14 + import {useKawaiiMode} from '#/state/preferences/kawaii' 15 16 const ratio = 57 / 64 17 ··· 27 const _fill = gradient ? 'url(#sky)' : fill || styles?.color || colors.blue3 28 // @ts-ignore it's fiiiiine 29 const size = parseInt(rest.width || 32) 30 + 31 + const isKawaii = useKawaiiMode() 32 + 33 + if (isKawaii) { 34 + return ( 35 + <Image 36 + source={ 37 + size > 100 38 + ? require('../../../assets/kawaii.png') 39 + : require('../../../assets/kawaii_smol.png') 40 + } 41 + accessibilityLabel="Bluesky" 42 + accessibilityHint="" 43 + accessibilityIgnoresInvertColors 44 + style={[{height: size, aspectRatio: 1.4}]} 45 + /> 46 + ) 47 + } 48 + 49 return ( 50 <Svg 51 fill="none"
+13
src/view/shell/Drawer.tsx
··· 18 import {StackActions, useNavigation} from '@react-navigation/native' 19 20 import {emitSoftReset} from '#/state/events' 21 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 22 import {useProfileQuery} from '#/state/queries/profile' 23 import {SessionAccount, useSession} from '#/state/session' ··· 117 const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} = 118 useNavigationTabState() 119 const {hasSession, currentAccount} = useSession() 120 121 // events 122 // = ··· 262 href="https://bsky.social/about/support/privacy-policy" 263 text={_(msg`Privacy Policy`)} 264 /> 265 </View> 266 267 <View style={styles.smallSpacer} />
··· 18 import {StackActions, useNavigation} from '@react-navigation/native' 19 20 import {emitSoftReset} from '#/state/events' 21 + import {useKawaiiMode} from '#/state/preferences/kawaii' 22 import {useUnreadNotifications} from '#/state/queries/notifications/unread' 23 import {useProfileQuery} from '#/state/queries/profile' 24 import {SessionAccount, useSession} from '#/state/session' ··· 118 const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} = 119 useNavigationTabState() 120 const {hasSession, currentAccount} = useSession() 121 + const kawaii = useKawaiiMode() 122 123 // events 124 // = ··· 264 href="https://bsky.social/about/support/privacy-policy" 265 text={_(msg`Privacy Policy`)} 266 /> 267 + {kawaii && ( 268 + <Text type="md" style={pal.textLight}> 269 + Logo by{' '} 270 + <TextLink 271 + type="md" 272 + href="/profile/sawaratsuki.bsky.social" 273 + text="@sawaratsuki.bsky.social" 274 + style={pal.link} 275 + /> 276 + </Text> 277 + )} 278 </View> 279 280 <View style={styles.smallSpacer} />
+24 -9
src/view/shell/desktop/RightNav.tsx
··· 1 import React from 'react' 2 import {StyleSheet, View} from 'react-native' 3 - import {usePalette} from 'lib/hooks/usePalette' 4 - import {DesktopSearch} from './Search' 5 - import {DesktopFeeds} from './Feeds' 6 - import {Text} from 'view/com/util/text/Text' 7 - import {TextLink} from 'view/com/util/Link' 8 - import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' 9 - import {s} from 'lib/styles' 10 - import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 11 import {useLingui} from '@lingui/react' 12 - import {msg} from '@lingui/macro' 13 import {useSession} from '#/state/session' 14 15 export function DesktopRightNav({routeName}: {routeName: string}) { 16 const pal = usePalette('default') 17 const {_} = useLingui() 18 const {hasSession, currentAccount} = useSession() 19 20 const {isTablet} = useWebMediaQueries() 21 if (isTablet) { ··· 90 text={_(msg`Help`)} 91 /> 92 </View> 93 </View> 94 </View> 95 </View>
··· 1 import React from 'react' 2 import {StyleSheet, View} from 'react-native' 3 + import {msg} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 + 6 + import {useKawaiiMode} from '#/state/preferences/kawaii' 7 import {useSession} from '#/state/session' 8 + import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants' 9 + import {usePalette} from 'lib/hooks/usePalette' 10 + import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 11 + import {s} from 'lib/styles' 12 + import {TextLink} from 'view/com/util/Link' 13 + import {Text} from 'view/com/util/text/Text' 14 + import {DesktopFeeds} from './Feeds' 15 + import {DesktopSearch} from './Search' 16 17 export function DesktopRightNav({routeName}: {routeName: string}) { 18 const pal = usePalette('default') 19 const {_} = useLingui() 20 const {hasSession, currentAccount} = useSession() 21 + 22 + const kawaii = useKawaiiMode() 23 24 const {isTablet} = useWebMediaQueries() 25 if (isTablet) { ··· 94 text={_(msg`Help`)} 95 /> 96 </View> 97 + {kawaii && ( 98 + <Text type="md" style={[pal.textLight, {marginTop: 12}]}> 99 + Logo by{' '} 100 + <TextLink 101 + type="md" 102 + href="/profile/sawaratsuki.bsky.social" 103 + text="@sawaratsuki.bsky.social" 104 + style={pal.link} 105 + /> 106 + </Text> 107 + )} 108 </View> 109 </View> 110 </View>