diff --git a/src/modules/settings/profile/components/LoggedInView.tsx b/src/modules/settings/profile/components/LoggedInView.tsx deleted file mode 100644 index 3c6ae7d..0000000 --- a/src/modules/settings/profile/components/LoggedInView.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Focusable } from "@decky/ui"; -import { ProfileInstalledEntry } from "./ProfileInstalledEntry"; -import { useProfileContext } from "../state"; -import { SectionTitle } from "./SectionTitle"; -import { SectionSubtitle } from "./SectionSubtitle"; -import { ProfileUploadedEntry } from "./ProfileUploadedEntry"; - -export function LoggedInView() { - const { downloadedProfiles, localProfiles, uploadedProfiles } = useProfileContext(); - return ( - - - Downloaded Profiles - - {downloadedProfiles.map((profile) => ( - - ))} - - -
- - Your Profiles - - Local - - {localProfiles.map((profile) => ( - - ))} - - - - On DeckThemes - - {uploadedProfiles.map((profile) => ( - - ))} - - - - - ); -} diff --git a/src/modules/settings/profile/components/LoggedOutView.tsx b/src/modules/settings/profile/components/LoggedOutView.tsx deleted file mode 100644 index f2e57e1..0000000 --- a/src/modules/settings/profile/components/LoggedOutView.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Focusable } from "@decky/ui"; -import { ProfileInstalledEntry } from "./ProfileInstalledEntry"; -import { useProfileContext } from "../state"; -import { SectionTitle } from "./SectionTitle"; - -export function LoggedOutView() { - const { downloadedProfiles, localProfiles } = useProfileContext(); - return ( - - - Downloaded Profiles - - {downloadedProfiles.map((profile) => ( - - ))} - - -
- - Your Profiles - - {localProfiles.map((profile) => ( - - ))} - - - - ); -} diff --git a/src/modules/settings/profile/components/OnlineView.tsx b/src/modules/settings/profile/components/OnlineView.tsx new file mode 100644 index 0000000..e558e2d --- /dev/null +++ b/src/modules/settings/profile/components/OnlineView.tsx @@ -0,0 +1,52 @@ +import { Focusable } from "@decky/ui"; +import { ProfileInstalledEntry } from "./ProfileInstalledEntry"; +import { useProfileContext } from "../state"; +import { SectionTitle } from "./SectionTitle"; +import { SectionSubtitle } from "./SectionSubtitle"; +import { ProfileUploadedEntry } from "./ProfileUploadedEntry"; + +export function OnlineView() { + const { downloadedProfiles, localProfiles, uploadedProfiles } = useProfileContext(); + return ( + + {downloadedProfiles.length > 0 && ( + <> + + Downloaded Profiles + + {downloadedProfiles.map((profile) => ( + + ))} + + +
+ + )} + {(localProfiles.length > 0 || uploadedProfiles.length > 0) && ( + + Your Profiles + {localProfiles.length > 0 && ( + + Local + + {localProfiles.map((profile) => ( + + ))} + + + )} + {uploadedProfiles.length > 0 && ( + + On DeckThemes + + {uploadedProfiles.map((profile) => ( + + ))} + + + )} + + )} + + ); +} diff --git a/src/modules/settings/profile/components/index.ts b/src/modules/settings/profile/components/index.ts index 13d5f51..cc4d39f 100644 --- a/src/modules/settings/profile/components/index.ts +++ b/src/modules/settings/profile/components/index.ts @@ -1,4 +1,2 @@ -export * from "./ProfileInstalledEntry"; -export * from "./ProfileUploadedEntry"; -export * from "./SectionSubtitle"; -export * from "./SectionTitle"; +export * from "./OfflineView"; +export * from "./OnlineView"; diff --git a/src/modules/settings/profile/pages/ProfileSettingsPage.tsx b/src/modules/settings/profile/pages/ProfileSettingsPage.tsx index ab36895..5fee354 100644 --- a/src/modules/settings/profile/pages/ProfileSettingsPage.tsx +++ b/src/modules/settings/profile/pages/ProfileSettingsPage.tsx @@ -1,45 +1,37 @@ -import { useCSSLoaderActions, useCSSLoaderValues } from "@/backend"; import { PresetSelectionDropdown } from "@/lib"; -import { Flags, PartialCSSThemeInfo, Theme } from "@/types"; -import { DialogButton, Focusable } from "@decky/ui"; -import { useEffect, useMemo, useState } from "react"; +import { Focusable } from "@decky/ui"; import { ProfileContextProvider, useProfileContext } from "../state"; -import { OfflineView } from "../components/OfflineView"; -import { LoggedInView } from "../components/LoggedInView"; -import { LoggedOutView } from "../components/LoggedOutView"; +import { OfflineView, OnlineView } from "../components"; +import { ImSpinner5 } from "react-icons/im"; export function ProfileSettingsPage() { return ( - - -
- - + ); } -function ProfileSettingsContent() { +function ProfileSettingsPageContent() { + const { loading } = useProfileContext(); + return ( + + +
+ + {loading && ( +
+ +
+ )} + + ); +} + +function ProfileSettingsModeSwitcher() { const { displayMode } = useProfileContext(); - if (displayMode === "loggedin") { - return ; - } - if (displayMode === "loggedout") { - return ; + if (displayMode === "online") { + return ; } return ; } - -// function UploadProfileButton() { -// const { publishProfile } = useCSSLoaderActions(); -// return ( -// { -// publishProfile("LCD Hero.profile", false); -// }} -// > -// Upload Profile -// -// ); -// } diff --git a/src/modules/settings/profile/state/ProfileContext.tsx b/src/modules/settings/profile/state/ProfileContext.tsx index af83b16..c89c846 100644 --- a/src/modules/settings/profile/state/ProfileContext.tsx +++ b/src/modules/settings/profile/state/ProfileContext.tsx @@ -7,7 +7,8 @@ const ProfileContext = createContext({} as IProfileContext); // TODO: Potentially this should be moved to @cssloader as it isn't decky dependent // TODO: Also, this should be zustand using .subscribe on the cssloader store, I just was lazy implementing it this way here interface IProfileContextValues { - displayMode: "offline" | "loggedout" | "loggedin"; + displayMode: "offline" | "online"; + loading: boolean; downloadedProfiles: Theme[]; localProfiles: Theme[]; uploadedProfiles: PartialCSSThemeInfo[]; @@ -24,15 +25,20 @@ export function ProfileContextProvider({ children }: { children: React.ReactNode const { getUploadedThemes } = useCSSLoaderActions(); const profiles = themes.filter((e) => e.flags.includes(Flags.isPreset)); - const [displayMode, setDisplayMode] = useState<"offline" | "loggedout" | "loggedin">("offline"); - const localProfiles = profiles.filter( - (e) => updateStatuses.find((status) => status[0] === e.id)?.[1] === "local" - ); + const [displayMode, setDisplayMode] = useState<"offline" | "online">("offline"); + const [loading, setLoading] = useState(true); const [uploadedProfileRemoteEntries, setUploadedProfileRemoteEntries] = useState< PartialCSSThemeInfo[] >([]); + const localProfiles = profiles.filter((e) => { + const isLocal = updateStatuses.find((status) => status[0] === e.id)?.[1] === "local"; + const isInUploaded = uploadedProfileRemoteEntries.some( + (uploadedProfile) => uploadedProfile.id === e.id + ); + return isLocal && !isInUploaded; + }); const downloadedProfiles = profiles.filter((profile) => { const isInLocal = localProfiles.some((localProfile) => localProfile.id === profile.id); const isInUploaded = uploadedProfileRemoteEntries.some( @@ -46,18 +52,20 @@ export function ProfileContextProvider({ children }: { children: React.ReactNode setDisplayMode("offline"); return; } + + setDisplayMode("online"); if (!apiMeData) { - setDisplayMode("loggedout"); return; } + setLoading(true); // Logged in mode, separate downloaded and local, and show uploaded profiles const uploadedThemes = await getUploadedThemes(); const uploadedProfileRemoteEntries = uploadedThemes.filter((theme) => theme.targets.includes("Profile") ); setUploadedProfileRemoteEntries(uploadedProfileRemoteEntries); - setDisplayMode("loggedin"); + setLoading(false); } useEffect(() => { @@ -68,6 +76,7 @@ export function ProfileContextProvider({ children }: { children: React.ReactNode useContext(ProfileContext); - -// const profileStore = createStore((set, get) => { -// return { -// displayMode: "offline", -// downloadedProfiles: [], -// localProfiles: [], -// uploadedProfiles: [], -// async initialize() { -// const { apiMeData, updateStatuses, themes, getUploadedThemes } = getCSSLoaderState(); -// const profiles = themes.filter((e) => e.flags.includes(Flags.isPreset)); -// if (!updateStatuses) { -// // Offline mode, no profile sorting, just one list -// set({ displayMode: "offline", downloadedProfiles: profiles }); -// return; -// } - -// let downloadedProfiles: Theme[] = []; -// let localProfiles: Theme[] = []; -// profiles.forEach((e) => { -// if (updateStatuses.find((status) => status[0] === e.id)?.[1] === "local") { -// localProfiles.push(e); -// } else { -// downloadedProfiles.push(e); -// } -// }); - -// if (!apiMeData) { -// // Logged out mode, separate downloaded and local, but no 'Uploaded' section -// set({ displayMode: "loggedout", downloadedProfiles, localProfiles }); -// return; -// } - -// // Logged in mode, separate downloaded and local, and show uploaded profiles -// const uploadedThemes = await getUploadedThemes(); -// const uploadedProfileRemoteEntries = uploadedThemes.filter((theme) => -// theme.targets.includes("Profile") -// ); - -// // Since uploaded profiles are also technically 'downloaded', we have to manually filter them out -// downloadedProfiles = downloadedProfiles.filter( -// (profile) => -// !uploadedProfileRemoteEntries.some((uploadedProfile) => uploadedProfile.id === profile.id) -// ); -// set({ -// displayMode: "loggedin", -// downloadedProfiles, -// localProfiles, -// uploadedProfiles: uploadedProfileRemoteEntries, -// }); -// }, -// }; -// }); - -// export const useProfileStoreValues = generateStoreSelector(profileStore); -// export const useProfileStoreActions = generateStoreSelector(profileStore);