From 23efda667d032b9f93d2482a57e9195276adfa1b Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Thu, 9 Mar 2023 16:44:45 +0800 Subject: [PATCH] Theme cleanup, expose extra reader themes This also removes synced reader preferences so everything is per device now. --- packages/web/components/elements/LogoBox.tsx | 2 +- .../web/components/patterns/DropdownMenu.tsx | 4 +- .../components/templates/PrimaryDropdown.tsx | 6 +- .../components/templates/PrimaryLayout.tsx | 3 + .../templates/article/ArticleContainer.tsx | 24 +++-- .../article/ReaderSettingsControl.tsx | 75 ++++++++++++-- .../templates/homeFeed/HomeFeedContainer.tsx | 3 - .../templates/reader/ReaderHeader.tsx | 5 +- .../web/components/tokens/stitches.config.ts | 44 ++++----- packages/web/lib/hooks/useReaderSettings.tsx | 25 ++--- .../mutations/userPersonalizationMutation.ts | 61 ------------ .../queries/useGetUserPreferences.tsx | 88 ----------------- packages/web/lib/themeUpdater.tsx | 98 ++++++++----------- .../web/pages/[username]/[slug]/index.tsx | 5 +- packages/web/pages/_app.tsx | 7 +- packages/web/pages/_document.tsx | 35 ------- 16 files changed, 172 insertions(+), 313 deletions(-) delete mode 100644 packages/web/lib/networking/mutations/userPersonalizationMutation.ts delete mode 100644 packages/web/lib/networking/queries/useGetUserPreferences.tsx diff --git a/packages/web/components/elements/LogoBox.tsx b/packages/web/components/elements/LogoBox.tsx index a0afa173d..94d45e895 100644 --- a/packages/web/components/elements/LogoBox.tsx +++ b/packages/web/components/elements/LogoBox.tsx @@ -32,7 +32,7 @@ export function LogoBox(): JSX.Element { }, }} > - + ) diff --git a/packages/web/components/patterns/DropdownMenu.tsx b/packages/web/components/patterns/DropdownMenu.tsx index efa46dfcc..7f6ffe6eb 100644 --- a/packages/web/components/patterns/DropdownMenu.tsx +++ b/packages/web/components/patterns/DropdownMenu.tsx @@ -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()) }} > diff --git a/packages/web/components/templates/PrimaryDropdown.tsx b/packages/web/components/templates/PrimaryDropdown.tsx index 3cfa6e774..b4cda53c8 100644 --- a/packages/web/components/templates/PrimaryDropdown.tsx +++ b/packages/web/components/templates/PrimaryDropdown.tsx @@ -234,7 +234,7 @@ function ThemeSection(props: PrimaryDropdownProps): JSX.Element { }} > { updateTheme(ThemeId.Light) }} @@ -243,9 +243,9 @@ function ThemeSection(props: PrimaryDropdownProps): JSX.Element { { - updateTheme(ThemeId.Darker) + updateTheme(ThemeId.Dark) }} > Dark diff --git a/packages/web/components/templates/PrimaryLayout.tsx b/packages/web/components/templates/PrimaryLayout.tsx index 790dd7b2c..f0764cb84 100644 --- a/packages/web/components/templates/PrimaryLayout.tsx +++ b/packages/web/components/templates/PrimaryLayout.tsx @@ -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) diff --git a/packages/web/components/templates/article/ArticleContainer.tsx b/packages/web/components/templates/article/ArticleContainer.tsx index 35263c034..22412dc11 100644 --- a/packages/web/components/templates/article/ArticleContainer.tsx +++ b/packages/web/components/templates/article/ArticleContainer.tsx @@ -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 { @@ -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} diff --git a/packages/web/components/templates/article/ReaderSettingsControl.tsx b/packages/web/components/templates/article/ReaderSettingsControl.tsx index 8670503ff..0484c47e9 100644 --- a/packages/web/components/templates/article/ReaderSettingsControl.tsx +++ b/packages/web/components/templates/article/ReaderSettingsControl.tsx @@ -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 ( { updateTheme(ThemeId.Light) setCurrentTheme(currentThemeName()) }} > - {!isDark && } + {currentTheme == ThemeId.Light && ( + + )} + + diff --git a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx index 480c6fcdd..60bc3863b 100644 --- a/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx +++ b/packages/web/components/templates/homeFeed/HomeFeedContainer.tsx @@ -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 })) diff --git a/packages/web/components/templates/reader/ReaderHeader.tsx b/packages/web/components/templates/reader/ReaderHeader.tsx index e3345535f..c4884c1d3 100644 --- a/packages/web/components/templates/reader/ReaderHeader.tsx +++ b/packages/web/components/templates/reader/ReaderHeader.tsx @@ -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', + }, }} > { - 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({ // key: `--display-hide-margins`, @@ -207,7 +203,6 @@ export const useReaderSettings = (): ReaderSettings => { ) return { - preferencesData, fontSize, lineHeight, marginWidth, diff --git a/packages/web/lib/networking/mutations/userPersonalizationMutation.ts b/packages/web/lib/networking/mutations/userPersonalizationMutation.ts deleted file mode 100644 index 5a07ade0b..000000000 --- a/packages/web/lib/networking/mutations/userPersonalizationMutation.ts +++ /dev/null @@ -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 { - 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 - } -} diff --git a/packages/web/lib/networking/queries/useGetUserPreferences.tsx b/packages/web/lib/networking/queries/useGetUserPreferences.tsx deleted file mode 100644 index 9b74a795b..000000000 --- a/packages/web/lib/networking/queries/useGetUserPreferences.tsx +++ /dev/null @@ -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, - } -} diff --git a/packages/web/lib/themeUpdater.tsx b/packages/web/lib/themeUpdater.tsx index 24668b03b..1b4694449 100644 --- a/packages/web/lib/themeUpdater.tsx +++ b/packages/web/lib/themeUpdater.tsx @@ -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 - } -} diff --git a/packages/web/pages/[username]/[slug]/index.tsx b/packages/web/pages/[username]/[slug]/index.tsx index af9c9b364..0b61ef31c 100644 --- a/packages/web/pages/[username]/[slug]/index.tsx +++ b/packages/web/pages/[username]/[slug]/index.tsx @@ -47,7 +47,6 @@ const PdfArticleContainerNoSSR = dynamic( export default function Home(): JSX.Element { const router = useRouter() const { cache, mutate } = useSWRConfig() - const scrollRef = useRef(null) const { slug } = router.query const [showEditModal, setShowEditModal] = useState(false) @@ -355,12 +354,12 @@ export default function Home(): JSX.Element { {article && viewerData?.me ? ( diff --git a/packages/web/pages/_app.tsx b/packages/web/pages/_app.tsx index ad224caca..ea31fe24a 100644 --- a/packages/web/pages/_app.tsx +++ b/packages/web/pages/_app.tsx @@ -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), }, ] diff --git a/packages/web/pages/_document.tsx b/packages/web/pages/_document.tsx index e5c96fe8b..f63ebc224 100644 --- a/packages/web/pages/_document.tsx +++ b/packages/web/pages/_document.tsx @@ -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 {
-