forked from Archives/Athou_commafeed
store view mode in localStorage (#1051)
This commit is contained in:
15
commafeed-client/package-lock.json
generated
15
commafeed-client/package-lock.json
generated
@@ -39,6 +39,7 @@
|
||||
"react-swipeable": "^7.0.0",
|
||||
"swagger-ui-react": "^4.15.5",
|
||||
"tinycon": "^0.6.8",
|
||||
"use-local-storage": "^3.0.0",
|
||||
"websocket-heartbeat-js": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -10546,6 +10547,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-local-storage": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/use-local-storage/-/use-local-storage-3.0.0.tgz",
|
||||
"integrity": "sha512-wlPNnBCG3ULIJMr5A+dvWqLiPWCfsN1Kwijq+sAhT5yV4ex0u6XmZuNwP+RerIOfzBuz1pwSZuzhZMiluGQHfQ==",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
@@ -18434,6 +18443,12 @@
|
||||
"use-isomorphic-layout-effect": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"use-local-storage": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/use-local-storage/-/use-local-storage-3.0.0.tgz",
|
||||
"integrity": "sha512-wlPNnBCG3ULIJMr5A+dvWqLiPWCfsN1Kwijq+sAhT5yV4ex0u6XmZuNwP+RerIOfzBuz1pwSZuzhZMiluGQHfQ==",
|
||||
"requires": {}
|
||||
},
|
||||
"use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"react-swipeable": "^7.0.0",
|
||||
"swagger-ui-react": "^4.15.5",
|
||||
"tinycon": "^0.6.8",
|
||||
"use-local-storage": "^3.0.0",
|
||||
"websocket-heartbeat-js": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { i18n } from "@lingui/core"
|
||||
import { I18nProvider } from "@lingui/react"
|
||||
import { ColorScheme, ColorSchemeProvider, MantineProvider } from "@mantine/core"
|
||||
import { useColorScheme, useLocalStorage } from "@mantine/hooks"
|
||||
import { useColorScheme } from "@mantine/hooks"
|
||||
import useLocalStorage from "use-local-storage"
|
||||
import { ModalsProvider } from "@mantine/modals"
|
||||
import { NotificationsProvider } from "@mantine/notifications"
|
||||
import { Constants } from "app/constants"
|
||||
@@ -32,11 +33,7 @@ import Tinycon from "tinycon"
|
||||
|
||||
function Providers(props: { children: React.ReactNode }) {
|
||||
const preferredColorScheme = useColorScheme()
|
||||
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
|
||||
key: "color-scheme",
|
||||
defaultValue: preferredColorScheme,
|
||||
getInitialValueInEffect: true,
|
||||
})
|
||||
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>("color-scheme", preferredColorScheme)
|
||||
const toggleColorScheme = (value?: ColorScheme) => setColorScheme(value || (colorScheme === "dark" ? "light" : "dark"))
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@ import { showNotification } from "@mantine/notifications"
|
||||
import { createAsyncThunk, createSlice, isAnyOf } from "@reduxjs/toolkit"
|
||||
import { client } from "app/client"
|
||||
import { RootState } from "app/store"
|
||||
import { ReadingMode, ReadingOrder, Settings, SharingSettings, UserModel, ViewMode } from "app/types"
|
||||
import { ReadingMode, ReadingOrder, Settings, SharingSettings, UserModel } from "app/types"
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { reloadEntries } from "./entries"
|
||||
|
||||
@@ -36,46 +36,67 @@ export const changeReadingOrder = createAsyncThunk<void, ReadingOrder, { state:
|
||||
thunkApi.dispatch(reloadEntries())
|
||||
}
|
||||
)
|
||||
export const changeViewMode = createAsyncThunk<void, ViewMode, { state: RootState }>("settings/viewMode", (viewMode, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, viewMode })
|
||||
thunkApi.dispatch(reloadEntries())
|
||||
})
|
||||
export const changeLanguage = createAsyncThunk<void, string, { state: RootState }>("settings/language", (language, thunkApi) => {
|
||||
export const changeLanguage = createAsyncThunk<
|
||||
void,
|
||||
string,
|
||||
{
|
||||
state: RootState
|
||||
}
|
||||
>("settings/language", (language, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, language })
|
||||
})
|
||||
export const changeScrollSpeed = createAsyncThunk<void, boolean, { state: RootState }>("settings/scrollSpeed", (speed, thunkApi) => {
|
||||
export const changeScrollSpeed = createAsyncThunk<
|
||||
void,
|
||||
boolean,
|
||||
{
|
||||
state: RootState
|
||||
}
|
||||
>("settings/scrollSpeed", (speed, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, scrollSpeed: speed ? 400 : 0 })
|
||||
})
|
||||
export const changeShowRead = createAsyncThunk<void, boolean, { state: RootState }>("settings/showRead", (showRead, thunkApi) => {
|
||||
export const changeShowRead = createAsyncThunk<
|
||||
void,
|
||||
boolean,
|
||||
{
|
||||
state: RootState
|
||||
}
|
||||
>("settings/showRead", (showRead, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, showRead })
|
||||
})
|
||||
export const changeScrollMarks = createAsyncThunk<void, boolean, { state: RootState }>("settings/scrollMarks", (scrollMarks, thunkApi) => {
|
||||
export const changeScrollMarks = createAsyncThunk<
|
||||
void,
|
||||
boolean,
|
||||
{
|
||||
state: RootState
|
||||
}
|
||||
>("settings/scrollMarks", (scrollMarks, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({ ...settings, scrollMarks })
|
||||
})
|
||||
export const changeSharingSetting = createAsyncThunk<void, { site: keyof SharingSettings; value: boolean }, { state: RootState }>(
|
||||
"settings/sharingSetting",
|
||||
(sharingSetting, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({
|
||||
...settings,
|
||||
sharingSettings: {
|
||||
...settings.sharingSettings,
|
||||
[sharingSetting.site]: sharingSetting.value,
|
||||
},
|
||||
})
|
||||
export const changeSharingSetting = createAsyncThunk<
|
||||
void,
|
||||
{ site: keyof SharingSettings; value: boolean },
|
||||
{
|
||||
state: RootState
|
||||
}
|
||||
)
|
||||
>("settings/sharingSetting", (sharingSetting, thunkApi) => {
|
||||
const { settings } = thunkApi.getState().user
|
||||
if (!settings) return
|
||||
client.user.saveSettings({
|
||||
...settings,
|
||||
sharingSettings: {
|
||||
...settings.sharingSettings,
|
||||
[sharingSetting.site]: sharingSetting.value,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
export const userSlice = createSlice({
|
||||
name: "user",
|
||||
@@ -99,10 +120,6 @@ export const userSlice = createSlice({
|
||||
if (!state.settings) return
|
||||
state.settings.readingOrder = action.meta.arg
|
||||
})
|
||||
builder.addCase(changeViewMode.pending, (state, action) => {
|
||||
if (!state.settings) return
|
||||
state.settings.viewMode = action.meta.arg
|
||||
})
|
||||
builder.addCase(changeLanguage.pending, (state, action) => {
|
||||
if (!state.settings) return
|
||||
state.settings.language = action.meta.arg
|
||||
|
||||
@@ -228,7 +228,6 @@ export interface Settings {
|
||||
language: string
|
||||
readingMode: ReadingMode
|
||||
readingOrder: ReadingOrder
|
||||
viewMode: ViewMode
|
||||
showRead: boolean
|
||||
scrollMarks: boolean
|
||||
theme?: string
|
||||
|
||||
@@ -21,6 +21,7 @@ import throttle from "lodash/throttle"
|
||||
import { useEffect } from "react"
|
||||
import InfiniteScroll from "react-infinite-scroller"
|
||||
import { FeedEntry } from "./FeedEntry"
|
||||
import { useViewMode } from "../../hooks/useViewMode"
|
||||
|
||||
export function FeedEntries() {
|
||||
const source = useAppSelector(state => state.entries.source)
|
||||
@@ -28,7 +29,7 @@ export function FeedEntries() {
|
||||
const entriesTimestamp = useAppSelector(state => state.entries.timestamp)
|
||||
const selectedEntryId = useAppSelector(state => state.entries.selectedEntryId)
|
||||
const hasMore = useAppSelector(state => state.entries.hasMore)
|
||||
const viewMode = useAppSelector(state => state.user.settings?.viewMode)
|
||||
const { viewMode } = useViewMode()
|
||||
const scrollMarks = useAppSelector(state => state.user.settings?.scrollMarks)
|
||||
const scrollingToEntry = useAppSelector(state => state.entries.scrollingToEntry)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Anchor, Box, createStyles, Divider, Paper } from "@mantine/core"
|
||||
import { Constants } from "app/constants"
|
||||
import { markEntry } from "app/slices/entries"
|
||||
import { useAppDispatch, useAppSelector } from "app/store"
|
||||
import { useAppDispatch } from "app/store"
|
||||
import { Entry, ViewMode } from "app/types"
|
||||
import React from "react"
|
||||
import { useSwipeable } from "react-swipeable"
|
||||
@@ -11,6 +11,7 @@ import { FeedEntryCompactHeader } from "./FeedEntryCompactHeader"
|
||||
import { FeedEntryContextMenu, useFeedEntryContextMenu } from "./FeedEntryContextMenu"
|
||||
import { FeedEntryFooter } from "./FeedEntryFooter"
|
||||
import { FeedEntryHeader } from "./FeedEntryHeader"
|
||||
import { useViewMode } from "../../hooks/useViewMode"
|
||||
|
||||
interface FeedEntryProps {
|
||||
entry: Entry
|
||||
@@ -63,7 +64,7 @@ const useStyles = createStyles((theme, props: FeedEntryProps & { viewMode?: View
|
||||
})
|
||||
|
||||
export function FeedEntry(props: FeedEntryProps) {
|
||||
const viewMode = useAppSelector(state => state.user.settings?.viewMode)
|
||||
const { viewMode } = useViewMode()
|
||||
const { classes } = useStyles({ ...props, viewMode })
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Box, Divider, Group, Menu, SegmentedControl, SegmentedControlItem, useM
|
||||
import { showNotification } from "@mantine/notifications"
|
||||
import { client } from "app/client"
|
||||
import { redirectToAbout, redirectToAdminUsers, redirectToMetrics, redirectToSettings } from "app/slices/redirect"
|
||||
import { changeViewMode } from "app/slices/user"
|
||||
import { useAppDispatch, useAppSelector } from "app/store"
|
||||
import { ViewMode } from "app/types"
|
||||
import { useState } from "react"
|
||||
@@ -21,6 +20,7 @@ import {
|
||||
TbUsers,
|
||||
TbWorldDownload,
|
||||
} from "react-icons/tb"
|
||||
import { useViewMode } from "../../hooks/useViewMode"
|
||||
|
||||
interface ProfileMenuProps {
|
||||
control: React.ReactElement
|
||||
@@ -81,7 +81,7 @@ const viewModeData: ViewModeControlItem[] = [
|
||||
|
||||
export function ProfileMenu(props: ProfileMenuProps) {
|
||||
const [opened, setOpened] = useState(false)
|
||||
const viewMode = useAppSelector(state => state.user.settings?.viewMode)
|
||||
const { viewMode, setViewMode } = useViewMode()
|
||||
const profile = useAppSelector(state => state.user.profile)
|
||||
const admin = useAppSelector(state => state.user.profile?.admin)
|
||||
const dispatch = useAppDispatch()
|
||||
@@ -139,7 +139,7 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
||||
orientation="vertical"
|
||||
data={viewModeData}
|
||||
value={viewMode}
|
||||
onChange={e => dispatch(changeViewMode(e as ViewMode))}
|
||||
onChange={e => setViewMode(e as ViewMode)}
|
||||
mb="xs"
|
||||
/>
|
||||
|
||||
|
||||
7
commafeed-client/src/hooks/useViewMode.ts
Normal file
7
commafeed-client/src/hooks/useViewMode.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import useLocalStorage from "use-local-storage"
|
||||
import { ViewMode } from "../app/types"
|
||||
|
||||
export function useViewMode() {
|
||||
const [viewMode, setViewMode] = useLocalStorage<ViewMode>("view-mode", "detailed")
|
||||
return { viewMode, setViewMode }
|
||||
}
|
||||
Reference in New Issue
Block a user