From e0ea81fb84ea34553181f6e4e0f118292ef8e141 Mon Sep 17 00:00:00 2001 From: Alexander Harding <2166114+aeharding@users.noreply.github.com> Date: Sat, 15 Jul 2023 18:17:26 -0500 Subject: [PATCH] Add general page, fixup race condition Co-authored-by: Berne Gerrits <31410921+Faalangst26@users.noreply.github.com> --- src/GlobalStyles.tsx | 4 +- src/TabbedRoutes.tsx | 7 + src/features/comment/CommentSort.tsx | 2 +- src/features/comment/Comments.tsx | 2 +- src/features/feed/Feed.tsx | 2 +- src/features/feed/PostCommentFeed.tsx | 2 +- src/features/post/inFeed/Post.tsx | 2 +- .../post/inFeed/compact/CompactPost.tsx | 4 +- .../post/inFeed/compact/Thumbnail.tsx | 4 +- src/features/post/inFeed/large/LargePost.tsx | 4 +- src/features/post/shared/Embed.tsx | 4 +- .../appearance/AppearanceSettings.tsx | 2 - .../settings/appearance/CompactSettings.tsx | 6 +- src/features/settings/appearance/TextSize.tsx | 4 +- .../settings/appearance/posts/BlurNsfw.tsx | 4 +- .../settings/appearance/posts/PostSize.tsx | 4 +- .../settings/appearance/system/DarkMode.tsx | 4 +- .../appearance/system/UserDarkMode.tsx | 4 +- .../settings/general/GeneralSettings.tsx | 9 + .../comments/CollapsedByDefault.tsx | 4 +- .../comments/Comments.tsx | 6 +- .../settings/general/comments/DefaultSort.tsx | 59 ++++++ .../appearanceSlice.tsx => settingsSlice.tsx} | 180 +++++++++++------- src/pages/posts/PostPage.tsx | 5 +- src/pages/settings/GeneralPage.tsx | 29 +++ src/pages/settings/SettingsPage.tsx | 7 + src/services/db.ts | 11 ++ src/store.tsx | 6 +- 28 files changed, 275 insertions(+), 106 deletions(-) create mode 100644 src/features/settings/general/GeneralSettings.tsx rename src/features/settings/{appearance => general}/comments/CollapsedByDefault.tsx (93%) rename src/features/settings/{appearance => general}/comments/Comments.tsx (62%) create mode 100644 src/features/settings/general/comments/DefaultSort.tsx rename src/features/settings/{appearance/appearanceSlice.tsx => settingsSlice.tsx} (56%) create mode 100644 src/pages/settings/GeneralPage.tsx diff --git a/src/GlobalStyles.tsx b/src/GlobalStyles.tsx index 8a5897e8e1..a7574bd1ec 100644 --- a/src/GlobalStyles.tsx +++ b/src/GlobalStyles.tsx @@ -15,7 +15,7 @@ interface GlobalStylesProps { export default function GlobalStyles({ children }: GlobalStylesProps) { const systemDarkMode = useSystemDarkMode(); const { fontSizeMultiplier, useSystemFontSize } = useAppSelector( - (state) => state.appearance.font + (state) => state.settings.appearance.font ); const baseFontStyles = useSystemFontSize @@ -27,7 +27,7 @@ export default function GlobalStyles({ children }: GlobalStylesProps) { `; const { userDarkMode, usingSystemDarkMode } = useAppSelector( - (state) => state.appearance.dark + (state) => state.settings.appearance.dark ); const isDark = usingSystemDarkMode ? systemDarkMode : userDarkMode; diff --git a/src/TabbedRoutes.tsx b/src/TabbedRoutes.tsx index 635b107868..d119864513 100644 --- a/src/TabbedRoutes.tsx +++ b/src/TabbedRoutes.tsx @@ -55,6 +55,7 @@ import { PageContextProvider } from "./features/auth/PageContext"; import { scrollUpIfNeeded } from "./helpers/scrollUpIfNeeded"; import BlocksSettingsPage from "./pages/settings/BlocksSettingsPage"; import { getDefaultServer } from "./services/app"; +import GeneralPage from "./pages/settings/GeneralPage"; const Interceptor = styled.div` position: absolute; @@ -70,6 +71,7 @@ export default function TabbedRoutes() { const totalUnread = useAppSelector(totalUnreadSelector); const { status: updateStatus } = useContext(UpdateContext); const shouldInstall = useShouldInstall(); + const ready = useAppSelector((state) => state.settings.ready); const settingsNotificationCount = (shouldInstall ? 1 : 0) + (updateStatus === "outdated" ? 1 : 0); @@ -209,6 +211,8 @@ export default function TabbedRoutes() { ]; } + if (!ready) return; + return ( {/* TODO key={} resets the tab route stack whenever your instance changes. */} @@ -321,6 +325,9 @@ export default function TabbedRoutes() { + + + diff --git a/src/features/comment/CommentSort.tsx b/src/features/comment/CommentSort.tsx index 65a648b2a8..afa0bae47a 100644 --- a/src/features/comment/CommentSort.tsx +++ b/src/features/comment/CommentSort.tsx @@ -65,7 +65,7 @@ export default function CommentSort({ sort, setSort }: CommentSortProps) { ); } -function getSortIcon(sort: CommentSortType): string { +export function getSortIcon(sort: CommentSortType): string { switch (sort) { case "Hot": return flameOutline; diff --git a/src/features/comment/Comments.tsx b/src/features/comment/Comments.tsx index 54765b9bbd..5959f257e6 100644 --- a/src/features/comment/Comments.tsx +++ b/src/features/comment/Comments.tsx @@ -30,7 +30,7 @@ import useClient from "../../helpers/useClient"; import { useSetActivePage } from "../auth/AppContext"; import { FeedContext } from "../feed/FeedContext"; import { jwtSelector } from "../auth/authSlice"; -import { defaultCommentDepthSelector } from "../settings/appearance/appearanceSlice"; +import { defaultCommentDepthSelector } from "../settings/settingsSlice"; const centerCss = css` position: relative; diff --git a/src/features/feed/Feed.tsx b/src/features/feed/Feed.tsx index 4b38486a5a..3c2b60ad57 100644 --- a/src/features/feed/Feed.tsx +++ b/src/features/feed/Feed.tsx @@ -50,7 +50,7 @@ export default function Feed({ const [atEnd, setAtEnd] = useState(false); const [present] = useIonToast(); const postAppearanceType = useAppSelector( - (state) => state.appearance.posts.type + (state) => state.settings.appearance.posts.type ); const filteredItems = useMemo( diff --git a/src/features/feed/PostCommentFeed.tsx b/src/features/feed/PostCommentFeed.tsx index e12d588dc6..8bb92b100b 100644 --- a/src/features/feed/PostCommentFeed.tsx +++ b/src/features/feed/PostCommentFeed.tsx @@ -37,7 +37,7 @@ export default function PostCommentFeed({ }: PostCommentFeed) { const dispatch = useAppDispatch(); const postAppearanceType = useAppSelector( - (state) => state.appearance.posts.type + (state) => state.settings.appearance.posts.type ); const postHiddenById = useAppSelector(postHiddenByIdSelector); diff --git a/src/features/post/inFeed/Post.tsx b/src/features/post/inFeed/Post.tsx index 76ccf8bd7a..2e968686cc 100644 --- a/src/features/post/inFeed/Post.tsx +++ b/src/features/post/inFeed/Post.tsx @@ -63,7 +63,7 @@ export default function Post(props: PostProps) { const buildGeneralBrowseLink = useBuildGeneralBrowseLink(); const postAppearanceType = useAppSelector( - (state) => state.appearance.posts.type + (state) => state.settings.appearance.posts.type ); const postBody = (() => { diff --git a/src/features/post/inFeed/compact/CompactPost.tsx b/src/features/post/inFeed/compact/CompactPost.tsx index fae537765e..f404fd8737 100644 --- a/src/features/post/inFeed/compact/CompactPost.tsx +++ b/src/features/post/inFeed/compact/CompactPost.tsx @@ -96,11 +96,11 @@ const EndDetails = styled.div` export default function CompactPost({ post, communityMode }: PostProps) { const compactThumbnailPositionType = useAppSelector( - (state) => state.appearance.compact.thumbnailsPosition + (state) => state.settings.appearance.compact.thumbnailsPosition ); const compactShowVotingButtons = useAppSelector( - (state) => state.appearance.compact.showVotingButtons + (state) => state.settings.appearance.compact.showVotingButtons ); const hasBeenRead: boolean = diff --git a/src/features/post/inFeed/compact/Thumbnail.tsx b/src/features/post/inFeed/compact/Thumbnail.tsx index 3354d2487b..c1a61f47b9 100644 --- a/src/features/post/inFeed/compact/Thumbnail.tsx +++ b/src/features/post/inFeed/compact/Thumbnail.tsx @@ -96,7 +96,9 @@ export default function Thumbnail({ post }: ImgProps) { if (markdownLoneImage) return markdownLoneImage.url; })(); - const blurNsfw = useAppSelector((state) => state.appearance.posts.blurNsfw); + const blurNsfw = useAppSelector( + (state) => state.settings.appearance.posts.blurNsfw + ); const nsfw = useMemo(() => isNsfwBlurred(post, blurNsfw), [post, blurNsfw]); diff --git a/src/features/post/inFeed/large/LargePost.tsx b/src/features/post/inFeed/large/LargePost.tsx index aea4a0d924..381bf97dc2 100644 --- a/src/features/post/inFeed/large/LargePost.tsx +++ b/src/features/post/inFeed/large/LargePost.tsx @@ -111,7 +111,9 @@ export default function LargePost({ post, communityMode }: PostProps) { () => (post.post.body ? findLoneImage(post.post.body) : undefined), [post] ); - const blurNsfw = useAppSelector((state) => state.appearance.posts.blurNsfw); + const blurNsfw = useAppSelector( + (state) => state.settings.appearance.posts.blurNsfw + ); function renderPostBody() { if (post.post.url) { diff --git a/src/features/post/shared/Embed.tsx b/src/features/post/shared/Embed.tsx index 372ff9282e..20cac2f858 100644 --- a/src/features/post/shared/Embed.tsx +++ b/src/features/post/shared/Embed.tsx @@ -80,7 +80,9 @@ export default function Embed({ post, className }: EmbedProps) { e.stopPropagation(); dispatch(setPostRead(post.post.id)); }; - const blurNsfw = useAppSelector((state) => state.appearance.posts.blurNsfw); + const blurNsfw = useAppSelector( + (state) => state.settings.appearance.posts.blurNsfw + ); return ( - diff --git a/src/features/settings/appearance/CompactSettings.tsx b/src/features/settings/appearance/CompactSettings.tsx index 65b1059387..b7f384ff76 100644 --- a/src/features/settings/appearance/CompactSettings.tsx +++ b/src/features/settings/appearance/CompactSettings.tsx @@ -11,7 +11,7 @@ import { InsetIonItem } from "../../user/Profile"; import { useAppSelector, useAppDispatch } from "../../../store"; import { useState } from "react"; import { startCase } from "lodash"; -import { setShowVotingButtons, setThumbnailPosition } from "./appearanceSlice"; +import { setShowVotingButtons, setThumbnailPosition } from "../settingsSlice"; import { OCompactThumbnailPositionType, CompactThumbnailPositionType, @@ -33,11 +33,11 @@ export default function CompactSettings() { const dispatch = useAppDispatch(); const compactThumbnailsPositionType = useAppSelector( - (state) => state.appearance.compact.thumbnailsPosition + (state) => state.settings.appearance.compact.thumbnailsPosition ); const compactShowVotingButtons = useAppSelector( - (state) => state.appearance.compact.showVotingButtons + (state) => state.settings.appearance.compact.showVotingButtons ); return ( diff --git a/src/features/settings/appearance/TextSize.tsx b/src/features/settings/appearance/TextSize.tsx index 6d12aa7a16..4fc1c0b131 100644 --- a/src/features/settings/appearance/TextSize.tsx +++ b/src/features/settings/appearance/TextSize.tsx @@ -3,7 +3,7 @@ import { css } from "@emotion/react"; import { IonLabel, IonList, IonRange, IonToggle } from "@ionic/react"; import { InsetIonItem } from "../../../pages/profile/ProfileFeedItemsPage"; import { useAppDispatch, useAppSelector } from "../../../store"; -import { setFontSizeMultiplier, setUseSystemFontSize } from "./appearanceSlice"; +import { setFontSizeMultiplier, setUseSystemFontSize } from "../settingsSlice"; export const ListHeader = styled.div` font-size: 0.8em; @@ -44,7 +44,7 @@ const MIN_LARGER_FONT_ADJUSTMENT = 2; export default function TextSize() { const dispatch = useAppDispatch(); const { fontSizeMultiplier, useSystemFontSize } = useAppSelector( - (state) => state.appearance.font + (state) => state.settings.appearance.font ); const ranges = diff --git a/src/features/settings/appearance/posts/BlurNsfw.tsx b/src/features/settings/appearance/posts/BlurNsfw.tsx index b699e71d2f..38f80e4cef 100644 --- a/src/features/settings/appearance/posts/BlurNsfw.tsx +++ b/src/features/settings/appearance/posts/BlurNsfw.tsx @@ -9,7 +9,7 @@ import { OverlayEventDetail, } from "@ionic/core"; import { OPostBlurNsfw, PostBlurNsfwType } from "../../../../services/db"; -import { setBlurNsfwState } from "../appearanceSlice"; +import { setBlurNsfwState } from "../../settingsSlice"; const BUTTONS: ActionSheetButton[] = Object.values( OPostBlurNsfw @@ -25,7 +25,7 @@ export default function BlurNsfw() { const dispatch = useAppDispatch(); const nsfwBlurred = useAppSelector( - (state) => state.appearance.posts.blurNsfw + (state) => state.settings.appearance.posts.blurNsfw ); return ( diff --git a/src/features/settings/appearance/posts/PostSize.tsx b/src/features/settings/appearance/posts/PostSize.tsx index ec4fd4d02c..21e64bee49 100644 --- a/src/features/settings/appearance/posts/PostSize.tsx +++ b/src/features/settings/appearance/posts/PostSize.tsx @@ -7,7 +7,7 @@ import { OPostAppearanceType, PostAppearanceType, setPostAppearance, -} from "../appearanceSlice"; +} from "../../settingsSlice"; import { startCase } from "lodash"; import { useState } from "react"; import { useAppDispatch, useAppSelector } from "../../../../store"; @@ -28,7 +28,7 @@ export default function PostSize() { const dispatch = useAppDispatch(); const postsAppearanceType = useAppSelector( - (state) => state.appearance.posts.type + (state) => state.settings.appearance.posts.type ); return ( diff --git a/src/features/settings/appearance/system/DarkMode.tsx b/src/features/settings/appearance/system/DarkMode.tsx index a1783882ce..6c35ee8ca3 100644 --- a/src/features/settings/appearance/system/DarkMode.tsx +++ b/src/features/settings/appearance/system/DarkMode.tsx @@ -1,13 +1,13 @@ import { IonLabel, IonList, IonToggle } from "@ionic/react"; import { InsetIonItem } from "../../../../pages/profile/ProfileFeedItemsPage"; import { useAppDispatch, useAppSelector } from "../../../../store"; -import { setUseSystemDarkMode } from "../appearanceSlice"; +import { setUseSystemDarkMode } from "../../settingsSlice"; import UserDarkMode from "./UserDarkMode"; export default function DarkMode() { const dispatch = useAppDispatch(); const { usingSystemDarkMode } = useAppSelector( - (state) => state.appearance.dark + (state) => state.settings.appearance.dark ); return ( diff --git a/src/features/settings/appearance/system/UserDarkMode.tsx b/src/features/settings/appearance/system/UserDarkMode.tsx index aa1b92d50c..4723fcd814 100644 --- a/src/features/settings/appearance/system/UserDarkMode.tsx +++ b/src/features/settings/appearance/system/UserDarkMode.tsx @@ -2,7 +2,7 @@ import styled from "@emotion/styled"; import { IonLabel, IonList, IonRadio, IonRadioGroup } from "@ionic/react"; import { InsetIonItem } from "../../../../pages/profile/ProfileFeedItemsPage"; import { useAppDispatch, useAppSelector } from "../../../../store"; -import { setUserDarkMode } from "../appearanceSlice"; +import { setUserDarkMode } from "../../settingsSlice"; const ListHeader = styled.div` font-size: 0.8em; @@ -14,7 +14,7 @@ const ListHeader = styled.div` export default function UserDarkMode() { const dispatch = useAppDispatch(); const userDarkMode = useAppSelector( - (state) => state.appearance.dark.userDarkMode + (state) => state.settings.appearance.dark.userDarkMode ); return ( diff --git a/src/features/settings/general/GeneralSettings.tsx b/src/features/settings/general/GeneralSettings.tsx new file mode 100644 index 0000000000..4875d05c40 --- /dev/null +++ b/src/features/settings/general/GeneralSettings.tsx @@ -0,0 +1,9 @@ +import Comments from "./comments/Comments"; + +export default function GeneralSettings() { + return ( + <> + + + ); +} diff --git a/src/features/settings/appearance/comments/CollapsedByDefault.tsx b/src/features/settings/general/comments/CollapsedByDefault.tsx similarity index 93% rename from src/features/settings/appearance/comments/CollapsedByDefault.tsx rename to src/features/settings/general/comments/CollapsedByDefault.tsx index 2d4af65bed..17a2bf243b 100644 --- a/src/features/settings/appearance/comments/CollapsedByDefault.tsx +++ b/src/features/settings/general/comments/CollapsedByDefault.tsx @@ -5,7 +5,7 @@ import { useAppDispatch, useAppSelector } from "../../../../store"; import { OCommentThreadCollapse, setCommentsCollapsed, -} from "../appearanceSlice"; +} from "../../settingsSlice"; export const ListHeader = styled.div` font-size: 0.8em; @@ -18,7 +18,7 @@ export default function CollapsedByDefault() { const dispatch = useAppDispatch(); const { collapseCommentThreads } = useAppSelector( // this needs a better naming - (state) => state.appearance.comments + (state) => state.settings.general.comments ); return ( diff --git a/src/features/settings/appearance/comments/Comments.tsx b/src/features/settings/general/comments/Comments.tsx similarity index 62% rename from src/features/settings/appearance/comments/Comments.tsx rename to src/features/settings/general/comments/Comments.tsx index cf0f55e987..22f8cd03ba 100644 --- a/src/features/settings/appearance/comments/Comments.tsx +++ b/src/features/settings/general/comments/Comments.tsx @@ -1,5 +1,8 @@ import { IonLabel, IonList } from "@ionic/react"; -import CollapsedByDefault, { ListHeader } from "./CollapsedByDefault"; +import CollapsedByDefault, { + ListHeader, +} from "../../general/comments/CollapsedByDefault"; +import DefaultSort from "./DefaultSort"; export default function Comments() { return ( @@ -9,6 +12,7 @@ export default function Comments() { + ); diff --git a/src/features/settings/general/comments/DefaultSort.tsx b/src/features/settings/general/comments/DefaultSort.tsx new file mode 100644 index 0000000000..7bef4d1e22 --- /dev/null +++ b/src/features/settings/general/comments/DefaultSort.tsx @@ -0,0 +1,59 @@ +import { ActionSheetButton, IonActionSheet, IonLabel } from "@ionic/react"; +import { startCase } from "lodash"; +import { InsetIonItem } from "../../../user/Profile"; +import { useAppDispatch, useAppSelector } from "../../../../store"; +import { useState } from "react"; +import { + CommentDefaultSort, + OCommentDefaultSort, +} from "../../../../services/db"; +import { IonActionSheetCustomEvent, OverlayEventDetail } from "@ionic/core"; +import { setDefaultCommentSort } from "../../settingsSlice"; +import { getSortIcon } from "../../../comment/CommentSort"; + +const BUTTONS: ActionSheetButton[] = Object.values( + OCommentDefaultSort +).map(function (commentSort) { + return { + text: startCase(commentSort), + data: commentSort, + icon: getSortIcon(commentSort), + } as ActionSheetButton; +}); + +export default function DefaultSort() { + const [open, setOpen] = useState(false); + + const dispatch = useAppDispatch(); + const postsAppearanceType = useAppSelector( + (state) => state.settings.general.comments.sort + ); + + return ( + <> + setOpen(true)}> + Default Sort + + {startCase(postsAppearanceType)} + + setOpen(false)} + onWillDismiss={( + e: IonActionSheetCustomEvent> + ) => { + if (e.detail.data) { + dispatch(setDefaultCommentSort(e.detail.data)); + } + }} + header="Default Comments Sort..." + buttons={BUTTONS.map((b) => ({ + ...b, + role: postsAppearanceType === b.data ? "selected" : undefined, + }))} + /> + + + ); +} diff --git a/src/features/settings/appearance/appearanceSlice.tsx b/src/features/settings/settingsSlice.tsx similarity index 56% rename from src/features/settings/appearance/appearanceSlice.tsx rename to src/features/settings/settingsSlice.tsx index c18c2f876d..9c207c1cd7 100644 --- a/src/features/settings/appearance/appearanceSlice.tsx +++ b/src/features/settings/settingsSlice.tsx @@ -5,8 +5,8 @@ import { createSlice, } from "@reduxjs/toolkit"; import { merge } from "lodash"; -import { AppDispatch, RootState } from "../../../store"; -import { MAX_DEFAULT_COMMENT_DEPTH } from "../../../helpers/lemmy"; +import { AppDispatch, RootState } from "../../store"; +import { MAX_DEFAULT_COMMENT_DEPTH } from "../../helpers/lemmy"; import { CommentThreadCollapse, OCommentThreadCollapse, @@ -17,8 +17,10 @@ import { CompactThumbnailPositionType, db, OPostBlurNsfw, -} from "../../../services/db"; -import { get, set } from "../storage"; + CommentDefaultSort, + OCommentDefaultSort, +} from "../../services/db"; +import { get, set } from "./storage"; export { type CommentThreadCollapse, @@ -27,27 +29,33 @@ export { OCommentThreadCollapse, OPostAppearanceType, OCompactThumbnailPositionType, -} from "../../../services/db"; +} from "../../services/db"; -interface AppearanceState { - font: { - fontSizeMultiplier: number; - useSystemFontSize: boolean; - }; - comments: { - collapseCommentThreads: CommentThreadCollapse; - }; - posts: { - blurNsfw: PostBlurNsfwType; - type: PostAppearanceType; +interface SettingsState { + ready: boolean; + appearance: { + font: { + fontSizeMultiplier: number; + useSystemFontSize: boolean; + }; + posts: { + blurNsfw: PostBlurNsfwType; + type: PostAppearanceType; + }; + compact: { + thumbnailsPosition: CompactThumbnailPositionType; + showVotingButtons: boolean; + }; + dark: { + usingSystemDarkMode: boolean; + userDarkMode: boolean; + }; }; - compact: { - thumbnailsPosition: CompactThumbnailPositionType; - showVotingButtons: boolean; - }; - dark: { - usingSystemDarkMode: boolean; - userDarkMode: boolean; + general: { + comments: { + collapseCommentThreads: CommentThreadCollapse; + sort: CommentDefaultSort; + }; }; } @@ -62,31 +70,37 @@ const LOCALSTORAGE_KEYS = { }, } as const; -const initialState: AppearanceState = { - font: { - fontSizeMultiplier: 1, - useSystemFontSize: false, - }, - comments: { - collapseCommentThreads: OCommentThreadCollapse.Never, - }, - posts: { - blurNsfw: OPostBlurNsfw.InFeed, - type: OPostAppearanceType.Large, - }, - compact: { - thumbnailsPosition: OCompactThumbnailPositionType.Left, - showVotingButtons: true, +const initialState: SettingsState = { + ready: false, + appearance: { + font: { + fontSizeMultiplier: 1, + useSystemFontSize: false, + }, + posts: { + blurNsfw: OPostBlurNsfw.InFeed, + type: OPostAppearanceType.Large, + }, + compact: { + thumbnailsPosition: OCompactThumbnailPositionType.Left, + showVotingButtons: true, + }, + dark: { + usingSystemDarkMode: true, + userDarkMode: false, + }, }, - dark: { - usingSystemDarkMode: true, - userDarkMode: false, + general: { + comments: { + collapseCommentThreads: OCommentThreadCollapse.Never, + sort: OCommentDefaultSort.Hot, + }, }, }; // We continue using localstorage for specific items because indexeddb is slow // and we don't want to wait for it to load before rendering the app and cause flickering -const stateWithLocalstorageItems: AppearanceState = merge(initialState, { +const stateWithLocalstorageItems: SettingsState = merge(initialState, { font: { fontSizeMultiplier: get(LOCALSTORAGE_KEYS.FONT.FONT_SIZE_MULTIPLIER), useSystemFontSize: get(LOCALSTORAGE_KEYS.FONT.USE_SYSTEM), @@ -98,7 +112,10 @@ const stateWithLocalstorageItems: AppearanceState = merge(initialState, { }); export const defaultCommentDepthSelector = createSelector( - [(state: RootState) => state.appearance.comments.collapseCommentThreads], + [ + (state: RootState) => + state.settings.general.comments.collapseCommentThreads, + ], (collapseCommentThreads): number => { switch (collapseCommentThreads) { case OCommentThreadCollapse.Always: @@ -115,35 +132,37 @@ export const appearanceSlice = createSlice({ extraReducers: (builder) => { builder.addCase( fetchSettingsFromDatabase.fulfilled, - (_, action: PayloadAction) => action.payload + (_, action: PayloadAction) => action.payload ); }, reducers: { setFontSizeMultiplier(state, action: PayloadAction) { - state.font.fontSizeMultiplier = action.payload; + state.appearance.font.fontSizeMultiplier = action.payload; set(LOCALSTORAGE_KEYS.FONT.FONT_SIZE_MULTIPLIER, action.payload); }, setUseSystemFontSize(state, action: PayloadAction) { - state.font.useSystemFontSize = action.payload; + state.appearance.font.useSystemFontSize = action.payload; set(LOCALSTORAGE_KEYS.FONT.USE_SYSTEM, action.payload); }, setCommentsCollapsed(state, action: PayloadAction) { - state.comments.collapseCommentThreads = action.payload; + state.general.comments.collapseCommentThreads = action.payload; db.setSetting("collapse_comment_threads", action.payload); }, setPostAppearance(state, action: PayloadAction) { - state.posts.type = action.payload; + state.appearance.posts.type = action.payload; db.setSetting("post_appearance_type", action.payload); }, setNsfwBlur(state, action: PayloadAction) { - state.posts.blurNsfw = action.payload; + state.appearance.posts.blurNsfw = action.payload; + + // Per user setting is updated in StoreProvider }, setShowVotingButtons(state, action: PayloadAction) { - state.compact.showVotingButtons = action.payload; + state.appearance.compact.showVotingButtons = action.payload; db.setSetting("compact_show_voting_buttons", action.payload); }, @@ -151,22 +170,30 @@ export const appearanceSlice = createSlice({ state, action: PayloadAction ) { - state.compact.thumbnailsPosition = action.payload; + state.appearance.compact.thumbnailsPosition = action.payload; db.setSetting("compact_thumbnail_position_type", action.payload); }, setUserDarkMode(state, action: PayloadAction) { - state.dark.userDarkMode = action.payload; + state.appearance.dark.userDarkMode = action.payload; set(LOCALSTORAGE_KEYS.DARK.USER_MODE, action.payload); }, setUseSystemDarkMode(state, action: PayloadAction) { - state.dark.usingSystemDarkMode = action.payload; + state.appearance.dark.usingSystemDarkMode = action.payload; set(LOCALSTORAGE_KEYS.DARK.USE_SYSTEM, action.payload); }, + setDefaultCommentSort(state, action: PayloadAction) { + state.general.comments.sort = action.payload; + + db.setSetting("default_comment_sort", action.payload); + }, - resetAppearance: () => initialState, + resetSettings: () => ({ + ...initialState, + ready: true, + }), }, }); @@ -190,10 +217,10 @@ export const getBlurNsfw = user_handle: userHandle, }); - dispatch(setNsfwBlur(blurNsfw ?? initialState.posts.blurNsfw)); + dispatch(setNsfwBlur(blurNsfw ?? initialState.appearance.posts.blurNsfw)); }; -export const fetchSettingsFromDatabase = createAsyncThunk( +export const fetchSettingsFromDatabase = createAsyncThunk( "appearance/fetchSettingsFromDatabase", async (_, thunkApi) => { return db.transaction("r", db.settings, async () => { @@ -209,25 +236,33 @@ export const fetchSettingsFromDatabase = createAsyncThunk( const compact_show_voting_buttons = await db.getSetting( "compact_show_voting_buttons" ); + const default_comment_sort = await db.getSetting("default_comment_sort"); return { - ...state.appearance, - comments: { - collapseCommentThreads: - collapse_comment_threads ?? - initialState.comments.collapseCommentThreads, - }, - posts: { - type: post_appearance_type ?? initialState.posts.type, - blurNsfw: blur_nsfw ?? initialState.posts.blurNsfw, + ...state.settings, + ready: true, + appearance: { + ...state.settings.appearance, + posts: { + type: post_appearance_type ?? initialState.appearance.posts.type, + blurNsfw: blur_nsfw ?? initialState.appearance.posts.blurNsfw, + }, + compact: { + thumbnailsPosition: + compact_thumbnail_position_type ?? + initialState.appearance.compact.thumbnailsPosition, + showVotingButtons: + compact_show_voting_buttons ?? + initialState.appearance.compact.showVotingButtons, + }, }, - compact: { - thumbnailsPosition: - compact_thumbnail_position_type ?? - initialState.compact.thumbnailsPosition, - showVotingButtons: - compact_show_voting_buttons ?? - initialState.compact.showVotingButtons, + general: { + comments: { + collapseCommentThreads: + collapse_comment_threads ?? + initialState.general.comments.collapseCommentThreads, + sort: default_comment_sort ?? initialState.general.comments.sort, + }, }, }; }); @@ -244,6 +279,7 @@ export const { setShowVotingButtons, setUserDarkMode, setUseSystemDarkMode, + setDefaultCommentSort, } = appearanceSlice.actions; export default appearanceSlice.reducer; diff --git a/src/pages/posts/PostPage.tsx b/src/pages/posts/PostPage.tsx index 4c572459ef..94816e9a6b 100644 --- a/src/pages/posts/PostPage.tsx +++ b/src/pages/posts/PostPage.tsx @@ -48,7 +48,10 @@ export default function PostPage() { const post = useAppSelector((state) => state.post.postById[id]); const jwt = useAppSelector(jwtSelector); const dispatch = useAppDispatch(); - const [sort, setSort] = useState("Hot"); + const defaultSort = useAppSelector( + (state) => state.settings.general.comments.sort + ); + const [sort, setSort] = useState(defaultSort); const postIfFound = typeof post === "object" ? post : undefined; diff --git a/src/pages/settings/GeneralPage.tsx b/src/pages/settings/GeneralPage.tsx new file mode 100644 index 0000000000..41301c2e92 --- /dev/null +++ b/src/pages/settings/GeneralPage.tsx @@ -0,0 +1,29 @@ +import { + IonBackButton, + IonButtons, + IonHeader, + IonPage, + IonTitle, + IonToolbar, +} from "@ionic/react"; +import AppContent from "../../features/shared/AppContent"; +import GeneralSettings from "../../features/settings/general/GeneralSettings"; + +export default function GeneralPage() { + return ( + + + + + + + + General + + + + + + + ); +} diff --git a/src/pages/settings/SettingsPage.tsx b/src/pages/settings/SettingsPage.tsx index 5115110e1d..57598e7254 100644 --- a/src/pages/settings/SettingsPage.tsx +++ b/src/pages/settings/SettingsPage.tsx @@ -12,6 +12,7 @@ import { InsetIonItem, SettingLabel } from "../../features/user/Profile"; import { apps, bagCheckOutline, + cog, colorPalette, logoGithub, mailOutline, @@ -94,6 +95,12 @@ export default function SettingsPage() { + + + + + General + diff --git a/src/services/db.ts b/src/services/db.ts index e93db3861b..3fa9ca3987 100644 --- a/src/services/db.ts +++ b/src/services/db.ts @@ -1,4 +1,5 @@ import Dexie, { Table } from "dexie"; +import { CommentSortType } from "lemmy-js-client"; export interface IPostMetadata { post_id: number; @@ -36,6 +37,15 @@ export const OPostBlurNsfw = { Never: "never", } as const; +export const OCommentDefaultSort: Record = { + Hot: "Hot", + Top: "Top", + New: "New", + Old: "Old", +} as const; + +export type CommentDefaultSort = CommentSortType; + export type PostBlurNsfwType = (typeof OPostBlurNsfw)[keyof typeof OPostBlurNsfw]; @@ -46,6 +56,7 @@ export type SettingValueTypes = { compact_show_voting_buttons: boolean; blur_nsfw: PostBlurNsfwType; favorite_communities: string[]; + default_comment_sort: CommentDefaultSort; }; export interface ISettingItem { diff --git a/src/store.tsx b/src/store.tsx index 75cd3c8f31..f16b184b5f 100644 --- a/src/store.tsx +++ b/src/store.tsx @@ -14,10 +14,10 @@ import communitySlice, { } from "./features/community/communitySlice"; import userSlice from "./features/user/userSlice"; import inboxSlice from "./features/inbox/inboxSlice"; -import appearanceSlice, { +import settingsSlice, { fetchSettingsFromDatabase, getBlurNsfw, -} from "./features/settings/appearance/appearanceSlice"; +} from "./features/settings/settingsSlice"; const store = configureStore({ reducer: { @@ -27,7 +27,7 @@ const store = configureStore({ community: communitySlice, user: userSlice, inbox: inboxSlice, - appearance: appearanceSlice, + settings: settingsSlice, }, }); export type RootState = ReturnType;