From 9d796108a566289d7762c134de906b06dd7186e3 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 16:49:39 +0200
Subject: [PATCH 1/9] setup sources reordering
---
package.json | 3 +
pnpm-lock.yaml | 103 ++++++++++++++-----
src/assets/locales/en.json | 2 +
src/components/form/SortableList.tsx | 89 ++++++++++++++++
src/hooks/useProviderScrape.tsx | 5 +
src/hooks/useSettingsState.ts | 16 ++-
src/pages/Settings.tsx | 15 +++
src/pages/parts/settings/PreferencesPart.tsx | 17 +++
src/stores/preferences/index.tsx | 13 ++-
9 files changed, 235 insertions(+), 28 deletions(-)
create mode 100644 src/components/form/SortableList.tsx
diff --git a/package.json b/package.json
index b9482526..ad6a855b 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,9 @@
]
},
"dependencies": {
+ "@dnd-kit/core": "^6.1.0",
+ "@dnd-kit/sortable": "^8.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
"@formkit/auto-animate": "^0.8.1",
"@headlessui/react": "^1.7.17",
"@ladjs/country-language": "^1.0.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 54dd87b7..0bca4afe 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,6 +12,15 @@ overrides:
rollup: npm:@rollup/wasm-node
dependencies:
+ '@dnd-kit/core':
+ specifier: ^6.1.0
+ version: 6.1.0(react-dom@18.2.0)(react@18.2.0)
+ '@dnd-kit/sortable':
+ specifier: ^8.0.0
+ version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0)
+ '@dnd-kit/utilities':
+ specifier: ^3.2.2
+ version: 3.2.2(react@18.2.0)
'@formkit/auto-animate':
specifier: ^0.8.1
version: 0.8.1
@@ -274,7 +283,7 @@ devDependencies:
version: 0.5.9(prettier@3.1.1)
rollup-plugin-visualizer:
specifier: ^5.11.0
- version: 5.11.0(@rollup/wasm-node@4.14.2)
+ version: 5.11.0(@rollup/wasm-node@4.14.3)
tailwind-scrollbar:
specifier: ^3.0.5
version: 3.0.5(tailwindcss@3.4.0)
@@ -2688,6 +2697,49 @@ packages:
to-fast-properties: 2.0.0
dev: true
+ /@dnd-kit/accessibility@3.1.0(react@18.2.0):
+ resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==}
+ peerDependencies:
+ react: '>=16.8.0'
+ dependencies:
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
+ /@dnd-kit/core@6.1.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@dnd-kit/accessibility': 3.1.0(react@18.2.0)
+ '@dnd-kit/utilities': 3.2.2(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ tslib: 2.6.2
+ dev: false
+
+ /@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0):
+ resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
+ peerDependencies:
+ '@dnd-kit/core': ^6.1.0
+ react: '>=16.8.0'
+ dependencies:
+ '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0)
+ '@dnd-kit/utilities': 3.2.2(react@18.2.0)
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
+ /@dnd-kit/utilities@3.2.2(react@18.2.0):
+ resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
+ peerDependencies:
+ react: '>=16.8.0'
+ dependencies:
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
/@esbuild/aix-ppc64@0.19.10:
resolution: {integrity: sha512-Q+mk96KJ+FZ30h9fsJl+67IjNJm3x2eX+GBWGmocAKgzp27cowCOOqSdscX80s0SpdFXZnIv/+1xD1EctFx96Q==}
engines: {node: '>=12'}
@@ -3175,7 +3227,7 @@ packages:
engines: {node: '>=14.0.0'}
dev: false
- /@rollup/plugin-babel@5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.2):
+ /@rollup/plugin-babel@5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.3):
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
engines: {node: '>= 10.0.0'}
peerDependencies:
@@ -3188,36 +3240,36 @@ packages:
dependencies:
'@babel/core': 7.24.3
'@babel/helper-module-imports': 7.24.3
- '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.2)
- rollup: /@rollup/wasm-node@4.14.2
+ '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.3)
+ rollup: /@rollup/wasm-node@4.14.3
dev: true
- /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.14.2):
+ /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.14.3):
resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
engines: {node: '>= 10.0.0'}
peerDependencies:
rollup: npm:@rollup/wasm-node
dependencies:
- '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.2)
+ '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.3)
'@types/resolve': 1.17.1
builtin-modules: 3.3.0
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.8
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
dev: true
- /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.14.2):
+ /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.14.3):
resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
peerDependencies:
rollup: npm:@rollup/wasm-node
dependencies:
- '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.2)
+ '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.14.3)
magic-string: 0.25.9
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
dev: true
- /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.14.2):
+ /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.14.3):
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
peerDependencies:
@@ -3226,11 +3278,11 @@ packages:
'@types/estree': 0.0.39
estree-walker: 1.0.1
picomatch: 2.3.1
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
dev: true
- /@rollup/wasm-node@4.14.2:
- resolution: {integrity: sha512-iwZbxtvP/0icwPWExUZWfA3A2jqQkDY38E8R5onRY2ALFmom0k7e37n9WDcJMMRcx/pdenfN8NaSohzX9LiDEQ==}
+ /@rollup/wasm-node@4.14.3:
+ resolution: {integrity: sha512-UyFUQV/iAu/Wt6rY6uQMYBQlfTMsynzYVIz6i7s9ySwjoG9WDNgtkK1TrazCSrUFbmuPZi2gbJm6VWdJCVw2yA==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
dependencies:
@@ -6557,7 +6609,7 @@ packages:
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6)
'@babel/types': 7.23.6
kleur: 4.1.5
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
unplugin: 1.5.1
transitivePeerDependencies:
- supports-color
@@ -7529,7 +7581,7 @@ packages:
glob: 7.2.3
dev: true
- /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.14.2):
+ /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.14.3):
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
peerDependencies:
@@ -7537,12 +7589,12 @@ packages:
dependencies:
'@babel/code-frame': 7.24.2
jest-worker: 26.6.2
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
serialize-javascript: 4.0.0
terser: 5.30.0
dev: true
- /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.14.2):
+ /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.14.3):
resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==}
engines: {node: '>=14'}
hasBin: true
@@ -7554,7 +7606,7 @@ packages:
dependencies:
open: 8.4.2
picomatch: 2.3.1
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
source-map: 0.7.4
yargs: 17.7.2
dev: true
@@ -8692,7 +8744,7 @@ packages:
'@types/node': 20.10.5
esbuild: 0.19.10
postcss: 8.4.32
- rollup: /@rollup/wasm-node@4.14.2
+ rollup: /@rollup/wasm-node@4.14.3
optionalDependencies:
fsevents: 2.3.3
dev: true
@@ -8959,9 +9011,9 @@ packages:
'@babel/core': 7.24.3
'@babel/preset-env': 7.24.3(@babel/core@7.24.3)
'@babel/runtime': 7.24.1
- '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.2)
- '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.14.2)
- '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.14.2)
+ '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.3)(@rollup/wasm-node@4.14.3)
+ '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.14.3)
+ '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.14.3)
'@surma/rollup-plugin-off-main-thread': 2.2.3
ajv: 8.12.0
common-tags: 1.8.2
@@ -8970,8 +9022,8 @@ packages:
glob: 7.2.3
lodash: 4.17.21
pretty-bytes: 5.6.0
- rollup: /@rollup/wasm-node@4.14.2
- rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.14.2)
+ rollup: /@rollup/wasm-node@4.14.3
+ rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.14.3)
source-map: 0.8.0-beta.0
stringify-object: 3.3.0
strip-comments: 2.0.1
@@ -9016,6 +9068,7 @@ packages:
/workbox-google-analytics@7.0.0:
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
+ deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
dependencies:
workbox-background-sync: 7.0.0
workbox-core: 7.0.0
diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json
index 6b951af6..acab2c56 100644
--- a/src/assets/locales/en.json
+++ b/src/assets/locales/en.json
@@ -527,6 +527,8 @@
"autoplay": "Autoplay",
"autoplayDescription": "Automatically play the next episode in a series after reaching the end. Can be enabled by users with the browser extension, a custom proxy, or with the default setup if allowed by the host.",
"autoplayLabel": "Autoplay",
+ "sourceOrder": "Reordering sources",
+ "sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch.",
"title": "Preferences"
},
"reset": "Reset",
diff --git a/src/components/form/SortableList.tsx b/src/components/form/SortableList.tsx
new file mode 100644
index 00000000..90c8fd94
--- /dev/null
+++ b/src/components/form/SortableList.tsx
@@ -0,0 +1,89 @@
+import {
+ DndContext,
+ DragEndEvent,
+ KeyboardSensor,
+ PointerSensor,
+ closestCenter,
+ useSensor,
+ useSensors,
+} from "@dnd-kit/core";
+import {
+ SortableContext,
+ arrayMove,
+ sortableKeyboardCoordinates,
+ useSortable,
+ verticalListSortingStrategy,
+} from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import classNames from "classnames";
+
+import { Icon, Icons } from "../Icon";
+
+function SortableItem(props: { id: string }) {
+ const { attributes, listeners, setNodeRef, transform, transition } =
+ useSortable({ id: props.id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ };
+
+ return (
+
+ {props.id}
+
+
+ );
+}
+
+export function DraggableList(props: {
+ items: string[];
+ setItems: (items: string[]) => void;
+}) {
+ const sensors = useSensors(
+ useSensor(PointerSensor),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ }),
+ );
+
+ const handleDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+ if (!over) return;
+ if (active.id !== over.id) {
+ const currentItems = props.items;
+ const oldIndex = currentItems.indexOf(active.id as string);
+ const newIndex = currentItems.indexOf(over.id as string);
+ const newItems = arrayMove(currentItems, oldIndex, newIndex);
+ props.setItems(newItems);
+ }
+ };
+
+ return (
+
+
+
+ {props.items.map((id) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/hooks/useProviderScrape.tsx b/src/hooks/useProviderScrape.tsx
index 932c8449..e83973e2 100644
--- a/src/hooks/useProviderScrape.tsx
+++ b/src/hooks/useProviderScrape.tsx
@@ -14,6 +14,7 @@ import {
} from "@/backend/helpers/providerApi";
import { getLoadbalancedProviderApiUrl } from "@/backend/providers/fetchers";
import { getProviders } from "@/backend/providers/providers";
+import { usePreferencesStore } from "@/stores/preferences";
export interface ScrapingItems {
id: string;
@@ -156,6 +157,8 @@ export function useScrape() {
startScrape,
} = useBaseScrape();
+ const preferredSourceOrder = usePreferencesStore((s) => s.sourceOrder);
+
const startScraping = useCallback(
async (media: ScrapeMedia) => {
const providerApiUrl = getLoadbalancedProviderApiUrl();
@@ -181,6 +184,7 @@ export function useScrape() {
const providers = getProviders();
const output = await providers.runAll({
media,
+ sourceOrder: preferredSourceOrder,
events: {
init: initEvent,
start: startEvent,
@@ -199,6 +203,7 @@ export function useScrape() {
discoverEmbedsEvent,
getResult,
startScrape,
+ preferredSourceOrder,
],
);
diff --git a/src/hooks/useSettingsState.ts b/src/hooks/useSettingsState.ts
index eb8cd73f..667093fb 100644
--- a/src/hooks/useSettingsState.ts
+++ b/src/hooks/useSettingsState.ts
@@ -52,6 +52,7 @@ export function useSettingsState(
| undefined,
enableThumbnails: boolean,
enableAutoplay: boolean,
+ sourceOrder: string[],
) {
const [proxyUrlsState, setProxyUrls, resetProxyUrls, proxyUrlsChanged] =
useDerived(proxyUrls);
@@ -91,6 +92,12 @@ export function useSettingsState(
resetEnableAutoplay,
enableAutoplayChanged,
] = useDerived(enableAutoplay);
+ const [
+ sourceOrderState,
+ setSourceOrderState,
+ resetSourceOrder,
+ sourceOrderChanged,
+ ] = useDerived(sourceOrder);
function reset() {
resetTheme();
@@ -103,6 +110,7 @@ export function useSettingsState(
resetProfile();
resetEnableThumbnails();
resetEnableAutoplay();
+ resetSourceOrder();
}
const changed =
@@ -114,7 +122,8 @@ export function useSettingsState(
proxyUrlsChanged ||
profileChanged ||
enableThumbnailsChanged ||
- enableAutoplayChanged;
+ enableAutoplayChanged ||
+ sourceOrderChanged;
return {
reset,
@@ -164,5 +173,10 @@ export function useSettingsState(
set: setEnableAutoplayState,
changed: enableAutoplayChanged,
},
+ sourceOrder: {
+ state: sourceOrderState,
+ set: setSourceOrderState,
+ changed: sourceOrderChanged,
+ },
};
}
diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx
index 997fa3ed..720d55ea 100644
--- a/src/pages/Settings.tsx
+++ b/src/pages/Settings.tsx
@@ -11,6 +11,7 @@ import {
import { getSessions, updateSession } from "@/backend/accounts/sessions";
import { updateSettings } from "@/backend/accounts/settings";
import { editUser } from "@/backend/accounts/user";
+import { getProviders } from "@/backend/providers/providers";
import { Button } from "@/components/buttons/Button";
import { WideContainer } from "@/components/layout/WideContainer";
import { UserIcons } from "@/components/UserIcon";
@@ -125,6 +126,9 @@ export function SettingsPage() {
const enableAutoplay = usePreferencesStore((s) => s.enableAutoplay);
const setEnableAutoplay = usePreferencesStore((s) => s.setEnableAutoplay);
+ const sourceOrder = usePreferencesStore((s) => s.sourceOrder);
+ const setSourceOrder = usePreferencesStore((s) => s.setSourceOrder);
+
const account = useAuthStore((s) => s.account);
const updateProfile = useAuthStore((s) => s.setAccountProfile);
const updateDeviceName = useAuthStore((s) => s.updateDeviceName);
@@ -148,6 +152,7 @@ export function SettingsPage() {
account?.profile,
enableThumbnails,
enableAutoplay,
+ sourceOrder,
);
useEffect(() => {
@@ -201,6 +206,7 @@ export function SettingsPage() {
setEnableThumbnails(state.enableThumbnails.state);
setEnableAutoplay(state.enableAutoplay.state);
+ setSourceOrder(state.sourceOrder.state);
setAppLanguage(state.appLanguage.state);
setTheme(state.theme.state);
setSubStyling(state.subtitleStyling.state);
@@ -227,6 +233,7 @@ export function SettingsPage() {
setEnableThumbnails,
state,
setEnableAutoplay,
+ setSourceOrder,
setAppLanguage,
setTheme,
setSubStyling,
@@ -274,6 +281,14 @@ export function SettingsPage() {
setEnableThumbnails={state.enableThumbnails.set}
enableAutoplay={state.enableAutoplay.state}
setEnableAutoplay={state.enableAutoplay.set}
+ sourceOrder={
+ state.sourceOrder.state.length > 0
+ ? state.sourceOrder.state
+ : getProviders()
+ .listSources()
+ .map((s) => s.id)
+ }
+ setSourceOrder={state.sourceOrder.set}
/>
diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx
index 71f9c5f8..2dab46b9 100644
--- a/src/pages/parts/settings/PreferencesPart.tsx
+++ b/src/pages/parts/settings/PreferencesPart.tsx
@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { Toggle } from "@/components/buttons/Toggle";
import { FlagIcon } from "@/components/FlagIcon";
import { Dropdown } from "@/components/form/Dropdown";
+import { DraggableList } from "@/components/form/SortableList";
import { Heading1 } from "@/components/utils/Text";
import { appLanguageOptions } from "@/setup/i18n";
import { isAutoplayAllowed } from "@/utils/autoplay";
@@ -16,6 +17,8 @@ export function PreferencesPart(props: {
setEnableThumbnails: (v: boolean) => void;
enableAutoplay: boolean;
setEnableAutoplay: (v: boolean) => void;
+ sourceOrder: string[];
+ setSourceOrder: (v: string[]) => void;
}) {
const { t } = useTranslation();
const sorted = sortLangCodes(appLanguageOptions.map((item) => item.code));
@@ -94,6 +97,20 @@ export function PreferencesPart(props: {
+
+
+
+ {t("settings.preferences.sourceOrder")}
+
+
+ {t("settings.preferences.sourceOrderDescription")}
+
+
+
+
);
}
diff --git a/src/stores/preferences/index.tsx b/src/stores/preferences/index.tsx
index ce198388..cc421573 100644
--- a/src/stores/preferences/index.tsx
+++ b/src/stores/preferences/index.tsx
@@ -4,26 +4,35 @@ import { immer } from "zustand/middleware/immer";
export interface PreferencesStore {
enableThumbnails: boolean;
- setEnableThumbnails(v: boolean): void;
enableAutoplay: boolean;
+ sourceOrder: string[];
+
+ setEnableThumbnails(v: boolean): void;
setEnableAutoplay(v: boolean): void;
+ setSourceOrder(v: string[]): void;
}
export const usePreferencesStore = create(
persist(
immer((set) => ({
enableThumbnails: false,
+ enableAutoplay: false,
+ sourceOrder: [],
setEnableThumbnails(v) {
set((s) => {
s.enableThumbnails = v;
});
},
- enableAutoplay: false,
setEnableAutoplay(v) {
set((s) => {
s.enableAutoplay = v;
});
},
+ setSourceOrder(v) {
+ set((s) => {
+ s.sourceOrder = v;
+ });
+ },
})),
{
name: "__MW::preferences",
From df89bda66a28d1063f24874726bd7d7c52b4f82e Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 16:52:50 +0200
Subject: [PATCH 2/9] rename
---
src/components/form/SortableList.tsx | 2 +-
src/pages/parts/settings/PreferencesPart.tsx | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/components/form/SortableList.tsx b/src/components/form/SortableList.tsx
index 90c8fd94..27975bd1 100644
--- a/src/components/form/SortableList.tsx
+++ b/src/components/form/SortableList.tsx
@@ -45,7 +45,7 @@ function SortableItem(props: { id: string }) {
);
}
-export function DraggableList(props: {
+export function SortableList(props: {
items: string[];
setItems: (items: string[]) => void;
}) {
diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx
index 2dab46b9..1afef469 100644
--- a/src/pages/parts/settings/PreferencesPart.tsx
+++ b/src/pages/parts/settings/PreferencesPart.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { Toggle } from "@/components/buttons/Toggle";
import { FlagIcon } from "@/components/FlagIcon";
import { Dropdown } from "@/components/form/Dropdown";
-import { DraggableList } from "@/components/form/SortableList";
+import { SortableList } from "@/components/form/SortableList";
import { Heading1 } from "@/components/utils/Text";
import { appLanguageOptions } from "@/setup/i18n";
import { isAutoplayAllowed } from "@/utils/autoplay";
@@ -106,7 +106,7 @@ export function PreferencesPart(props: {
{t("settings.preferences.sourceOrderDescription")}
-
From 42d7be106e0a35e7a576d84ec8b5ba575a1b17f9 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 17:15:15 +0200
Subject: [PATCH 3/9] use name of source instead
---
src/components/form/SortableList.tsx | 23 ++++++++++++--------
src/pages/parts/settings/PreferencesPart.tsx | 14 ++++++++++--
2 files changed, 26 insertions(+), 11 deletions(-)
diff --git a/src/components/form/SortableList.tsx b/src/components/form/SortableList.tsx
index 27975bd1..bdbb0fdd 100644
--- a/src/components/form/SortableList.tsx
+++ b/src/components/form/SortableList.tsx
@@ -19,9 +19,14 @@ import classNames from "classnames";
import { Icon, Icons } from "../Icon";
-function SortableItem(props: { id: string }) {
+export interface Item {
+ id: string;
+ name: string;
+}
+
+function SortableItem(props: { item: Item }) {
const { attributes, listeners, setNodeRef, transform, transition } =
- useSortable({ id: props.id });
+ useSortable({ id: props.item.id });
const style = {
transform: CSS.Transform.toString(transform),
@@ -39,15 +44,15 @@ function SortableItem(props: { id: string }) {
transform && "cursor-grabbing",
)}
>
- {props.id}
+ {props.item.name}
);
}
export function SortableList(props: {
- items: string[];
- setItems: (items: string[]) => void;
+ items: Item[];
+ setItems: (items: Item[]) => void;
}) {
const sensors = useSensors(
useSensor(PointerSensor),
@@ -61,8 +66,8 @@ export function SortableList(props: {
if (!over) return;
if (active.id !== over.id) {
const currentItems = props.items;
- const oldIndex = currentItems.indexOf(active.id as string);
- const newIndex = currentItems.indexOf(over.id as string);
+ const oldIndex = currentItems.findIndex((item) => item.id === active.id);
+ const newIndex = currentItems.findIndex((item) => item.id === over.id);
const newItems = arrayMove(currentItems, oldIndex, newIndex);
props.setItems(newItems);
}
@@ -79,8 +84,8 @@ export function SortableList(props: {
strategy={verticalListSortingStrategy}
>
- {props.items.map((id) => (
-
+ {props.items.map((item) => (
+
))}
diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx
index 1afef469..de943325 100644
--- a/src/pages/parts/settings/PreferencesPart.tsx
+++ b/src/pages/parts/settings/PreferencesPart.tsx
@@ -1,6 +1,8 @@
import classNames from "classnames";
import { useTranslation } from "react-i18next";
+import { getCachedMetadata } from "@/backend/helpers/providerApi";
+import { getProviders } from "@/backend/providers/providers";
import { Toggle } from "@/components/buttons/Toggle";
import { FlagIcon } from "@/components/FlagIcon";
import { Dropdown } from "@/components/form/Dropdown";
@@ -107,8 +109,16 @@ export function PreferencesPart(props: {
({
+ id,
+ name:
+ getProviders()
+ .listSources()
+ .find((s) => s.id === id)?.name || id,
+ }))}
+ setItems={(items) =>
+ props.setSourceOrder(items.map((item) => item.id))
+ }
/>
From 427646b444e4d8242fd66833585370938d2fa819 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 17:29:30 +0200
Subject: [PATCH 4/9] fix dragging not working on mobile devices
---
src/components/form/SortableList.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/form/SortableList.tsx b/src/components/form/SortableList.tsx
index bdbb0fdd..b139b8e4 100644
--- a/src/components/form/SortableList.tsx
+++ b/src/components/form/SortableList.tsx
@@ -40,7 +40,7 @@ function SortableItem(props: { item: Item }) {
{...attributes}
{...listeners}
className={classNames(
- "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg",
+ "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg touch-none",
transform && "cursor-grabbing",
)}
>
From d607a7cce1345c195f22b1508ba2e350caf05637 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 18:20:12 +0200
Subject: [PATCH 5/9] remove unused import
---
src/pages/parts/settings/PreferencesPart.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx
index de943325..4057f47c 100644
--- a/src/pages/parts/settings/PreferencesPart.tsx
+++ b/src/pages/parts/settings/PreferencesPart.tsx
@@ -1,7 +1,6 @@
import classNames from "classnames";
import { useTranslation } from "react-i18next";
-import { getCachedMetadata } from "@/backend/helpers/providerApi";
import { getProviders } from "@/backend/providers/providers";
import { Toggle } from "@/components/buttons/Toggle";
import { FlagIcon } from "@/components/FlagIcon";
From b671f228634bf8ab13170425aef892ab62b8b4b8 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 22:46:38 +0200
Subject: [PATCH 6/9] grey out sources that are not available on your current
device
---
src/assets/locales/en.json | 2 +-
src/backend/providers/providers.ts | 8 ++++++
src/components/form/SortableList.tsx | 7 +++--
src/pages/Settings.tsx | 26 +++++++++++------
src/pages/parts/settings/PreferencesPart.tsx | 30 ++++++++++++++------
5 files changed, 54 insertions(+), 19 deletions(-)
diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json
index acab2c56..b5976dd5 100644
--- a/src/assets/locales/en.json
+++ b/src/assets/locales/en.json
@@ -528,7 +528,7 @@
"autoplayDescription": "Automatically play the next episode in a series after reaching the end. Can be enabled by users with the browser extension, a custom proxy, or with the default setup if allowed by the host.",
"autoplayLabel": "Autoplay",
"sourceOrder": "Reordering sources",
- "sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch.",
+ "sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch. If a source is greyed out, it means it is not available on your device.",
"title": "Preferences"
},
"reset": "Reset",
diff --git a/src/backend/providers/providers.ts b/src/backend/providers/providers.ts
index ac4a7dfa..fba9b4d5 100644
--- a/src/backend/providers/providers.ts
+++ b/src/backend/providers/providers.ts
@@ -25,3 +25,11 @@ export function getProviders() {
target: targets.BROWSER,
});
}
+
+export function getAllProviders() {
+ return makeProviders({
+ fetcher: makeStandardFetcher(fetch),
+ target: targets.BROWSER_EXTENSION,
+ consistentIpForRequests: true,
+ });
+}
diff --git a/src/components/form/SortableList.tsx b/src/components/form/SortableList.tsx
index b139b8e4..43da96fa 100644
--- a/src/components/form/SortableList.tsx
+++ b/src/components/form/SortableList.tsx
@@ -22,6 +22,7 @@ import { Icon, Icons } from "../Icon";
export interface Item {
id: string;
name: string;
+ disabled?: boolean;
}
function SortableItem(props: { item: Item }) {
@@ -40,11 +41,13 @@ function SortableItem(props: { item: Item }) {
{...attributes}
{...listeners}
className={classNames(
- "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg touch-none",
- transform && "cursor-grabbing",
+ "bg-dropdown-background hover:bg-dropdown-hoverBackground select-none space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg touch-none",
+ props.item.disabled && "opacity-50",
+ transform ? "cursor-grabbing" : "cursor-grab",
)}
>
{props.item.name}
+ {props.item.disabled && }
);
diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx
index 720d55ea..03aea1db 100644
--- a/src/pages/Settings.tsx
+++ b/src/pages/Settings.tsx
@@ -11,7 +11,7 @@ import {
import { getSessions, updateSession } from "@/backend/accounts/sessions";
import { updateSettings } from "@/backend/accounts/settings";
import { editUser } from "@/backend/accounts/user";
-import { getProviders } from "@/backend/providers/providers";
+import { getAllProviders, getProviders } from "@/backend/providers/providers";
import { Button } from "@/components/buttons/Button";
import { WideContainer } from "@/components/layout/WideContainer";
import { UserIcons } from "@/components/UserIcon";
@@ -155,6 +155,22 @@ export function SettingsPage() {
sourceOrder,
);
+ const availableSources = useMemo(() => {
+ const sources = getAllProviders().listSources();
+ const sourceIDs = sources.map((s) => s.id);
+ const stateSources = state.sourceOrder.state;
+
+ // Filter out sources that are not in `stateSources` and are in `sources`
+ const updatedSources = stateSources.filter((ss) => sourceIDs.includes(ss));
+
+ // Add sources from `sources` that are not in `stateSources`
+ const missingSources = sources
+ .filter((s) => !stateSources.includes(s.id))
+ .map((s) => s.id);
+
+ return [...updatedSources, ...missingSources];
+ }, [state.sourceOrder.state]);
+
useEffect(() => {
setPreviewTheme(activeTheme ?? "default");
}, [setPreviewTheme, activeTheme]);
@@ -281,13 +297,7 @@ export function SettingsPage() {
setEnableThumbnails={state.enableThumbnails.set}
enableAutoplay={state.enableAutoplay.state}
setEnableAutoplay={state.enableAutoplay.set}
- sourceOrder={
- state.sourceOrder.state.length > 0
- ? state.sourceOrder.state
- : getProviders()
- .listSources()
- .map((s) => s.id)
- }
+ sourceOrder={availableSources}
setSourceOrder={state.sourceOrder.set}
/>
diff --git a/src/pages/parts/settings/PreferencesPart.tsx b/src/pages/parts/settings/PreferencesPart.tsx
index 4057f47c..d127655f 100644
--- a/src/pages/parts/settings/PreferencesPart.tsx
+++ b/src/pages/parts/settings/PreferencesPart.tsx
@@ -1,7 +1,9 @@
import classNames from "classnames";
+import { useMemo } from "react";
import { useTranslation } from "react-i18next";
-import { getProviders } from "@/backend/providers/providers";
+import { getAllProviders, getProviders } from "@/backend/providers/providers";
+import { Button } from "@/components/buttons/Button";
import { Toggle } from "@/components/buttons/Toggle";
import { FlagIcon } from "@/components/FlagIcon";
import { Dropdown } from "@/components/form/Dropdown";
@@ -38,6 +40,17 @@ export function PreferencesPart(props: {
(item) => item.id === getLocaleInfo(props.language)?.code,
);
+ const allSources = getAllProviders().listSources();
+
+ const sourceItems = useMemo(() => {
+ const currentDeviceSources = getProviders().listSources();
+ return props.sourceOrder.map((id) => ({
+ id,
+ name: allSources.find((s) => s.id === id)?.name || id,
+ disabled: !currentDeviceSources.find((s) => s.id === id),
+ }));
+ }, [props.sourceOrder, allSources]);
+
return (
{t("settings.preferences.title")}
@@ -108,17 +121,18 @@ export function PreferencesPart(props: {
({
- id,
- name:
- getProviders()
- .listSources()
- .find((s) => s.id === id)?.name || id,
- }))}
+ items={sourceItems}
setItems={(items) =>
props.setSourceOrder(items.map((item) => item.id))
}
/>
+
);
From f248a5ff61b1135583af2b8278e611c2e20d3cb7 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 22:51:49 +0200
Subject: [PATCH 7/9] remove unused import
---
src/pages/Settings.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx
index 03aea1db..a0818112 100644
--- a/src/pages/Settings.tsx
+++ b/src/pages/Settings.tsx
@@ -11,7 +11,7 @@ import {
import { getSessions, updateSession } from "@/backend/accounts/sessions";
import { updateSettings } from "@/backend/accounts/settings";
import { editUser } from "@/backend/accounts/user";
-import { getAllProviders, getProviders } from "@/backend/providers/providers";
+import { getAllProviders } from "@/backend/providers/providers";
import { Button } from "@/components/buttons/Button";
import { WideContainer } from "@/components/layout/WideContainer";
import { UserIcons } from "@/components/UserIcon";
From e2f1f8274b2212bffad7099c72a20a073333409e Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Mon, 15 Apr 2024 23:17:21 +0200
Subject: [PATCH 8/9] add global extension check
---
src/index.tsx | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/src/index.tsx b/src/index.tsx
index d5fffaae..29d76566 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -31,6 +31,7 @@ import { SettingsSyncer } from "@/stores/subtitles/SettingsSyncer";
import { ThemeProvider } from "@/stores/theme";
import { TurnstileProvider } from "@/stores/turnstile";
+import { extensionInfo } from "./backend/extension/messaging";
import { initializeChromecast } from "./setup/chromecast";
import { initializeOldStores } from "./stores/__old/migrations";
@@ -140,6 +141,15 @@ function TheRouter(props: { children: ReactNode }) {
return {props.children};
}
+// Checks if the extension is installed
+function ExtensionInfoLoader() {
+ useAsync(async () => {
+ await extensionInfo();
+ }, []);
+
+ return null;
+}
+
const container = document.getElementById("root");
const root = createRoot(container!);
@@ -149,6 +159,7 @@ root.render(
}>
+
From 07b07fc3547f1897b667c7ab7f552d0a18349d39 Mon Sep 17 00:00:00 2001
From: Jorrin
Date: Thu, 18 Apr 2024 20:31:53 +0200
Subject: [PATCH 9/9] throw promise to trigger suspense
---
src/index.tsx | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/index.tsx b/src/index.tsx
index 29d76566..b6e76888 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -31,7 +31,10 @@ import { SettingsSyncer } from "@/stores/subtitles/SettingsSyncer";
import { ThemeProvider } from "@/stores/theme";
import { TurnstileProvider } from "@/stores/turnstile";
-import { extensionInfo } from "./backend/extension/messaging";
+import {
+ extensionInfo,
+ isExtensionActiveCached,
+} from "./backend/extension/messaging";
import { initializeChromecast } from "./setup/chromecast";
import { initializeOldStores } from "./stores/__old/migrations";
@@ -142,10 +145,10 @@ function TheRouter(props: { children: ReactNode }) {
}
// Checks if the extension is installed
-function ExtensionInfoLoader() {
- useAsync(async () => {
- await extensionInfo();
- }, []);
+function ExtensionStatus() {
+ if (!isExtensionActiveCached()) {
+ throw extensionInfo();
+ }
return null;
}
@@ -159,7 +162,7 @@ root.render(
}>
-
+