Skip to content

Commit

Permalink
Remember comment sort by community (#1465)
Browse files Browse the repository at this point in the history
* Add remember comment sort

* Add migration

* Fallback for broken indexeddb
  • Loading branch information
aeharding authored May 9, 2024
1 parent 2fd0d2c commit 1d74518
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 60 deletions.
4 changes: 3 additions & 1 deletion src/features/comment/CommentSort.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ const BUTTONS: ActionSheetButton<CommentSortType>[] = COMMENT_SORTS.map(
);

interface CommentSortProps {
sort: CommentSortType;
sort: CommentSortType | undefined;
setSort: (sort: CommentSortType) => void;
}

export default function CommentSort({ sort, setSort }: CommentSortProps) {
const [open, setOpen] = useState(false);
const { activePageRef } = useContext(AppContext);

if (!sort) return;

return (
<>
<IonButton onClick={() => setOpen(true)}>
Expand Down
75 changes: 57 additions & 18 deletions src/features/feed/sort/feedSortSlice.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PayloadAction, createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { ListingType, SortType } from "lemmy-js-client";
import { CommentSortType, ListingType, SortType } from "lemmy-js-client";
import { db } from "../../../services/db";
import { RootState } from "../../../store";
import { getFeedUrlName } from "../../community/mod/ModActions";
Expand All @@ -8,34 +8,54 @@ interface PostSortState {
/**
* `null`: Loaded from database, but nothing there
*/
sortByFeedName: Record<string, SortType | null>;
sortByContextByFeedName: {
posts: Record<string, SortType | null>;
comments: Record<string, CommentSortType | null>;
};
}

const initialState: PostSortState = {
sortByFeedName: {},
sortByContextByFeedName: {
posts: {},
comments: {},
},
};

export type SetSortActionPayload =
| {
feed: FeedSortFeed;
sort: SortType;
context: "posts";
}
| {
feed: FeedSortFeed;
sort: CommentSortType;
context: "comments";
};

export const feedSortSlice = createSlice({
name: "feedSort",
initialState,
reducers: {
setFeedSort: (
state,
action: PayloadAction<{ feed: FeedSortFeed; sort: SortType }>,
) => {
setFeedSort: (state, action: PayloadAction<SetSortActionPayload>) => {
const feedName = serializeFeedName(action.payload.feed);
state.sortByFeedName[feedName] = action.payload.sort;
state.sortByContextByFeedName[action.payload.context][feedName] =
action.payload.sort;

db.setSetting("default_post_sort_by_feed", action.payload.sort, {
community: feedName,
});
db.setSetting(
getDefaultSortSettingForContext(action.payload.context),
action.payload.sort,
{
community: feedName,
},
);
},
},
extraReducers: (builder) => {
builder.addCase(getFeedSort.fulfilled, (state, action) => {
const { feedName, sort } = action.payload;
const { feedName, sort, context } = action.payload;

state.sortByFeedName[feedName] = sort;
state.sortByContextByFeedName[context][feedName] = sort;
});
},
});
Expand All @@ -55,23 +75,33 @@ export type FeedSortFeed =

export const getFeedSort = createAsyncThunk(
"feedSort/getFeedSort",
async (feed: FeedSortFeed) => {
async ({
feed,
context,
}: {
feed: FeedSortFeed;
context: "posts" | "comments";
}) => {
const feedName = serializeFeedName(feed);
const sort =
(await db.getSetting("default_post_sort_by_feed", {
(await db.getSetting(getDefaultSortSettingForContext(context), {
community: feedName,
})) ?? null;
})) ?? null; // null = loaded, but not found

return {
feedName,
sort,
context,
};
},
);

export const getFeedSortSelectorBuilder =
(feed: FeedSortFeed | undefined) => (state: RootState) =>
feed ? state.feedSort.sortByFeedName[serializeFeedName(feed)] : null;
(feed: FeedSortFeed | undefined, context: "posts" | "comments") =>
(state: RootState) =>
feed
? state.feedSort.sortByContextByFeedName[context][serializeFeedName(feed)]
: null;

function serializeFeedName(feed: FeedSortFeed): string {
switch (true) {
Expand All @@ -83,3 +113,12 @@ function serializeFeedName(feed: FeedSortFeed): string {
return feed;
}
}

function getDefaultSortSettingForContext(context: "posts" | "comments") {
switch (context) {
case "comments":
return "default_comment_sort_by_feed";
case "posts":
return "default_post_sort_by_feed";
}
}
47 changes: 33 additions & 14 deletions src/features/feed/sort/useFeedSort.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,68 @@
import { SortType } from "lemmy-js-client";
import { CommentSortType, SortType } from "lemmy-js-client";
import { useEffect, useState } from "react";
import { useAppDispatch, useAppSelector } from "../../../store";
import {
FeedSortFeed,
SetSortActionPayload,
getFeedSort,
getFeedSortSelectorBuilder,
setFeedSort,
} from "./feedSortSlice";

export default function useFeedSort(feed?: FeedSortFeed | undefined) {
type SortTypeByContext = {
posts: SortType;
comments: CommentSortType;
};

export default function useSortByFeed<
Context extends "posts" | "comments",
Sort extends SortTypeByContext[Context],
>(context: Context, feed?: FeedSortFeed | undefined) {
const dispatch = useAppDispatch();
const feedSort = useAppSelector(getFeedSortSelectorBuilder(feed));
const feedSort = useAppSelector(
getFeedSortSelectorBuilder(feed, context),
) as Sort;
const defaultSort = useAppSelector(
(state) => state.settings.general.posts.sort,
);
(state) => state.settings.general[context].sort,
) as Sort;
const rememberCommunitySort = useAppSelector(
(state) => state.settings.general.posts.rememberCommunitySort,
(state) => state.settings.general[context].rememberCommunitySort,
);

const [sort, _setSort] = useState<SortType | undefined>(
const [sort, _setSort] = useState<Sort | undefined>(
!rememberCommunitySort ? defaultSort : undefined,
);

useEffect(() => {
if (!rememberCommunitySort) return;
if (!feed) return;
(async () => {
if (!rememberCommunitySort) return;
if (!feed) return;

dispatch(getFeedSort(feed));
}, [feed, dispatch, rememberCommunitySort]);
try {
await dispatch(getFeedSort({ feed, context }));
} catch (error) {
_setSort((_sort) => _sort ?? defaultSort); // fallback if indexeddb unavailable
throw error;
}
})();
}, [feed, dispatch, rememberCommunitySort, context, defaultSort]);

useEffect(() => {
if (!rememberCommunitySort) return;
if (sort) return;
if (feedSort === undefined) return;
if (feedSort === undefined) return; // null = loaded, but custom community sort not found

_setSort(feedSort ?? defaultSort);
}, [feedSort, sort, defaultSort, rememberCommunitySort]);

function setSort(sort: SortType) {
function setSort(sort: Sort) {
if (rememberCommunitySort && feed) {
dispatch(
setFeedSort({
feed,
sort,
}),
context,
} as SetSortActionPayload),
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/features/settings/general/comments/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import TouchFriendlyLinks from "./TouchFriendlyLinks";
import TapToCollapse from "./TapToCollapse";
import ShowCommentImages from "./ShowCommentImages";
import ShowCollapsed from "./ShowCollapsed";
import RememberCommunityCommentSort from "./RememberCommunityCommentSort";

export default function Comments() {
return (
Expand All @@ -18,6 +19,7 @@ export default function Comments() {
</ListHeader>
<IonList inset>
<DefaultSort />
<RememberCommunityCommentSort />
<CollapsedByDefault />
<TapToCollapse />
<ShowJumpButton />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { IonItem, IonToggle } from "@ionic/react";

import { useAppDispatch, useAppSelector } from "../../../../store";
import { setRememberCommunityCommentSort } from "../../settingsSlice";

export default function RememberCommunityCommentSort() {
const dispatch = useAppDispatch();
const rememberCommunitySort = useAppSelector(
(state) => state.settings.general.comments.rememberCommunitySort,
);

return (
<IonItem>
<IonToggle
checked={rememberCommunitySort}
onIonChange={(e) =>
dispatch(setRememberCommunityCommentSort(e.detail.checked))
}
>
Remember Community Sort
</IonToggle>
</IonItem>
);
}
4 changes: 2 additions & 2 deletions src/features/settings/general/posts/Posts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ListHeader } from "../../shared/formatting";
import InfiniteScrolling from "./InfiniteScrolling";
import UpvoteOnSave from "./UpvoteOnSave";
import DefaultSort from "./DefaultSort";
import RememberCommunitySort from "./RememberCommunitySort";
import RememberCommunityPostSort from "./RememberCommunityPostSort";
import AutoplayMedia from "./AutoplayMedia";

export default function Posts() {
Expand All @@ -19,7 +19,7 @@ export default function Posts() {
</IonLabel>
</IonItem>
<DefaultSort />
<RememberCommunitySort />
<RememberCommunityPostSort />
<InfiniteScrolling />
<UpvoteOnSave />
<AutoplayMedia />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { IonItem, IonToggle } from "@ionic/react";

import { useAppDispatch, useAppSelector } from "../../../../store";
import { setRememberCommunitySort } from "../../settingsSlice";
import { setRememberCommunityPostSort } from "../../settingsSlice";

export default function RememberCommunitySort() {
export default function RememberCommunityPostSort() {
const dispatch = useAppDispatch();
const infiniteScrolling = useAppSelector(
const rememberCommunitySort = useAppSelector(
(state) => state.settings.general.posts.rememberCommunitySort,
);

return (
<IonItem>
<IonToggle
checked={infiniteScrolling}
checked={rememberCommunitySort}
onIonChange={(e) =>
dispatch(setRememberCommunitySort(e.detail.checked))
dispatch(setRememberCommunityPostSort(e.detail.checked))
}
>
Remember Community Sort
Expand Down
26 changes: 20 additions & 6 deletions src/features/settings/settingsSlice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ interface SettingsState {
touchFriendlyLinks: boolean;
showCommentImages: boolean;
showCollapsed: boolean;
rememberCommunitySort: boolean;
};
posts: {
sort: SortType;
Expand Down Expand Up @@ -206,6 +207,7 @@ export const initialState: SettingsState = {
touchFriendlyLinks: true,
showCommentImages: true,
showCollapsed: false,
rememberCommunitySort: false,
},
posts: {
sort: "Active",
Expand Down Expand Up @@ -483,10 +485,15 @@ export const appearanceSlice = createSlice({

db.setSetting("upvote_on_save", action.payload);
},
setRememberCommunitySort(state, action: PayloadAction<boolean>) {
setRememberCommunityPostSort(state, action: PayloadAction<boolean>) {
state.general.posts.rememberCommunitySort = action.payload;

db.setSetting("remember_community_sort", action.payload);
db.setSetting("remember_community_post_sort", action.payload);
},
setRememberCommunityCommentSort(state, action: PayloadAction<boolean>) {
state.general.comments.rememberCommunitySort = action.payload;

db.setSetting("remember_community_comment_sort", action.payload);
},
setAutoplayMedia(state, action: PayloadAction<AutoplayMediaType>) {
state.general.posts.autoplayMedia = action.payload;
Expand Down Expand Up @@ -673,8 +680,11 @@ export const fetchSettingsFromDatabase = createAsyncThunk<SettingsState>(
);
const infinite_scrolling = await db.getSetting("infinite_scrolling");
const upvote_on_save = await db.getSetting("upvote_on_save");
const remember_community_sort = await db.getSetting(
"remember_community_sort",
const remember_community_post_sort = await db.getSetting(
"remember_community_post_sort",
);
const remember_community_comment_sort = await db.getSetting(
"remember_community_comment_sort",
);
const autoplay_media = await db.getSetting("autoplay_media");
const enable_haptic_feedback = await db.getSetting(
Expand Down Expand Up @@ -787,6 +797,9 @@ export const fetchSettingsFromDatabase = createAsyncThunk<SettingsState>(
showCollapsed:
show_collapsed_comment ??
initialState.general.comments.showCollapsed,
rememberCommunitySort:
remember_community_comment_sort ??
initialState.general.comments.rememberCommunitySort,
},
posts: {
disableMarkingRead:
Expand All @@ -813,7 +826,7 @@ export const fetchSettingsFromDatabase = createAsyncThunk<SettingsState>(
upvote_on_save ?? initialState.general.posts.upvoteOnSave,
sort: default_post_sort ?? initialState.general.posts.sort,
rememberCommunitySort:
remember_community_sort ??
remember_community_post_sort ??
initialState.general.posts.rememberCommunitySort,
autoplayMedia:
autoplay_media ?? initialState.general.posts.autoplayMedia,
Expand Down Expand Up @@ -893,7 +906,8 @@ export const {
setDisableAutoHideInCommunities,
setInfiniteScrolling,
setUpvoteOnSave,
setRememberCommunitySort,
setRememberCommunityPostSort,
setRememberCommunityCommentSort,
setAutoplayMedia,
setTheme,
setEnableHapticFeedback,
Expand Down
Loading

0 comments on commit 1d74518

Please sign in to comment.