pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add save confirmation to admin page

fixes embed order not propagating to backend

Pas 71a3d91b f25e0554

+176 -10
+105
src/hooks/useEmbedOrderState.ts
··· 1 + import { useCallback, useEffect, useMemo, useState } from "react"; 2 + 3 + import { updateSettings } from "@/backend/accounts/settings"; 4 + import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; 5 + import { useAuthStore } from "@/stores/auth"; 6 + import { usePreferencesStore } from "@/stores/preferences"; 7 + 8 + export function useEmbedOrderState() { 9 + const account = useAuthStore((s) => s.account); 10 + const backendUrl = useBackendUrl(); 11 + 12 + // Get current values from store 13 + const embedOrder = usePreferencesStore((s) => s.embedOrder); 14 + const enableEmbedOrder = usePreferencesStore((s) => s.enableEmbedOrder); 15 + const disabledEmbeds = usePreferencesStore((s) => s.disabledEmbeds); 16 + 17 + // Local state for tracking changes 18 + const [localEmbedOrder, setLocalEmbedOrder] = useState(embedOrder); 19 + const [localEnableEmbedOrder, setLocalEnableEmbedOrder] = 20 + useState(enableEmbedOrder); 21 + const [localDisabledEmbeds, setLocalDisabledEmbeds] = 22 + useState(disabledEmbeds); 23 + 24 + // Store setters 25 + const setEmbedOrder = usePreferencesStore((s) => s.setEmbedOrder); 26 + const setEnableEmbedOrder = usePreferencesStore((s) => s.setEnableEmbedOrder); 27 + const setDisabledEmbeds = usePreferencesStore((s) => s.setDisabledEmbeds); 28 + 29 + // Check if any changes have been made 30 + const hasChanges = useMemo(() => { 31 + return ( 32 + JSON.stringify(localEmbedOrder) !== JSON.stringify(embedOrder) || 33 + localEnableEmbedOrder !== enableEmbedOrder || 34 + JSON.stringify(localDisabledEmbeds) !== JSON.stringify(disabledEmbeds) 35 + ); 36 + }, [ 37 + localEmbedOrder, 38 + embedOrder, 39 + localEnableEmbedOrder, 40 + enableEmbedOrder, 41 + localDisabledEmbeds, 42 + disabledEmbeds, 43 + ]); 44 + 45 + // Reset local state to match store 46 + const reset = useCallback(() => { 47 + setLocalEmbedOrder(embedOrder); 48 + setLocalEnableEmbedOrder(enableEmbedOrder); 49 + setLocalDisabledEmbeds(disabledEmbeds); 50 + }, [embedOrder, enableEmbedOrder, disabledEmbeds]); 51 + 52 + // Save changes to backend and update store 53 + const saveChanges = useCallback(async () => { 54 + if (!account || !backendUrl) return; 55 + 56 + try { 57 + await updateSettings(backendUrl, account, { 58 + embedOrder: localEmbedOrder, 59 + enableEmbedOrder: localEnableEmbedOrder, 60 + disabledEmbeds: localDisabledEmbeds, 61 + }); 62 + 63 + // Update the store with the new values 64 + setEmbedOrder(localEmbedOrder); 65 + setEnableEmbedOrder(localEnableEmbedOrder); 66 + setDisabledEmbeds(localDisabledEmbeds); 67 + } catch (error) { 68 + console.error("Failed to save embed order settings:", error); 69 + throw error; 70 + } 71 + }, [ 72 + account, 73 + backendUrl, 74 + localEmbedOrder, 75 + localEnableEmbedOrder, 76 + localDisabledEmbeds, 77 + setEmbedOrder, 78 + setEnableEmbedOrder, 79 + setDisabledEmbeds, 80 + ]); 81 + 82 + // Update local state when store changes 83 + useEffect(() => { 84 + setLocalEmbedOrder(embedOrder); 85 + setLocalEnableEmbedOrder(enableEmbedOrder); 86 + setLocalDisabledEmbeds(disabledEmbeds); 87 + }, [embedOrder, enableEmbedOrder, disabledEmbeds]); 88 + 89 + return { 90 + // Current values 91 + embedOrder: localEmbedOrder, 92 + enableEmbedOrder: localEnableEmbedOrder, 93 + disabledEmbeds: localDisabledEmbeds, 94 + 95 + // Setters 96 + setEmbedOrder: setLocalEmbedOrder, 97 + setEnableEmbedOrder: setLocalEnableEmbedOrder, 98 + setDisabledEmbeds: setLocalDisabledEmbeds, 99 + 100 + // State management 101 + hasChanges, 102 + reset, 103 + saveChanges, 104 + }; 105 + }
+54 -1
src/pages/admin/AdminPage.tsx
··· 1 + import { useCallback, useState } from "react"; 2 + import { useTranslation } from "react-i18next"; 3 + 4 + import { Button } from "@/components/buttons/Button"; 1 5 import { ThinContainer } from "@/components/layout/ThinContainer"; 2 6 import { Heading1, Paragraph } from "@/components/utils/Text"; 7 + import { Transition } from "@/components/utils/Transition"; 8 + import { useEmbedOrderState } from "@/hooks/useEmbedOrderState"; 3 9 import { SubPageLayout } from "@/pages/layouts/SubPageLayout"; 4 10 import { ConfigValuesPart } from "@/pages/parts/admin/ConfigValuesPart"; 5 11 import { M3U8TestPart } from "@/pages/parts/admin/M3U8TestPart"; ··· 10 16 import { EmbedOrderPart } from "../parts/admin/EmbedOrderPart"; 11 17 12 18 export function AdminPage() { 19 + const { t } = useTranslation(); 20 + const [isSaving, setIsSaving] = useState(false); 21 + const embedOrderState = useEmbedOrderState(); 22 + 23 + const handleSaveChanges = useCallback(async () => { 24 + setIsSaving(true); 25 + try { 26 + await embedOrderState.saveChanges(); 27 + } catch (error) { 28 + console.error("Failed to save embed order changes:", error); 29 + } finally { 30 + setIsSaving(false); 31 + } 32 + }, [embedOrderState]); 33 + 13 34 return ( 14 35 <SubPageLayout> 15 36 <ThinContainer> ··· 21 42 <WorkerTestPart /> 22 43 <TMDBTestPart /> 23 44 <M3U8TestPart /> 24 - <EmbedOrderPart /> 45 + <EmbedOrderPart 46 + embedOrder={embedOrderState.embedOrder} 47 + setEmbedOrder={embedOrderState.setEmbedOrder} 48 + enableEmbedOrder={embedOrderState.enableEmbedOrder} 49 + setEnableEmbedOrder={embedOrderState.setEnableEmbedOrder} 50 + disabledEmbeds={embedOrderState.disabledEmbeds} 51 + setDisabledEmbeds={embedOrderState.setDisabledEmbeds} 52 + /> 25 53 </ThinContainer> 54 + 55 + <Transition 56 + animation="fade" 57 + show={embedOrderState.hasChanges} 58 + className="bg-settings-saveBar-background border-t border-settings-card-border/50 py-4 transition-opacity w-full fixed bottom-0 flex justify-between flex-col md:flex-row px-8 items-start md:items-center gap-3 z-[999]" 59 + > 60 + <p className="text-type-danger">{t("settings.unsaved")}</p> 61 + <div className="space-x-3 w-full md:w-auto flex"> 62 + <Button 63 + className="w-full md:w-auto" 64 + theme="secondary" 65 + onClick={embedOrderState.reset} 66 + > 67 + {t("settings.reset")} 68 + </Button> 69 + <Button 70 + className="w-full md:w-auto" 71 + theme="purple" 72 + loading={isSaving} 73 + onClick={handleSaveChanges} 74 + > 75 + {t("settings.save")} 76 + </Button> 77 + </div> 78 + </Transition> 26 79 </SubPageLayout> 27 80 ); 28 81 }
+17 -9
src/pages/parts/admin/EmbedOrderPart.tsx
··· 7 7 import { Toggle } from "@/components/buttons/Toggle"; 8 8 import { SortableListWithToggles } from "@/components/form/SortableListWithToggles"; 9 9 import { Heading2 } from "@/components/utils/Text"; 10 - import { usePreferencesStore } from "@/stores/preferences"; 10 + 11 + interface EmbedOrderPartProps { 12 + embedOrder: string[]; 13 + setEmbedOrder: (order: string[]) => void; 14 + enableEmbedOrder: boolean; 15 + setEnableEmbedOrder: (enabled: boolean) => void; 16 + disabledEmbeds: string[]; 17 + setDisabledEmbeds: (disabled: string[]) => void; 18 + } 11 19 12 - export function EmbedOrderPart() { 20 + export function EmbedOrderPart({ 21 + embedOrder, 22 + setEmbedOrder, 23 + enableEmbedOrder, 24 + setEnableEmbedOrder, 25 + disabledEmbeds, 26 + setDisabledEmbeds, 27 + }: EmbedOrderPartProps) { 13 28 const { t } = useTranslation(); 14 29 const navigate = useNavigate(); 15 - 16 - const embedOrder = usePreferencesStore((s) => s.embedOrder); 17 - const setEmbedOrder = usePreferencesStore((s) => s.setEmbedOrder); 18 - const enableEmbedOrder = usePreferencesStore((s) => s.enableEmbedOrder); 19 - const setEnableEmbedOrder = usePreferencesStore((s) => s.setEnableEmbedOrder); 20 - const disabledEmbeds = usePreferencesStore((s) => s.disabledEmbeds); 21 - const setDisabledEmbeds = usePreferencesStore((s) => s.setDisabledEmbeds); 22 30 23 31 const allEmbeds = getAllProviders().listEmbeds(); 24 32