Skip to content

Commit

Permalink
finish profile page view (excluding uploading)
Browse files Browse the repository at this point in the history
  • Loading branch information
beebls committed Mar 9, 2025
1 parent bbb054a commit f724bb5
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 168 deletions.
42 changes: 0 additions & 42 deletions src/modules/settings/profile/components/LoggedInView.tsx

This file was deleted.

29 changes: 0 additions & 29 deletions src/modules/settings/profile/components/LoggedOutView.tsx

This file was deleted.

52 changes: 52 additions & 0 deletions src/modules/settings/profile/components/OnlineView.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Focusable className="flex flex-col gap-4">
{downloadedProfiles.length > 0 && (
<>
<Focusable className="flex flex-col gap-2">
<SectionTitle>Downloaded Profiles</SectionTitle>
<Focusable className="flex flex-col gap-1">
{downloadedProfiles.map((profile) => (
<ProfileInstalledEntry key={profile.id} data={profile} />
))}
</Focusable>
</Focusable>
<div className="cl_divider" />
</>
)}
{(localProfiles.length > 0 || uploadedProfiles.length > 0) && (
<Focusable className="flex flex-col gap-2">
<SectionTitle>Your Profiles</SectionTitle>
{localProfiles.length > 0 && (
<Focusable className="flex flex-col">
<SectionSubtitle>Local</SectionSubtitle>
<Focusable className="flex flex-col gap-1">
{localProfiles.map((profile) => (
<ProfileInstalledEntry key={profile.id} data={profile} />
))}
</Focusable>
</Focusable>
)}
{uploadedProfiles.length > 0 && (
<Focusable className="flex flex-col">
<SectionSubtitle>On DeckThemes</SectionSubtitle>
<Focusable className="flex flex-col gap-1">
{uploadedProfiles.map((profile) => (
<ProfileUploadedEntry key={profile.id} data={profile} />
))}
</Focusable>
</Focusable>
)}
</Focusable>
)}
</Focusable>
);
}
6 changes: 2 additions & 4 deletions src/modules/settings/profile/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
export * from "./ProfileInstalledEntry";
export * from "./ProfileUploadedEntry";
export * from "./SectionSubtitle";
export * from "./SectionTitle";
export * from "./OfflineView";
export * from "./OnlineView";
54 changes: 23 additions & 31 deletions src/modules/settings/profile/pages/ProfileSettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<ProfileContextProvider>
<Focusable className="cl_settingspage_container flex flex-col gap-4">
<PresetSelectionDropdown noBottomSeparator />
<div className="cl_divider" />
<ProfileSettingsContent />
</Focusable>
<ProfileSettingsPageContent />
</ProfileContextProvider>
);
}

function ProfileSettingsContent() {
function ProfileSettingsPageContent() {
const { loading } = useProfileContext();
return (
<Focusable className="cl_settingspage_container flex flex-col gap-4">
<PresetSelectionDropdown noBottomSeparator />
<div className="cl_divider" />
<ProfileSettingsModeSwitcher />
{loading && (
<div className="h-full w-full flex items-center justify-center gap-4">
<ImSpinner5 className="cl_spinny" size={48} />
</div>
)}
</Focusable>
);
}

function ProfileSettingsModeSwitcher() {
const { displayMode } = useProfileContext();
if (displayMode === "loggedin") {
return <LoggedInView />;
}
if (displayMode === "loggedout") {
return <LoggedOutView />;
if (displayMode === "online") {
return <OnlineView />;
}
return <OfflineView />;
}

// function UploadProfileButton() {
// const { publishProfile } = useCSSLoaderActions();
// return (
// <DialogButton
// onClick={() => {
// publishProfile("LCD Hero.profile", false);
// }}
// >
// Upload Profile
// </DialogButton>
// );
// }
78 changes: 16 additions & 62 deletions src/modules/settings/profile/state/ProfileContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const ProfileContext = createContext<IProfileContext>({} 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[];
Expand All @@ -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(
Expand All @@ -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(() => {
Expand All @@ -68,6 +76,7 @@ export function ProfileContextProvider({ children }: { children: React.ReactNode
<ProfileContext.Provider
value={{
displayMode,
loading,
downloadedProfiles,
localProfiles,
uploadedProfiles: uploadedProfileRemoteEntries,
Expand All @@ -80,58 +89,3 @@ export function ProfileContextProvider({ children }: { children: React.ReactNode
}

export const useProfileContext = () => useContext(ProfileContext);

// const profileStore = createStore<IProfileStore>((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<IProfileStoreValues>(profileStore);
// export const useProfileStoreActions = generateStoreSelector<IProfileStoreActions>(profileStore);

0 comments on commit f724bb5

Please sign in to comment.