forked from Archives/Athou_commafeed
move other settings to localSettings too
This commit is contained in:
10
commafeed-client/package-lock.json
generated
10
commafeed-client/package-lock.json
generated
@@ -44,7 +44,6 @@
|
|||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tinycon": "^0.6.8",
|
"tinycon": "^0.6.8",
|
||||||
"tss-react": "^4.9.13",
|
"tss-react": "^4.9.13",
|
||||||
"use-local-storage": "^3.0.0",
|
|
||||||
"vite-plugin-biome": "^1.0.12",
|
"vite-plugin-biome": "^1.0.12",
|
||||||
"websocket-heartbeat-js": "^1.1.3"
|
"websocket-heartbeat-js": "^1.1.3"
|
||||||
},
|
},
|
||||||
@@ -8380,15 +8379,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=16.8.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/use-sidecar": {
|
"node_modules/use-sidecar": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz",
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tinycon": "^0.6.8",
|
"tinycon": "^0.6.8",
|
||||||
"tss-react": "^4.9.13",
|
"tss-react": "^4.9.13",
|
||||||
"use-local-storage": "^3.0.0",
|
|
||||||
"vite-plugin-biome": "^1.0.12",
|
"vite-plugin-biome": "^1.0.12",
|
||||||
"websocket-heartbeat-js": "^1.1.3"
|
"websocket-heartbeat-js": "^1.1.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { entriesSlice } from "app/entries/slice"
|
|||||||
import { redirectSlice } from "app/redirect/slice"
|
import { redirectSlice } from "app/redirect/slice"
|
||||||
import { serverSlice } from "app/server/slice"
|
import { serverSlice } from "app/server/slice"
|
||||||
import { treeSlice } from "app/tree/slice"
|
import { treeSlice } from "app/tree/slice"
|
||||||
import type { ViewMode } from "app/types"
|
import { initialLocalSettings, userSlice } from "app/user/slice"
|
||||||
import { userSlice } from "app/user/slice"
|
|
||||||
import { type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
|
import { type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
|
||||||
|
|
||||||
export const reducers = {
|
export const reducers = {
|
||||||
@@ -19,15 +18,13 @@ export const store = configureStore({
|
|||||||
reducer: reducers,
|
reducer: reducers,
|
||||||
preloadedState: {
|
preloadedState: {
|
||||||
user: {
|
user: {
|
||||||
localSettings: {
|
localSettings: JSON.parse(localStorage.getItem("commafeed-local-settings") ?? JSON.stringify(initialLocalSettings)),
|
||||||
viewMode: localStorage.getItem("view-mode") as ViewMode,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
store.subscribe(() => {
|
store.subscribe(() => {
|
||||||
const state = store.getState()
|
const localSettings = store.getState().user.localSettings
|
||||||
localStorage.setItem("view-mode", state.user.localSettings.viewMode)
|
localStorage.setItem("commafeed-local-settings", JSON.stringify(localSettings))
|
||||||
})
|
})
|
||||||
|
|
||||||
export type RootState = ReturnType<typeof store.getState>
|
export type RootState = ReturnType<typeof store.getState>
|
||||||
|
|||||||
@@ -254,6 +254,12 @@ export interface Settings {
|
|||||||
sharingSettings: SharingSettings
|
sharingSettings: SharingSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LocalSettings {
|
||||||
|
viewMode: ViewMode
|
||||||
|
sidebarWidth: number
|
||||||
|
announcementHash: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface StarRequest {
|
export interface StarRequest {
|
||||||
id: string
|
id: string
|
||||||
feedId: number
|
feedId: number
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { t } from "@lingui/macro"
|
||||||
import { showNotification } from "@mantine/notifications"
|
import { showNotification } from "@mantine/notifications"
|
||||||
import { type PayloadAction, createSlice, isAnyOf } from "@reduxjs/toolkit"
|
import { type PayloadAction, createSlice, isAnyOf } from "@reduxjs/toolkit"
|
||||||
import type { Settings, UserModel, ViewMode } from "app/types"
|
import type { LocalSettings, Settings, UserModel, ViewMode } from "app/types"
|
||||||
import {
|
import {
|
||||||
changeCustomContextMenu,
|
changeCustomContextMenu,
|
||||||
changeEntriesToKeepOnTopWhenScrolling,
|
changeEntriesToKeepOnTopWhenScrolling,
|
||||||
@@ -26,17 +26,19 @@ import {
|
|||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
settings?: Settings
|
settings?: Settings
|
||||||
localSettings: {
|
localSettings: LocalSettings
|
||||||
viewMode: ViewMode
|
|
||||||
}
|
|
||||||
profile?: UserModel
|
profile?: UserModel
|
||||||
tags?: string[]
|
tags?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const initialLocalSettings: LocalSettings = {
|
||||||
|
viewMode: "detailed",
|
||||||
|
sidebarWidth: 360,
|
||||||
|
announcementHash: "no-hash",
|
||||||
|
}
|
||||||
|
|
||||||
const initialState: UserState = {
|
const initialState: UserState = {
|
||||||
localSettings: {
|
localSettings: initialLocalSettings,
|
||||||
viewMode: "detailed",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const userSlice = createSlice({
|
export const userSlice = createSlice({
|
||||||
@@ -46,6 +48,12 @@ export const userSlice = createSlice({
|
|||||||
setViewMode: (state, action: PayloadAction<ViewMode>) => {
|
setViewMode: (state, action: PayloadAction<ViewMode>) => {
|
||||||
state.localSettings.viewMode = action.payload
|
state.localSettings.viewMode = action.payload
|
||||||
},
|
},
|
||||||
|
setSidebarWidth: (state, action: PayloadAction<number>) => {
|
||||||
|
state.localSettings.sidebarWidth = action.payload
|
||||||
|
},
|
||||||
|
setAnnouncementHash: (state, action: PayloadAction<string>) => {
|
||||||
|
state.localSettings.announcementHash = action.payload
|
||||||
|
},
|
||||||
},
|
},
|
||||||
extraReducers: builder => {
|
extraReducers: builder => {
|
||||||
builder.addCase(reloadSettings.fulfilled, (state, action) => {
|
builder.addCase(reloadSettings.fulfilled, (state, action) => {
|
||||||
@@ -148,4 +156,4 @@ export const userSlice = createSlice({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { setViewMode } = userSlice.actions
|
export const { setViewMode, setSidebarWidth, setAnnouncementHash } = userSlice.actions
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Box, Dialog, Text } from "@mantine/core"
|
import { Box, Dialog, Text } from "@mantine/core"
|
||||||
import { useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
|
import { setAnnouncementHash } from "app/user/slice"
|
||||||
import { Content } from "components/content/Content"
|
import { Content } from "components/content/Content"
|
||||||
import { useAsync } from "react-async-hook"
|
import { useAsync } from "react-async-hook"
|
||||||
import useLocalStorage from "use-local-storage"
|
|
||||||
|
|
||||||
const sha256Hex = async (input: string | undefined) => {
|
const sha256Hex = async (input: string | undefined) => {
|
||||||
const data = new TextEncoder().encode(input)
|
const data = new TextEncoder().encode(input)
|
||||||
@@ -15,10 +15,11 @@ const sha256Hex = async (input: string | undefined) => {
|
|||||||
export function AnnouncementDialog() {
|
export function AnnouncementDialog() {
|
||||||
const announcement = useAppSelector(state => state.server.serverInfos?.announcement)
|
const announcement = useAppSelector(state => state.server.serverInfos?.announcement)
|
||||||
const announcementHash = useAsync(sha256Hex, [announcement]).result
|
const announcementHash = useAsync(sha256Hex, [announcement]).result
|
||||||
const [localStorageHash, setLocalStorageHash] = useLocalStorage("announcement-hash", "no-hash")
|
const existingAnnouncementHash = useAppSelector(state => state.user.localSettings.announcementHash)
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const opened = !!announcementHash && announcementHash !== localStorageHash
|
const opened = !!announcementHash && announcementHash !== existingAnnouncementHash
|
||||||
const onClosed = () => setLocalStorageHash(announcementHash)
|
const onClosed = () => announcementHash && dispatch(setAnnouncementHash(announcementHash))
|
||||||
|
|
||||||
if (!announcement) return null
|
if (!announcement) return null
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { redirectToAdd, redirectToRootCategory } from "app/redirect/thunks"
|
|||||||
import { useAppDispatch, useAppSelector } from "app/store"
|
import { useAppDispatch, useAppSelector } from "app/store"
|
||||||
import { setMobileMenuOpen } from "app/tree/slice"
|
import { setMobileMenuOpen } from "app/tree/slice"
|
||||||
import { reloadTree } from "app/tree/thunks"
|
import { reloadTree } from "app/tree/thunks"
|
||||||
|
import { setSidebarWidth } from "app/user/slice"
|
||||||
import { reloadProfile, reloadSettings, reloadTags } from "app/user/thunks"
|
import { reloadProfile, reloadSettings, reloadTags } from "app/user/thunks"
|
||||||
import { ActionButton } from "components/ActionButton"
|
import { ActionButton } from "components/ActionButton"
|
||||||
import { AnnouncementDialog } from "components/AnnouncementDialog"
|
import { AnnouncementDialog } from "components/AnnouncementDialog"
|
||||||
@@ -23,7 +24,6 @@ import { TbMenu2, TbPlus, TbX } from "react-icons/tb"
|
|||||||
import { Outlet } from "react-router-dom"
|
import { Outlet } from "react-router-dom"
|
||||||
import { useSwipeable } from "react-swipeable"
|
import { useSwipeable } from "react-swipeable"
|
||||||
import { tss } from "tss"
|
import { tss } from "tss"
|
||||||
import useLocalStorage from "use-local-storage"
|
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
sidebar: ReactNode
|
sidebar: ReactNode
|
||||||
@@ -64,21 +64,23 @@ export default function Layout(props: LayoutProps) {
|
|||||||
const theme = useMantineTheme()
|
const theme = useMantineTheme()
|
||||||
const mobile = useMobile()
|
const mobile = useMobile()
|
||||||
const { isBrowserExtensionPopup } = useBrowserExtension()
|
const { isBrowserExtensionPopup } = useBrowserExtension()
|
||||||
const [sidebarWidth, setSidebarWidth] = useLocalStorage("sidebar-width", 350)
|
|
||||||
|
const { loading } = useAppLoading()
|
||||||
|
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
||||||
|
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
|
||||||
|
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
|
||||||
|
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
|
||||||
|
const sidebarWidth = useAppSelector(state => state.user.localSettings.sidebarWidth)
|
||||||
|
const headerInFooter = mobile && !isBrowserExtensionPopup && mobileFooter
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
useWebSocket()
|
||||||
|
|
||||||
const sidebarPadding = theme.spacing.xs
|
const sidebarPadding = theme.spacing.xs
|
||||||
const { classes } = useStyles({
|
const { classes } = useStyles({
|
||||||
sidebarWidth,
|
sidebarWidth,
|
||||||
sidebarPadding,
|
sidebarPadding,
|
||||||
sidebarRightBorderWidth: "1px",
|
sidebarRightBorderWidth: "1px",
|
||||||
})
|
})
|
||||||
const { loading } = useAppLoading()
|
|
||||||
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
|
|
||||||
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
|
|
||||||
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
|
|
||||||
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
|
|
||||||
const headerInFooter = mobile && !isBrowserExtensionPopup && mobileFooter
|
|
||||||
const dispatch = useAppDispatch()
|
|
||||||
useWebSocket()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// load initial data
|
// load initial data
|
||||||
@@ -192,7 +194,10 @@ export default function Layout(props: LayoutProps) {
|
|||||||
right: 1000,
|
right: 1000,
|
||||||
}}
|
}}
|
||||||
grid={[30, 30]}
|
grid={[30, 30]}
|
||||||
onDrag={(_e, data) => setSidebarWidth(data.x)}
|
onDrag={(_e, data) => {
|
||||||
|
dispatch(setSidebarWidth(data.x))
|
||||||
|
return
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
Reference in New Issue
Block a user