diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9363671..637648b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Reader**) Add setting to keep the screen on while reading
- (**Extension**) Support installing external JARs
- (**Reader**) Add auto background color setting
+- (**Library**) Add random sort option to library
### Changed
diff --git a/src/base/components/inputs/SortRadioInput.tsx b/src/base/components/inputs/SortRadioInput.tsx
index b61ce61b..aab02e4e 100644
--- a/src/base/components/inputs/SortRadioInput.tsx
+++ b/src/base/components/inputs/SortRadioInput.tsx
@@ -8,6 +8,7 @@
import ArrowDownward from '@mui/icons-material/ArrowDownward';
import ArrowUpward from '@mui/icons-material/ArrowUpward';
+import Replay from '@mui/icons-material/Replay';
import { memo } from 'react';
import type { RadioInputProps } from '@/base/components/inputs/RadioInput.tsx';
import { RadioInput } from '@/base/components/inputs/RadioInput.tsx';
@@ -22,3 +23,7 @@ export const SortRadioInput = memo(({ sortDescending, ...rest }: IProps) => (
{...rest}
/>
));
+
+export const SortRadioInputRandom = memo(({ sortDescending, ...rest }: IProps) => (
+ } {...rest} />
+));
diff --git a/src/features/library/Library.types.ts b/src/features/library/Library.types.ts
index 760b871d..e1935a3d 100644
--- a/src/features/library/Library.types.ts
+++ b/src/features/library/Library.types.ts
@@ -28,7 +28,8 @@ export type LibrarySortMode =
| 'dateAdded'
| 'lastRead'
| 'latestFetchedChapter'
- | 'latestUploadedChapter';
+ | 'latestUploadedChapter'
+ | 'random';
export interface LibraryOptions {
// sort options
diff --git a/src/features/library/components/LibraryOptionsPanel.tsx b/src/features/library/components/LibraryOptionsPanel.tsx
index 2480d90c..95c3f803 100644
--- a/src/features/library/components/LibraryOptionsPanel.tsx
+++ b/src/features/library/components/LibraryOptionsPanel.tsx
@@ -15,7 +15,7 @@ import { useMemo } from 'react';
import uniqBy from 'lodash/fp/uniqBy';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
import { RadioInput } from '@/base/components/inputs/RadioInput.tsx';
-import { SortRadioInput } from '@/base/components/inputs/SortRadioInput.tsx';
+import { SortRadioInput, SortRadioInputRandom } from '@/base/components/inputs/SortRadioInput.tsx';
import { ThreeStateCheckboxInput } from '@/base/components/inputs/ThreeStateCheckboxInput.tsx';
import { OptionsTabs } from '@/base/components/modals/OptionsTabs.tsx';
import { requestManager } from '@/lib/requests/RequestManager.ts';
@@ -51,6 +51,7 @@ const SORT_OPTIONS: [LibrarySortMode, MessageDescriptor][] = [
['lastRead', msg`Recently read`],
['latestFetchedChapter', msg`Latest fetched chapter`],
['latestUploadedChapter', msg`Latest uploaded chapter`],
+ ['random', msg`Random`],
];
export const LibraryOptionsPanel = ({
@@ -174,19 +175,33 @@ export const LibraryOptionsPanel = ({
);
}
if (key === 'sort') {
- return SORT_OPTIONS.map(([mode, label]) => (
-
- mode !== categoryLibraryOptions.sortBy
- ? updateCategoryLibraryOptions('sortBy', mode)
- : updateCategoryLibraryOptions('sortDesc', !categoryLibraryOptions.sortDesc)
- }
- />
- ));
+ return SORT_OPTIONS.map(([mode, label]) =>
+ mode === 'random' ? (
+
+ mode !== categoryLibraryOptions.sortBy
+ ? updateCategoryLibraryOptions('sortBy', mode)
+ : updateCategoryLibraryOptions('sortDesc', !categoryLibraryOptions.sortDesc)
+ }
+ />
+ ) : (
+
+ mode !== categoryLibraryOptions.sortBy
+ ? updateCategoryLibraryOptions('sortBy', mode)
+ : updateCategoryLibraryOptions('sortDesc', !categoryLibraryOptions.sortDesc)
+ }
+ />
+ ),
+ );
}
if (key === 'display') {
return (
diff --git a/src/features/library/hooks/useGetVisibleLibraryMangas.ts b/src/features/library/hooks/useGetVisibleLibraryMangas.ts
index be791265..1c77d007 100644
--- a/src/features/library/hooks/useGetVisibleLibraryMangas.ts
+++ b/src/features/library/hooks/useGetVisibleLibraryMangas.ts
@@ -182,6 +182,8 @@ const sortByNumber = (a: number | string = 0, b: number | string = 0) => Number(
const sortByString = (a: string, b: string): number => a.localeCompare(b, undefined, { sensitivity: 'base' });
+const sortByRandom = () => Math.floor(Math.random() * 3 - 1);
+
type TMangaSort = MangaTitleInfo &
MangaInLibraryInfo &
MangaUnreadInfo &
@@ -213,6 +215,8 @@ const sortManga = (
return (a, b) => sortByNumber(a.latestFetchedChapter?.fetchedAt, b.latestFetchedChapter?.fetchedAt);
case 'totalChapters':
return (a, b) => sortByNumber(a.chapters.totalCount, b.chapters.totalCount);
+ case 'random':
+ return () => sortByRandom();
default:
return () => 0;
}
@@ -256,14 +260,19 @@ export const useGetVisibleLibraryMangas = sortManga(mangas, options.sortBy, options.sortDesc),
+ [mangas, options.sortBy, options.sortDesc],
+ );
+
const filteredMangas = useMemo(
() =>
- filterMangas(mangas, query, {
+ filterMangas(sortedMangas, query, {
...options,
ignoreFilters: settings.ignoreFilters,
}),
[
- mangas,
+ sortedMangas,
query,
hasUnreadChapters,
hasReadChapters,
@@ -276,10 +285,6 @@ export const useGetVisibleLibraryMangas = sortManga(filteredMangas, options.sortBy, options.sortDesc),
- [filteredMangas, options.sortBy, options.sortDesc],
- );
const isATrackFilterActive = Object.values(options.hasTrackerBinding).some(
(trackFilterState) => trackFilterState != null,
@@ -299,7 +304,7 @@ export const useGetVisibleLibraryMangas = 0;
return {
- visibleMangas: sortedMangas,
+ visibleMangas: filteredMangas,
showFilteredOutMessage,
filterKey: `${JSON.stringify(options)}${settings.ignoreFilters}`,
};
diff --git a/src/i18n/locales/en.po b/src/i18n/locales/en.po
index 600708f1..eb99592b 100644
--- a/src/i18n/locales/en.po
+++ b/src/i18n/locales/en.po
@@ -2942,6 +2942,10 @@ msgstr "Queued"
msgid "Quick settings"
msgstr "Quick settings"
+#: src/features/library/components/LibraryOptionsPanel.tsx
+msgid "Random"
+msgstr "Random"
+
#: src/features/reader/settings/screens/GlobalReaderSettings.tsx
#: src/features/settings/screens/Settings.tsx
msgid "Reader"