mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Theme cleanup, expose extra reader themes
This also removes synced reader preferences so everything is per device now.
This commit is contained in:
parent
bf0a7d2865
commit
23efda667d
16 changed files with 172 additions and 313 deletions
|
|
@ -32,7 +32,7 @@ export function LogoBox(): JSX.Element {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<OmnivoreNameLogo />
|
||||
<OmnivoreNameLogo color={theme.colors.thHighContrast.toString()} />
|
||||
</SpanBox>
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,8 @@ import { currentThemeName } from '../../lib/themeUpdater'
|
|||
import { Check } from 'phosphor-react'
|
||||
|
||||
export type HeaderDropdownAction =
|
||||
| 'apply-darker-theme'
|
||||
| 'apply-dark-theme'
|
||||
| 'apply-light-theme'
|
||||
| 'apply-lighter-theme'
|
||||
| 'navigate-to-install'
|
||||
| 'navigate-to-emails'
|
||||
| 'navigate-to-labels'
|
||||
|
|
@ -49,7 +47,7 @@ export function DropdownMenu(props: DropdownMenuProps): JSX.Element {
|
|||
css={{ background: '#FFFFFF' }}
|
||||
data-state={isDark ? 'unselected' : 'selected'}
|
||||
onClick={() => {
|
||||
props.actionHandler('apply-lighter-theme')
|
||||
props.actionHandler('apply-light-theme')
|
||||
setCurrentTheme(currentThemeName())
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ function ThemeSection(props: PrimaryDropdownProps): JSX.Element {
|
|||
}}
|
||||
>
|
||||
<StyledToggleButton
|
||||
data-state={currentTheme() != ThemeId.Darker ? 'on' : 'off'}
|
||||
data-state={currentTheme() != ThemeId.Dark ? 'on' : 'off'}
|
||||
onClick={() => {
|
||||
updateTheme(ThemeId.Light)
|
||||
}}
|
||||
|
|
@ -243,9 +243,9 @@ function ThemeSection(props: PrimaryDropdownProps): JSX.Element {
|
|||
<Sun size={15} color={theme.colors.thTextContrast2.toString()} />
|
||||
</StyledToggleButton>
|
||||
<StyledToggleButton
|
||||
data-state={currentTheme() == ThemeId.Darker ? 'on' : 'off'}
|
||||
data-state={currentTheme() == ThemeId.Dark ? 'on' : 'off'}
|
||||
onClick={() => {
|
||||
updateTheme(ThemeId.Darker)
|
||||
updateTheme(ThemeId.Dark)
|
||||
}}
|
||||
>
|
||||
Dark
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { KeyboardShortcutListModal } from './KeyboardShortcutListModal'
|
|||
import { logoutMutation } from '../../lib/networking/mutations/logoutMutation'
|
||||
import { setupAnalytics } from '../../lib/analytics'
|
||||
import { primaryCommands } from '../../lib/keyboardShortcuts/navigationShortcuts'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
|
||||
type PrimaryLayoutProps = {
|
||||
children: ReactNode
|
||||
|
|
@ -21,6 +22,8 @@ type PrimaryLayoutProps = {
|
|||
}
|
||||
|
||||
export function PrimaryLayout(props: PrimaryLayoutProps): JSX.Element {
|
||||
applyStoredTheme(false)
|
||||
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
const router = useRouter()
|
||||
const [showLogoutConfirmation, setShowLogoutConfirmation] = useState(false)
|
||||
|
|
|
|||
|
|
@ -9,17 +9,15 @@ import {
|
|||
import { theme, ThemeId } from './../../tokens/stitches.config'
|
||||
import { HighlightsLayer } from '../../templates/article/HighlightsLayer'
|
||||
import { Button } from '../../elements/Button'
|
||||
import { useEffect, useState, useRef, useMemo } from 'react'
|
||||
import { useEffect, useState, useRef, useMemo, useCallback } from 'react'
|
||||
import { ReportIssuesModal } from './ReportIssuesModal'
|
||||
import { reportIssueMutation } from '../../../lib/networking/mutations/reportIssueMutation'
|
||||
import { userPersonalizationMutation } from '../../../lib/networking/mutations/userPersonalizationMutation'
|
||||
import { updateTheme, updateThemeLocally } from '../../../lib/themeUpdater'
|
||||
import { ArticleMutations } from '../../../lib/articleActions'
|
||||
import { LabelChip } from '../../elements/LabelChip'
|
||||
import { Label } from '../../../lib/networking/fragments/labelFragment'
|
||||
import { Recommendation } from '../../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { Avatar } from '../../elements/Avatar'
|
||||
import { usePersistedState } from '../../../lib/hooks/usePersistedState'
|
||||
|
||||
type ArticleContainerProps = {
|
||||
article: ArticleAttributes
|
||||
|
|
@ -125,12 +123,14 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
|||
window.location.hash ? window.location.hash.split('#')[1] : null
|
||||
)
|
||||
|
||||
const updateFontSize = async (newFontSize: number) => {
|
||||
if (fontSize !== newFontSize) {
|
||||
setFontSize(newFontSize)
|
||||
await userPersonalizationMutation({ fontSize: newFontSize })
|
||||
}
|
||||
}
|
||||
const updateFontSize = useCallback(
|
||||
(newFontSize: number) => {
|
||||
if (fontSize !== newFontSize) {
|
||||
setFontSize(newFontSize)
|
||||
}
|
||||
},
|
||||
[setFontSize]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
updateFontSize(props.fontSize ?? 20)
|
||||
|
|
@ -279,7 +279,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
|||
<Box
|
||||
id="article-container"
|
||||
css={{
|
||||
padding: '16px',
|
||||
padding: '30px',
|
||||
paddingTop: '80px',
|
||||
maxWidth: `${styles.maxWidthPercentage ?? 100}%`,
|
||||
background: props.isAppleAppEmbed
|
||||
|
|
@ -310,6 +310,9 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
|||
? `${styles.maxWidthPercentage}%`
|
||||
: 1024 - styles.margin,
|
||||
},
|
||||
'@mdDown': {
|
||||
padding: '15px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<VStack alignment="start" distribution="start">
|
||||
|
|
@ -325,6 +328,7 @@ export function ArticleContainer(props: ArticleContainerProps): JSX.Element {
|
|||
fontFamily: styles.fontFamily,
|
||||
width: '100%',
|
||||
wordWrap: 'break-word',
|
||||
color: styles.readerFontColor,
|
||||
}}
|
||||
>
|
||||
{props.article.title}
|
||||
|
|
|
|||
|
|
@ -520,10 +520,7 @@ function LayoutControls(props: LayoutControlsProps): JSX.Element {
|
|||
|
||||
function ThemeSelector(props: ReaderSettingsProps): JSX.Element {
|
||||
const [currentTheme, setCurrentTheme] = useState(currentThemeName())
|
||||
|
||||
const isDark = useMemo(() => {
|
||||
return currentTheme === 'Dark' || currentTheme === 'Darker'
|
||||
}, [currentTheme])
|
||||
console.log('currentTheme: ', currentTheme)
|
||||
|
||||
return (
|
||||
<VStack
|
||||
|
|
@ -563,13 +560,15 @@ function ThemeSelector(props: ReaderSettingsProps): JSX.Element {
|
|||
border: '2px solid #6A6968',
|
||||
},
|
||||
}}
|
||||
data-state={isDark ? 'unselected' : 'selected'}
|
||||
data-state={currentTheme == ThemeId.Light ? 'unselected' : 'selected'}
|
||||
onClick={() => {
|
||||
updateTheme(ThemeId.Light)
|
||||
setCurrentTheme(currentThemeName())
|
||||
}}
|
||||
>
|
||||
{!isDark && <Check color="#6A6968" size={15} weight="bold" />}
|
||||
{currentTheme == ThemeId.Light && (
|
||||
<Check color="#6A6968" size={15} weight="bold" />
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
style="themeSwitch"
|
||||
|
|
@ -591,13 +590,73 @@ function ThemeSelector(props: ReaderSettingsProps): JSX.Element {
|
|||
border: '2px solid #6A6968',
|
||||
},
|
||||
}}
|
||||
data-state={isDark ? 'selected' : 'unselected'}
|
||||
data-state={currentTheme == ThemeId.Dark ? 'selected' : 'unselected'}
|
||||
onClick={() => {
|
||||
updateTheme(ThemeId.Dark)
|
||||
setCurrentTheme(currentThemeName())
|
||||
}}
|
||||
>
|
||||
{isDark && <Check color="#F9D354" size={20} />}
|
||||
{currentTheme == ThemeId.Dark && <Check color="#F9D354" size={20} />}
|
||||
</Button>
|
||||
<Button
|
||||
style="themeSwitch"
|
||||
css={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
alignContent: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
background: '#FBF0D9',
|
||||
borderRadius: '50%',
|
||||
border: 'unset',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.1)',
|
||||
border: '2px solid #6A6968',
|
||||
},
|
||||
'&[data-state="selected"]': {
|
||||
border: '2px solid #6A6968',
|
||||
},
|
||||
}}
|
||||
data-state={currentTheme == ThemeId.Sepia ? 'selected' : 'unselected'}
|
||||
onClick={() => {
|
||||
updateTheme(ThemeId.Sepia)
|
||||
setCurrentTheme(currentThemeName())
|
||||
}}
|
||||
>
|
||||
{currentTheme == ThemeId.Sepia && <Check color="#F9D354" size={20} />}
|
||||
</Button>
|
||||
<Button
|
||||
style="themeSwitch"
|
||||
css={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
alignContent: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
background: '#F3F3F3',
|
||||
borderRadius: '50%',
|
||||
border: 'unset',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.1)',
|
||||
border: '2px solid #6A6968',
|
||||
},
|
||||
'&[data-state="selected"]': {
|
||||
border: '2px solid #6A6968',
|
||||
},
|
||||
}}
|
||||
data-state={
|
||||
currentTheme == ThemeId.Apollo ? 'selected' : 'unselected'
|
||||
}
|
||||
onClick={() => {
|
||||
updateTheme(ThemeId.Apollo)
|
||||
setCurrentTheme(currentThemeName())
|
||||
}}
|
||||
>
|
||||
{currentTheme == ThemeId.Apollo && (
|
||||
<Check color="#F9D354" size={20} />
|
||||
)}
|
||||
</Button>
|
||||
</HStack>
|
||||
</VStack>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import {
|
|||
} from '../../../lib/networking/fragments/articleFragment'
|
||||
import { Action, createAction, useKBar, useRegisterActions } from 'kbar'
|
||||
import { EditLibraryItemModal } from './EditItemModals'
|
||||
import { useGetUserPreferences } from '../../../lib/networking/queries/useGetUserPreferences'
|
||||
import debounce from 'lodash/debounce'
|
||||
import {
|
||||
SearchItem,
|
||||
|
|
@ -65,8 +64,6 @@ const debouncedFetchSearchResults = debounce((query, cb) => {
|
|||
}, 300)
|
||||
|
||||
export function HomeFeedContainer(): JSX.Element {
|
||||
useGetUserPreferences()
|
||||
|
||||
const { viewerData } = useGetViewerQuery()
|
||||
const router = useRouter()
|
||||
const { queryValue } = useKBar((state) => ({ queryValue: state.searchQuery }))
|
||||
|
|
|
|||
|
|
@ -35,9 +35,12 @@ export function ReaderHeader(props: ReaderHeaderProps): JSX.Element {
|
|||
'@xlgDown': {
|
||||
height: MOBILE_HEADER_HEIGHT,
|
||||
pt: '0px',
|
||||
bg: '$thBackground3',
|
||||
bg: '$readerMargin',
|
||||
borderBottom: '1px solid $thBorderColor',
|
||||
},
|
||||
'@mdDown': {
|
||||
bg: '$readerBg',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@ import type * as Stitches from '@stitches/react'
|
|||
import { createStitches, createTheme } from '@stitches/react'
|
||||
|
||||
export enum ThemeId {
|
||||
Lighter = 'White',
|
||||
Light = 'LightGray',
|
||||
Dark = 'Gray',
|
||||
Darker = 'Dark',
|
||||
Light = 'Light',
|
||||
Dark = 'Dark',
|
||||
Sepia = 'Sepia',
|
||||
Charcoal = 'Charcoal',
|
||||
Apollo = 'Apollo',
|
||||
}
|
||||
|
||||
export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
||||
|
|
@ -150,6 +148,7 @@ export const { styled, css, theme, getCssText, globalCss, keyframes, config } =
|
|||
readerFont: '#3D3D3D',
|
||||
readerFontHighContrast: 'black',
|
||||
readerTableHeader: '#FFFFFF',
|
||||
readerMargin: 'white',
|
||||
|
||||
// Avatar Fallback color
|
||||
avatarBg: '#FFEA9F',
|
||||
|
|
@ -232,6 +231,8 @@ const darkThemeSpec = {
|
|||
readerFont: '#b9b9b9',
|
||||
readerFontHighContrast: 'white',
|
||||
readerTableHeader: '#FFFFFF',
|
||||
readerMargin: '#2A2A2A',
|
||||
|
||||
avatarBg: '#7B5C3E',
|
||||
avatarFont: '#D9D9D9',
|
||||
|
||||
|
|
@ -277,43 +278,38 @@ const darkThemeSpec = {
|
|||
|
||||
const sepiaThemeSpec = {
|
||||
colors: {
|
||||
// Reader Colors
|
||||
readerBg: '#F9F1DC',
|
||||
readerFont: '#554A34',
|
||||
readerFontHighContrast: 'black',
|
||||
readerHeader: '554A34',
|
||||
BACKING: 'red',
|
||||
readerBg: '#FBF0D9',
|
||||
readerFont: '#5F4B32',
|
||||
readerMargin: '#F3F3F3',
|
||||
readerFontHighContrast: '#0A0806',
|
||||
readerTableHeader: '#FFFFFF',
|
||||
},
|
||||
}
|
||||
|
||||
const charcoalThemeSpec = {
|
||||
const apolloThemeSpec = {
|
||||
colors: {
|
||||
// Reader Colors
|
||||
readerBg: '#303030',
|
||||
readerFont: '#b9b9b9',
|
||||
readerBg: '#6A6968',
|
||||
readerFont: '#F3F3F3',
|
||||
readerMargin: '#474747',
|
||||
readerFontHighContrast: 'white',
|
||||
readerHeader: '#b9b9b9',
|
||||
readerTableHeader: '#FFFFFF',
|
||||
},
|
||||
}
|
||||
|
||||
// Dark and Darker theme now match each other.
|
||||
// Use the darkThemeSpec object to make updates.
|
||||
export const darkTheme = createTheme(ThemeId.Dark, darkThemeSpec)
|
||||
export const darkerTheme = createTheme(ThemeId.Darker, darkThemeSpec)
|
||||
export const sepiaTheme = createTheme(ThemeId.Sepia, {
|
||||
...darkThemeSpec,
|
||||
...sepiaThemeSpec,
|
||||
})
|
||||
export const charcoalTheme = createTheme(ThemeId.Charcoal, {
|
||||
export const apolloTheme = createTheme(ThemeId.Apollo, {
|
||||
...darkThemeSpec,
|
||||
...charcoalThemeSpec,
|
||||
colors: {
|
||||
...darkThemeSpec.colors,
|
||||
...apolloThemeSpec.colors,
|
||||
},
|
||||
})
|
||||
|
||||
// Lighter theme now matches the default theme.
|
||||
// This only exists for users that might still have a lighter theme set
|
||||
export const lighterTheme = createTheme(ThemeId.Lighter, {})
|
||||
|
||||
// Apply global styles in here
|
||||
export const globalStyles = globalCss({
|
||||
body: {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
import { useRegisterActions } from 'kbar'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { userPersonalizationMutation } from '../networking/mutations/userPersonalizationMutation'
|
||||
import {
|
||||
useGetUserPreferences,
|
||||
UserPreferences,
|
||||
} from '../networking/queries/useGetUserPreferences'
|
||||
import { applyStoredTheme } from '../themeUpdater'
|
||||
import { usePersistedState } from './usePersistedState'
|
||||
|
||||
const DEFAULT_FONT = 'Inter'
|
||||
|
||||
export type ReaderSettings = {
|
||||
preferencesData: UserPreferences | undefined
|
||||
fontSize: number
|
||||
lineHeight: number
|
||||
marginWidth: number
|
||||
|
|
@ -41,12 +36,13 @@ export type ReaderSettings = {
|
|||
}
|
||||
|
||||
export const useReaderSettings = (): ReaderSettings => {
|
||||
const { preferencesData } = useGetUserPreferences()
|
||||
applyStoredTheme(false)
|
||||
|
||||
const [, updateState] = useState({})
|
||||
|
||||
const [fontSize, setFontSize] = usePersistedState({
|
||||
key: 'fontSize',
|
||||
initialValue: preferencesData?.fontSize ?? 20,
|
||||
initialValue: 20,
|
||||
})
|
||||
const [lineHeight, setLineHeight] = usePersistedState({
|
||||
key: 'lineHeight',
|
||||
|
|
@ -76,12 +72,12 @@ export const useReaderSettings = (): ReaderSettings => {
|
|||
useState(false)
|
||||
const [showDeleteConfirmation, setShowDeleteConfirmation] = useState(false)
|
||||
|
||||
const updateFontSize = async (newFontSize: number) => {
|
||||
setFontSize(newFontSize)
|
||||
;(async () => {
|
||||
await userPersonalizationMutation({ fontSize: newFontSize })
|
||||
})()
|
||||
}
|
||||
const updateFontSize = useCallback(
|
||||
(newFontSize: number) => {
|
||||
setFontSize(newFontSize)
|
||||
},
|
||||
[setFontSize]
|
||||
)
|
||||
|
||||
// const [hideMargins, setHideMargins] = usePersistedState<boolean | undefined>({
|
||||
// key: `--display-hide-margins`,
|
||||
|
|
@ -207,7 +203,6 @@ export const useReaderSettings = (): ReaderSettings => {
|
|||
)
|
||||
|
||||
return {
|
||||
preferencesData,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
marginWidth,
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import {
|
||||
UserPreferences,
|
||||
SortOrder,
|
||||
updateUserPreferencesCache,
|
||||
} from '../queries/useGetUserPreferences'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
|
||||
type UserPersonalizationInput = {
|
||||
theme?: string
|
||||
fontSize?: number
|
||||
fontFamily?: string
|
||||
margin?: number
|
||||
libraryLayoutType?: string
|
||||
librarySortOrder?: SortOrder
|
||||
}
|
||||
|
||||
type SetUserPersonalizationResult = {
|
||||
setUserPersonalization: InnerSetUserPersonalization
|
||||
}
|
||||
|
||||
type InnerSetUserPersonalization = {
|
||||
updatedUserPersonalization?: UserPreferences
|
||||
}
|
||||
|
||||
export async function userPersonalizationMutation(
|
||||
input: UserPersonalizationInput
|
||||
): Promise<UserPreferences | undefined> {
|
||||
const mutation = gql`
|
||||
mutation SetUserPersonalization($input: SetUserPersonalizationInput!) {
|
||||
setUserPersonalization(input: $input) {
|
||||
... on SetUserPersonalizationSuccess {
|
||||
updatedUserPersonalization {
|
||||
id
|
||||
theme
|
||||
fontSize
|
||||
fontFamily
|
||||
margin
|
||||
libraryLayoutType
|
||||
librarySortOrder
|
||||
}
|
||||
}
|
||||
... on SetUserPersonalizationError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
try {
|
||||
const data = await gqlFetcher(mutation, { input })
|
||||
const result = data as SetUserPersonalizationResult | undefined
|
||||
if (result?.setUserPersonalization?.updatedUserPersonalization) {
|
||||
updateUserPreferencesCache(result.setUserPersonalization.updatedUserPersonalization)
|
||||
return result.setUserPersonalization?.updatedUserPersonalization
|
||||
}
|
||||
return undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import useSWR, { mutate } from 'swr'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
import { applyStoredTheme, updateThemeLocally } from '../../themeUpdater'
|
||||
import { ThemeId } from '../../../components/tokens/stitches.config'
|
||||
|
||||
type UserPreferencesResponse = {
|
||||
preferencesData?: UserPreferences
|
||||
preferencesDataError?: unknown
|
||||
isLoading: boolean
|
||||
isValidating: boolean
|
||||
}
|
||||
|
||||
type QueryResponse = {
|
||||
getUserPersonalization: InnerQueryReponse
|
||||
}
|
||||
|
||||
type InnerQueryReponse = {
|
||||
userPersonalization: UserPreferences
|
||||
}
|
||||
|
||||
export type UserPreferences = {
|
||||
id: string
|
||||
theme: string
|
||||
fontSize: number
|
||||
fontFamily: string
|
||||
margin: number
|
||||
lineHeight?: number
|
||||
libraryLayoutType: string
|
||||
librarySortOrder?: SortOrder
|
||||
}
|
||||
|
||||
export type SortOrder = 'ASCENDING' | 'DESCENDING'
|
||||
|
||||
const QUERY = gql`
|
||||
query GetUserPersonalization {
|
||||
getUserPersonalization {
|
||||
... on GetUserPersonalizationSuccess {
|
||||
userPersonalization {
|
||||
id
|
||||
theme
|
||||
margin
|
||||
fontSize
|
||||
fontFamily
|
||||
libraryLayoutType
|
||||
librarySortOrder
|
||||
}
|
||||
}
|
||||
... on GetUserPersonalizationError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export function updateUserPreferencesCache(
|
||||
userPersonalization: UserPreferences
|
||||
): void {
|
||||
mutate(
|
||||
QUERY,
|
||||
{
|
||||
getUserPersonalization: { userPersonalization },
|
||||
},
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
export function useGetUserPreferences(): UserPreferencesResponse {
|
||||
const currentTheme = applyStoredTheme(false)
|
||||
const { data, error, isValidating } = useSWR(QUERY, gqlFetcher, {
|
||||
dedupingInterval: 200000,
|
||||
})
|
||||
|
||||
const preferencesData = (data as QueryResponse | undefined)
|
||||
?.getUserPersonalization.userPersonalization
|
||||
|
||||
const serverThemeKey = preferencesData?.theme as ThemeId | undefined
|
||||
if (!isValidating && serverThemeKey && currentTheme !== serverThemeKey) {
|
||||
updateThemeLocally(serverThemeKey)
|
||||
}
|
||||
|
||||
return {
|
||||
preferencesData,
|
||||
isValidating,
|
||||
preferencesDataError: error, // TODO: figure out error possibilities
|
||||
isLoading: !error && !data,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +1,54 @@
|
|||
import {
|
||||
ThemeId,
|
||||
lighterTheme,
|
||||
darkTheme,
|
||||
darkerTheme,
|
||||
sepiaTheme,
|
||||
apolloTheme,
|
||||
} from '../components/tokens/stitches.config'
|
||||
import { userPersonalizationMutation } from './networking/mutations/userPersonalizationMutation'
|
||||
|
||||
const themeKey = 'theme'
|
||||
|
||||
// Map legacy theme names to their new equivelents
|
||||
const LEGACY_THEMES: { [string: string]: string } = {
|
||||
White: ThemeId.Light,
|
||||
LightGray: ThemeId.Light,
|
||||
Gray: ThemeId.Dark,
|
||||
Darker: ThemeId.Dark,
|
||||
}
|
||||
|
||||
export function updateTheme(themeId: string): void {
|
||||
if (typeof window === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
updateThemeLocally(themeId)
|
||||
userPersonalizationMutation({ theme: themeId })
|
||||
}
|
||||
|
||||
function getTheme(themeId: string) {
|
||||
switch (currentTheme()) {
|
||||
case ThemeId.Dark:
|
||||
return darkTheme
|
||||
case ThemeId.Sepia:
|
||||
return sepiaTheme
|
||||
case ThemeId.Apollo:
|
||||
return apolloTheme
|
||||
}
|
||||
return ThemeId.Light
|
||||
}
|
||||
|
||||
export function updateThemeLocally(themeId: string): void {
|
||||
if (typeof window !== 'undefined') {
|
||||
console.trace('storing theme: ', themeId)
|
||||
window.localStorage.setItem(themeKey, themeId)
|
||||
}
|
||||
|
||||
document.body.classList.remove(
|
||||
lighterTheme,
|
||||
...Object.keys(LEGACY_THEMES),
|
||||
sepiaTheme,
|
||||
darkTheme,
|
||||
darkerTheme,
|
||||
ThemeId.Light,
|
||||
ThemeId.Dark,
|
||||
ThemeId.Darker,
|
||||
ThemeId.Lighter,
|
||||
ThemeId.Sepia,
|
||||
ThemeId.Charcoal
|
||||
apolloTheme,
|
||||
...Object.keys(ThemeId)
|
||||
)
|
||||
document.body.classList.add(themeId)
|
||||
document.body.classList.add(getTheme(themeId))
|
||||
}
|
||||
|
||||
export function currentThemeName(): string {
|
||||
|
|
@ -42,17 +57,12 @@ export function currentThemeName(): string {
|
|||
return 'Light'
|
||||
case ThemeId.Dark:
|
||||
return 'Dark'
|
||||
case ThemeId.Darker:
|
||||
return 'Darker'
|
||||
case ThemeId.Lighter:
|
||||
return 'Lighter'
|
||||
case ThemeId.Sepia:
|
||||
return 'Sepia'
|
||||
case ThemeId.Charcoal:
|
||||
return 'Charcoal'
|
||||
default:
|
||||
return ''
|
||||
case ThemeId.Apollo:
|
||||
return 'Apollo'
|
||||
}
|
||||
return 'Light'
|
||||
}
|
||||
|
||||
export function currentTheme(): ThemeId | undefined {
|
||||
|
|
@ -60,7 +70,16 @@ export function currentTheme(): ThemeId | undefined {
|
|||
return undefined
|
||||
}
|
||||
|
||||
return window.localStorage.getItem(themeKey) as ThemeId | undefined
|
||||
const str = window.localStorage.getItem(themeKey)
|
||||
if (str && Object.values(ThemeId).includes(str as ThemeId)) {
|
||||
return str as ThemeId
|
||||
}
|
||||
|
||||
if (str && Object.keys(LEGACY_THEMES).includes(str)) {
|
||||
return LEGACY_THEMES[str] as ThemeId
|
||||
}
|
||||
|
||||
return ThemeId.Light
|
||||
}
|
||||
|
||||
export function applyStoredTheme(syncWithServer = true): ThemeId | undefined {
|
||||
|
|
@ -72,7 +91,8 @@ export function applyStoredTheme(syncWithServer = true): ThemeId | undefined {
|
|||
| ThemeId
|
||||
| undefined
|
||||
if (theme && Object.values(ThemeId).includes(theme)) {
|
||||
syncWithServer ? updateTheme(theme) : updateThemeLocally(theme)
|
||||
console.log('applying stored theme: ', theme)
|
||||
updateThemeLocally(theme)
|
||||
}
|
||||
return theme
|
||||
}
|
||||
|
|
@ -81,35 +101,3 @@ export function isDarkTheme(): boolean {
|
|||
const currentTheme = currentThemeName()
|
||||
return currentTheme === 'Dark' || currentTheme === 'Darker'
|
||||
}
|
||||
|
||||
export function darkenTheme(): void {
|
||||
switch (currentTheme()) {
|
||||
case ThemeId.Dark:
|
||||
updateTheme(ThemeId.Darker)
|
||||
break
|
||||
case ThemeId.Light:
|
||||
updateTheme(ThemeId.Dark)
|
||||
break
|
||||
case ThemeId.Lighter:
|
||||
updateTheme(ThemeId.Light)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
export function lightenTheme(): void {
|
||||
switch (currentTheme()) {
|
||||
case ThemeId.Dark:
|
||||
updateTheme(ThemeId.Light)
|
||||
break
|
||||
case ThemeId.Darker:
|
||||
updateTheme(ThemeId.Dark)
|
||||
break
|
||||
case ThemeId.Light:
|
||||
updateTheme(ThemeId.Lighter)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ const PdfArticleContainerNoSSR = dynamic<PdfArticleContainerProps>(
|
|||
export default function Home(): JSX.Element {
|
||||
const router = useRouter()
|
||||
const { cache, mutate } = useSWRConfig()
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null)
|
||||
const { slug } = router.query
|
||||
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
|
|
@ -355,12 +354,12 @@ export default function Home(): JSX.Element {
|
|||
<VStack
|
||||
alignment="center"
|
||||
distribution="start"
|
||||
ref={scrollRef}
|
||||
className="disable-webkit-callout"
|
||||
css={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: '$thBackground',
|
||||
background: '$readerMargin',
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
{article && viewerData?.me ? (
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ import {
|
|||
KBarResultsComponents,
|
||||
searchStyle,
|
||||
} from '../components/elements/KBar'
|
||||
import { darkenTheme, lightenTheme } from '../lib/themeUpdater'
|
||||
import { updateTheme } from '../lib/themeUpdater'
|
||||
import { ThemeId } from '../components/tokens/stitches.config'
|
||||
|
||||
TopBarProgress.config({
|
||||
barColors: {
|
||||
|
|
@ -52,7 +53,7 @@ const generateActions = (router: NextRouter) => {
|
|||
shortcut: ['v', 'l'],
|
||||
keywords: 'light theme',
|
||||
priority: Priority.LOW,
|
||||
perform: () => lightenTheme(),
|
||||
perform: () => updateTheme(ThemeId.Light),
|
||||
},
|
||||
{
|
||||
id: 'darkTheme',
|
||||
|
|
@ -61,7 +62,7 @@ const generateActions = (router: NextRouter) => {
|
|||
shortcut: ['v', 'd'],
|
||||
keywords: 'dark theme',
|
||||
priority: Priority.LOW,
|
||||
perform: () => darkenTheme(),
|
||||
perform: () => updateTheme(ThemeId.Dark),
|
||||
},
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -5,40 +5,6 @@ import { getCssText, globalStyles } from '../components/tokens/stitches.config'
|
|||
|
||||
export default class Document extends NextDocument {
|
||||
render() {
|
||||
const setUserPreferences = `
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let ca = document.cookie.split(';');
|
||||
for(let i = 0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function storeCookieInLocalStorage(key) {
|
||||
let value = getCookie(key);
|
||||
if (value != "") {
|
||||
window.localStorage.setItem(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
storeCookieInLocalStorage("authToken")
|
||||
storeCookieInLocalStorage("theme")
|
||||
|
||||
var themeId = window.localStorage.getItem('theme')
|
||||
|
||||
if (themeId) {
|
||||
document.body.classList.remove('theme-default', 'White', 'Gray', 'LightGray', 'Dark', 'Sepia', 'Charcoal')
|
||||
document.body.classList.add(themeId)
|
||||
}
|
||||
`
|
||||
|
||||
globalStyles()
|
||||
|
||||
return (
|
||||
|
|
@ -112,7 +78,6 @@ export default class Document extends NextDocument {
|
|||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
<script dangerouslySetInnerHTML={{ __html: setUserPreferences }} />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue