Add kawaii mode (#3773)
parent
181e61bedb
commit
81ae7e425d
|
@ -20,8 +20,8 @@ jobs:
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.21'
|
||||||
- name: Dummy JS File
|
- name: Dummy Static Files
|
||||||
run: touch bskyweb/static/js/blah.js
|
run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt
|
||||||
- name: Check
|
- name: Check
|
||||||
run: cd bskyweb/ && make check
|
run: cd bskyweb/ && make check
|
||||||
- name: Build (binary)
|
- name: Build (binary)
|
||||||
|
@ -37,7 +37,7 @@ jobs:
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.21'
|
||||||
- name: Dummy JS File
|
- name: Dummy Static Files
|
||||||
run: touch bskyweb/static/js/blah.js
|
run: touch bskyweb/static/js/blah.js && touch bskyweb/static/media/blah.txt
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: cd bskyweb/ && make lint
|
run: cd bskyweb/ && make lint
|
||||||
|
|
|
@ -40,6 +40,7 @@ RUN find ./bskyweb/embedr-static && find ./bskyweb/embedr-templates && find ./bs
|
||||||
|
|
||||||
# hack around issue with empty directory and go:embed
|
# hack around issue with empty directory and go:embed
|
||||||
RUN touch bskyweb/static/js/empty.txt
|
RUN touch bskyweb/static/js/empty.txt
|
||||||
|
RUN touch bskyweb/static/media/empty.txt
|
||||||
|
|
||||||
#
|
#
|
||||||
# Generate the embedr Go binary.
|
# Generate the embedr Go binary.
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
|
@ -10,6 +10,8 @@ static/js/*.js
|
||||||
static/js/*.map
|
static/js/*.map
|
||||||
static/js/*.js.LICENSE.txt
|
static/js/*.js.LICENSE.txt
|
||||||
static/js/empty.txt
|
static/js/empty.txt
|
||||||
|
static/media/*.png
|
||||||
|
static/media/empty.txt
|
||||||
templates/scripts.html
|
templates/scripts.html
|
||||||
templates/*-embed.html
|
templates/*-embed.html
|
||||||
static/embed/*.html
|
static/embed/*.html
|
||||||
|
|
|
@ -158,7 +158,7 @@ func serve(cctx *cli.Context) error {
|
||||||
|
|
||||||
// Cache javascript and images files for 1 week, which works because
|
// Cache javascript and images files for 1 week, which works because
|
||||||
// they're always versioned (e.g. /static/js/main.64c14927.js)
|
// they're always versioned (e.g. /static/js/main.64c14927.js)
|
||||||
if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/images/") {
|
if strings.HasPrefix(path, "/static/js/") || strings.HasPrefix(path, "/static/images/") || strings.HasPrefix(path, "/static/media/") {
|
||||||
maxAge = 7 * (60 * 60 * 24) // 1 week
|
maxAge = 7 * (60 * 60 * 24) // 1 week
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
"web": "expo start --web",
|
"web": "expo start --web",
|
||||||
"use-build-number": "./scripts/useBuildNumberEnv.sh",
|
"use-build-number": "./scripts/useBuildNumberEnv.sh",
|
||||||
"use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh",
|
"use-build-number-with-bump": "./scripts/useBuildNumberEnvWithBump.sh",
|
||||||
"build-web": "expo export:web && node ./scripts/post-web-build.js && cp -v ./web-build/static/js/*.* ./bskyweb/static/js/",
|
"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/",
|
||||||
"build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all",
|
"build-all": "yarn intl:build && yarn use-build-number-with-bump eas build --platform all",
|
||||||
"build-ios": "yarn use-build-number-with-bump eas build -p ios",
|
"build-ios": "yarn use-build-number-with-bump eas build -p ios",
|
||||||
"build-android": "yarn use-build-number-with-bump eas build -p android",
|
"build-android": "yarn use-build-number-with-bump eas build -p android",
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import EventEmitter from 'eventemitter3'
|
import EventEmitter from 'eventemitter3'
|
||||||
import {logger} from '#/logger'
|
|
||||||
import {defaults, Schema} from '#/state/persisted/schema'
|
|
||||||
import {migrate} from '#/state/persisted/legacy'
|
|
||||||
import * as store from '#/state/persisted/store'
|
|
||||||
import BroadcastChannel from '#/lib/broadcast'
|
|
||||||
|
|
||||||
export type {Schema, PersistedAccount} from '#/state/persisted/schema'
|
import BroadcastChannel from '#/lib/broadcast'
|
||||||
|
import {logger} from '#/logger'
|
||||||
|
import {migrate} from '#/state/persisted/legacy'
|
||||||
|
import {defaults, Schema} from '#/state/persisted/schema'
|
||||||
|
import * as store from '#/state/persisted/store'
|
||||||
|
export type {PersistedAccount, Schema} from '#/state/persisted/schema'
|
||||||
export {defaults} from '#/state/persisted/schema'
|
export {defaults} from '#/state/persisted/schema'
|
||||||
|
|
||||||
const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL')
|
const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL')
|
||||||
|
|
|
@ -80,6 +80,7 @@ export const schema = z.object({
|
||||||
pdsAddressHistory: z.array(z.string()).optional(),
|
pdsAddressHistory: z.array(z.string()).optional(),
|
||||||
disableHaptics: z.boolean().optional(),
|
disableHaptics: z.boolean().optional(),
|
||||||
disableAutoplay: z.boolean().optional(),
|
disableAutoplay: z.boolean().optional(),
|
||||||
|
kawaii: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
export type Schema = z.infer<typeof schema>
|
export type Schema = z.infer<typeof schema>
|
||||||
|
|
||||||
|
@ -117,4 +118,5 @@ export const defaults: Schema = {
|
||||||
pdsAddressHistory: [],
|
pdsAddressHistory: [],
|
||||||
disableHaptics: false,
|
disableHaptics: false,
|
||||||
disableAutoplay: prefersReducedMotion,
|
disableAutoplay: prefersReducedMotion,
|
||||||
|
kawaii: false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {Provider as DisableHapticsProvider} from './disable-haptics'
|
||||||
import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs'
|
import {Provider as ExternalEmbedsProvider} from './external-embeds-prefs'
|
||||||
import {Provider as HiddenPostsProvider} from './hidden-posts'
|
import {Provider as HiddenPostsProvider} from './hidden-posts'
|
||||||
import {Provider as InAppBrowserProvider} from './in-app-browser'
|
import {Provider as InAppBrowserProvider} from './in-app-browser'
|
||||||
|
import {Provider as KawaiiProvider} from './kawaii'
|
||||||
import {Provider as LanguagesProvider} from './languages'
|
import {Provider as LanguagesProvider} from './languages'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -32,7 +33,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
<InAppBrowserProvider>
|
<InAppBrowserProvider>
|
||||||
<DisableHapticsProvider>
|
<DisableHapticsProvider>
|
||||||
<AutoplayProvider>
|
<AutoplayProvider>
|
||||||
<DmServiceUrlProvider>{children}</DmServiceUrlProvider>
|
<DmServiceUrlProvider>
|
||||||
|
<KawaiiProvider>{children}</KawaiiProvider>
|
||||||
|
</DmServiceUrlProvider>
|
||||||
</AutoplayProvider>
|
</AutoplayProvider>
|
||||||
</DisableHapticsProvider>
|
</DisableHapticsProvider>
|
||||||
</InAppBrowserProvider>
|
</InAppBrowserProvider>
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import {isWeb} from '#/platform/detection'
|
||||||
|
import * as persisted from '#/state/persisted'
|
||||||
|
|
||||||
|
type StateContext = persisted.Schema['kawaii']
|
||||||
|
|
||||||
|
const stateContext = React.createContext<StateContext>(
|
||||||
|
persisted.defaults.kawaii,
|
||||||
|
)
|
||||||
|
|
||||||
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
const [state, setState] = React.useState(persisted.get('kawaii'))
|
||||||
|
|
||||||
|
const setStateWrapped = React.useCallback(
|
||||||
|
(kawaii: persisted.Schema['kawaii']) => {
|
||||||
|
setState(kawaii)
|
||||||
|
persisted.write('kawaii', kawaii)
|
||||||
|
},
|
||||||
|
[setState],
|
||||||
|
)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
return persisted.onUpdate(() => {
|
||||||
|
setState(persisted.get('kawaii'))
|
||||||
|
})
|
||||||
|
}, [setStateWrapped])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// dumb and stupid but it's web only so just refresh the page if you want to change it
|
||||||
|
|
||||||
|
if (isWeb) {
|
||||||
|
const kawaii = new URLSearchParams(window.location.search).get('kawaii')
|
||||||
|
switch (kawaii) {
|
||||||
|
case 'true':
|
||||||
|
setStateWrapped(true)
|
||||||
|
break
|
||||||
|
case 'false':
|
||||||
|
setStateWrapped(false)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [setStateWrapped])
|
||||||
|
|
||||||
|
return <stateContext.Provider value={state}>{children}</stateContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useKawaiiMode() {
|
||||||
|
return React.useContext(stateContext)
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {Logo} from '#/view/icons/Logo'
|
import {Logo} from '#/view/icons/Logo'
|
||||||
import {Logotype} from '#/view/icons/Logotype'
|
import {Logotype} from '#/view/icons/Logotype'
|
||||||
|
@ -28,6 +29,8 @@ export const SplashScreen = ({
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {isTabletOrMobile: isMobileWeb} = useWebMediaQueries()
|
const {isTabletOrMobile: isMobileWeb} = useWebMediaQueries()
|
||||||
|
|
||||||
|
const kawaii = useKawaiiMode()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{onDismiss && (
|
{onDismiss && (
|
||||||
|
@ -66,11 +69,13 @@ export const SplashScreen = ({
|
||||||
]}>
|
]}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<View style={[a.justify_center, a.align_center]}>
|
<View style={[a.justify_center, a.align_center]}>
|
||||||
<Logo width={92} fill="sky" />
|
<Logo width={kawaii ? 300 : 92} fill="sky" />
|
||||||
|
|
||||||
|
{!kawaii && (
|
||||||
<View style={[a.pb_sm, a.pt_5xl]}>
|
<View style={[a.pb_sm, a.pt_5xl]}>
|
||||||
<Logotype width={161} fill={t.atoms.text.color} />
|
<Logotype width={161} fill={t.atoms.text.color} />
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
|
|
|
@ -15,6 +15,7 @@ import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
import {Logo} from '#/view/icons/Logo'
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
import {useKawaiiMode} from '../../../state/preferences/kawaii'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile'
|
import {HomeHeaderLayoutMobile} from './HomeHeaderLayoutMobile'
|
||||||
|
|
||||||
|
@ -43,10 +44,19 @@ function HomeHeaderLayoutDesktopAndTablet({
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
|
|
||||||
|
const kawaii = useKawaiiMode()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{hasSession && (
|
{hasSession && (
|
||||||
<View style={[pal.view, pal.border, styles.bar, styles.topBar]}>
|
<View
|
||||||
|
style={[
|
||||||
|
pal.view,
|
||||||
|
pal.border,
|
||||||
|
styles.bar,
|
||||||
|
styles.topBar,
|
||||||
|
kawaii && {paddingTop: 4, paddingBottom: 0},
|
||||||
|
]}>
|
||||||
<Link
|
<Link
|
||||||
href="/settings/following-feed"
|
href="/settings/following-feed"
|
||||||
hitSlop={10}
|
hitSlop={10}
|
||||||
|
@ -58,7 +68,7 @@ function HomeHeaderLayoutDesktopAndTablet({
|
||||||
style={pal.textLight as FontAwesomeIconStyle}
|
style={pal.textLight as FontAwesomeIconStyle}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<Logo width={28} />
|
<Logo width={kawaii ? 60 : 28} />
|
||||||
<Link
|
<Link
|
||||||
href="/settings/saved-feeds"
|
href="/settings/saved-feeds"
|
||||||
hitSlop={10}
|
hitSlop={10}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TextProps} from 'react-native'
|
import {StyleSheet, TextProps} from 'react-native'
|
||||||
import Svg, {
|
import Svg, {
|
||||||
Path,
|
|
||||||
Defs,
|
Defs,
|
||||||
LinearGradient,
|
LinearGradient,
|
||||||
|
Path,
|
||||||
|
PathProps,
|
||||||
Stop,
|
Stop,
|
||||||
SvgProps,
|
SvgProps,
|
||||||
PathProps,
|
|
||||||
} from 'react-native-svg'
|
} from 'react-native-svg'
|
||||||
|
import {Image} from 'expo-image'
|
||||||
|
|
||||||
import {colors} from '#/lib/styles'
|
import {colors} from '#/lib/styles'
|
||||||
|
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||||
|
|
||||||
const ratio = 57 / 64
|
const ratio = 57 / 64
|
||||||
|
|
||||||
|
@ -25,6 +27,25 @@ export const Logo = React.forwardRef(function LogoImpl(props: Props, ref) {
|
||||||
const _fill = gradient ? 'url(#sky)' : fill || styles?.color || colors.blue3
|
const _fill = gradient ? 'url(#sky)' : fill || styles?.color || colors.blue3
|
||||||
// @ts-ignore it's fiiiiine
|
// @ts-ignore it's fiiiiine
|
||||||
const size = parseInt(rest.width || 32)
|
const size = parseInt(rest.width || 32)
|
||||||
|
|
||||||
|
const isKawaii = useKawaiiMode()
|
||||||
|
|
||||||
|
if (isKawaii) {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
source={
|
||||||
|
size > 100
|
||||||
|
? require('../../../assets/kawaii.png')
|
||||||
|
: require('../../../assets/kawaii_smol.png')
|
||||||
|
}
|
||||||
|
accessibilityLabel="Bluesky"
|
||||||
|
accessibilityHint=""
|
||||||
|
accessibilityIgnoresInvertColors
|
||||||
|
style={[{height: size, aspectRatio: 1.4}]}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Svg
|
<Svg
|
||||||
fill="none"
|
fill="none"
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {useLingui} from '@lingui/react'
|
||||||
import {StackActions, useNavigation} from '@react-navigation/native'
|
import {StackActions, useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
import {emitSoftReset} from '#/state/events'
|
import {emitSoftReset} from '#/state/events'
|
||||||
|
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||||
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||||
import {useProfileQuery} from '#/state/queries/profile'
|
import {useProfileQuery} from '#/state/queries/profile'
|
||||||
import {SessionAccount, useSession} from '#/state/session'
|
import {SessionAccount, useSession} from '#/state/session'
|
||||||
|
@ -117,6 +118,7 @@ let DrawerContent = ({}: {}): React.ReactNode => {
|
||||||
const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} =
|
const {isAtHome, isAtSearch, isAtFeeds, isAtNotifications, isAtMyProfile} =
|
||||||
useNavigationTabState()
|
useNavigationTabState()
|
||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
|
const kawaii = useKawaiiMode()
|
||||||
|
|
||||||
// events
|
// events
|
||||||
// =
|
// =
|
||||||
|
@ -262,6 +264,17 @@ let DrawerContent = ({}: {}): React.ReactNode => {
|
||||||
href="https://bsky.social/about/support/privacy-policy"
|
href="https://bsky.social/about/support/privacy-policy"
|
||||||
text={_(msg`Privacy Policy`)}
|
text={_(msg`Privacy Policy`)}
|
||||||
/>
|
/>
|
||||||
|
{kawaii && (
|
||||||
|
<Text type="md" style={pal.textLight}>
|
||||||
|
Logo by{' '}
|
||||||
|
<TextLink
|
||||||
|
type="md"
|
||||||
|
href="/profile/sawaratsuki.bsky.social"
|
||||||
|
text="@sawaratsuki.bsky.social"
|
||||||
|
style={pal.link}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.smallSpacer} />
|
<View style={styles.smallSpacer} />
|
||||||
|
|
|
@ -1,22 +1,26 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
|
||||||
import {DesktopSearch} from './Search'
|
|
||||||
import {DesktopFeeds} from './Feeds'
|
|
||||||
import {Text} from 'view/com/util/text/Text'
|
|
||||||
import {TextLink} from 'view/com/util/Link'
|
|
||||||
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants'
|
|
||||||
import {s} from 'lib/styles'
|
|
||||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants'
|
||||||
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
|
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||||
|
import {s} from 'lib/styles'
|
||||||
|
import {TextLink} from 'view/com/util/Link'
|
||||||
|
import {Text} from 'view/com/util/text/Text'
|
||||||
|
import {DesktopFeeds} from './Feeds'
|
||||||
|
import {DesktopSearch} from './Search'
|
||||||
|
|
||||||
export function DesktopRightNav({routeName}: {routeName: string}) {
|
export function DesktopRightNav({routeName}: {routeName: string}) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
|
|
||||||
|
const kawaii = useKawaiiMode()
|
||||||
|
|
||||||
const {isTablet} = useWebMediaQueries()
|
const {isTablet} = useWebMediaQueries()
|
||||||
if (isTablet) {
|
if (isTablet) {
|
||||||
return null
|
return null
|
||||||
|
@ -90,6 +94,17 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
||||||
text={_(msg`Help`)}
|
text={_(msg`Help`)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
{kawaii && (
|
||||||
|
<Text type="md" style={[pal.textLight, {marginTop: 12}]}>
|
||||||
|
Logo by{' '}
|
||||||
|
<TextLink
|
||||||
|
type="md"
|
||||||
|
href="/profile/sawaratsuki.bsky.social"
|
||||||
|
text="@sawaratsuki.bsky.social"
|
||||||
|
style={pal.link}
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
Loading…
Reference in New Issue