From 2ffe9284f2bc03086c296d9255dcac38ac6bcfc4 Mon Sep 17 00:00:00 2001 From: Tormak <63308171+Tormak9970@users.noreply.github.com> Date: Sun, 7 Jan 2024 21:43:37 -0500 Subject: [PATCH] chore: refactored groups to profiles --- main.py | 16 +-- src/components/QuickAccessContent.tsx | 6 +- src/components/context-menus/LibraryMenu.tsx | 4 +- src/components/context-menus/TabGroupMenu.tsx | 78 ------------- .../context-menus/TabProfileMenu.tsx | 78 +++++++++++++ src/components/modals/TabGroupModals.tsx | 57 ---------- src/components/modals/TabProfileModals.tsx | 103 ++++++++++++++++++ .../styles/TabProfileModalStyles.tsx | 56 ++++++++++ src/lib/controllers/PythonInterop.ts | 20 ++-- src/state/TabGroupManager.tsx | 45 -------- src/state/TabMasterManager.tsx | 10 +- src/state/TabProfileManager.tsx | 45 ++++++++ 12 files changed, 310 insertions(+), 208 deletions(-) delete mode 100644 src/components/context-menus/TabGroupMenu.tsx create mode 100644 src/components/context-menus/TabProfileMenu.tsx delete mode 100644 src/components/modals/TabGroupModals.tsx create mode 100644 src/components/modals/TabProfileModals.tsx create mode 100644 src/components/styles/TabProfileModalStyles.tsx delete mode 100644 src/state/TabGroupManager.tsx create mode 100644 src/state/TabProfileManager.tsx diff --git a/main.py b/main.py index f45b436..0bd9caf 100644 --- a/main.py +++ b/main.py @@ -148,18 +148,18 @@ async def get_friends_games(self) -> dict[int, list[int]] | None: log(f"Got {len(friends_games)} friendsGames") return friends_games or {} - async def get_tab_groups(self) -> dict[str, list[str]] | None: + async def get_tab_profiles(self) -> dict[str, list[str]] | None: """ - Waits until users_dict is loaded, then returns the tab groups + Waits until users_dict is loaded, then returns the tab profiles - :return: User's tab groups + :return: User's tab profiles """ while Plugin.users_dict is None: await asyncio.sleep(0.1) - tab_groups = Plugin.users_dict[Plugin.user_id]["tabGroups"] - log(f"Got tab groups {tab_groups}") - return tab_groups or {} + tab_profiles = Plugin.users_dict[Plugin.user_id]["tabProfiles"] + log(f"Got tab profiles {tab_profiles}") + return tab_profiles or {} # Plugin settings setters async def set_tabs(self, tabs: dict[str, dict]): @@ -178,8 +178,8 @@ async def set_friends_games(self, friends_games: dict[str, list[int]]): Plugin.users_dict[Plugin.user_id]["friendsGames"] = friends_games await Plugin.set_setting(self, "usersDict", Plugin.users_dict) - async def set_tab_groups(self, tab_groups: dict[str, list[str]]): - Plugin.users_dict[Plugin.user_id]["tabGroups"] = tab_groups + async def set_tab_profiles(self, tab_profiles: dict[str, list[str]]): + Plugin.users_dict[Plugin.user_id]["tabProfiles"] = tab_profiles await Plugin.set_setting(self, "usersDict", Plugin.users_dict) async def get_docs(self): diff --git a/src/components/QuickAccessContent.tsx b/src/components/QuickAccessContent.tsx index 5262cc1..7bb7d63 100644 --- a/src/components/QuickAccessContent.tsx +++ b/src/components/QuickAccessContent.tsx @@ -27,7 +27,7 @@ import { TabListLabel } from './TabListLabel'; import { MicroSDeckInstallState, MicroSDeckInterop, microSDeckLibVersion } from '../lib/controllers/MicroSDeckInterop'; import { MicroSDeckNotice } from './MicroSDeckNotice'; import { CustomTabContainer } from './CustomTabContainer'; -import { TabGroupsMenu } from './context-menus/TabGroupMenu'; +import { TabProfilesMenu } from './context-menus/TabProfileMenu'; export type TabIdEntryType = { @@ -103,11 +103,11 @@ export const QuickAccessContent: VFC<{}> = ({ }) => { { if(evt.detail.button === GamepadButton.SELECT) { - showContextMenu(); + showContextMenu(); } }} onMenuButton={() => { Navigation.CloseSideMenus(); Navigation.Navigate("/tab-master-docs"); }} diff --git a/src/components/context-menus/LibraryMenu.tsx b/src/components/context-menus/LibraryMenu.tsx index 005c159..516cb3f 100644 --- a/src/components/context-menus/LibraryMenu.tsx +++ b/src/components/context-menus/LibraryMenu.tsx @@ -10,7 +10,7 @@ import { PresetMenuItems } from './PresetMenu'; import { CustomTabContainer } from '../CustomTabContainer'; import { TabListLabel } from '../TabListLabel'; import { MicroSDeckInterop } from '../../lib/controllers/MicroSDeckInterop'; -import { TabGroupsSubMenu } from './TabGroupMenu'; +import { TabProfilesSubMenu } from './TabProfileMenu'; import { TabIdEntryType } from "../QuickAccessContent"; export interface LibraryMenuProps { @@ -66,7 +66,7 @@ const LibraryMenuItems: VFC = ({ selectedTabId, closeMenu
- + diff --git a/src/components/context-menus/TabGroupMenu.tsx b/src/components/context-menus/TabGroupMenu.tsx deleted file mode 100644 index 02df9a0..0000000 --- a/src/components/context-menus/TabGroupMenu.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Menu, MenuGroup, MenuItem, showModal } from 'decky-frontend-lib'; -import { VFC, Fragment } from 'react'; -import { TabMasterManager } from '../../state/TabMasterManager'; -import { CreateTabGroupModal, OverwriteTabGroupModal } from '../modals/TabGroupModals'; - -interface TabsGroupMenuProps { - tabMasterManager: TabMasterManager, -} - -/** - * Context menu for managing Tab Groups. - */ -export const TabGroupsMenu: VFC = ({ tabMasterManager }) => { - return - - ; -}; - -/** - * Context menu sub-menu for managing Tab Groups. - */ -export const TabGroupsSubMenu: VFC = ({ tabMasterManager }) => { - return - - ; -}; - -/** - * Menu items for the Tab Group context menu. - */ -const TabGroupMenuItems: VFC = ({ tabMasterManager }) => { - return ( - <> - showModal()}> - Create Group - - - {/*
*/} - - - ); -}; - -/** - * The overwrite menu for Tab Groups. - */ -const OverwriteTabGroupMenu: VFC = ({ tabMasterManager }) => { - return ( - - {Object.keys(tabMasterManager.tabGroupManager?.tabGroups ?? {}).map(snapshotName => { - return ( - showModal()}> - {snapshotName} - - ); - })} - - ); -}; - -/** - * The apply menu for Tab Groups. - */ -const ApplyTabGroupMenu: VFC = ({ tabMasterManager }) => { - return ( - - {Object.keys(tabMasterManager.tabGroupManager?.tabGroups ?? {}).map(snapshotName => { - return ( - tabMasterManager.tabGroupManager?.apply(snapshotName, tabMasterManager)}> - {snapshotName} - - ); - })} - - ); -}; - - diff --git a/src/components/context-menus/TabProfileMenu.tsx b/src/components/context-menus/TabProfileMenu.tsx new file mode 100644 index 0000000..e546952 --- /dev/null +++ b/src/components/context-menus/TabProfileMenu.tsx @@ -0,0 +1,78 @@ +import { Menu, MenuGroup, MenuItem, showModal } from 'decky-frontend-lib'; +import { VFC, Fragment } from 'react'; +import { TabMasterManager } from '../../state/TabMasterManager'; +import { CreateTabProfileModal, OverwriteTabProfileModal } from '../modals/TabProfileModals'; + +interface TabsProfilesMenuProps { + tabMasterManager: TabMasterManager, +} + +/** + * Context menu for managing Tab Profiles. + */ +export const TabProfilesMenu: VFC = ({ tabMasterManager }) => { + return + + ; +}; + +/** + * Context menu sub-menu for managing Tab Profiles. + */ +export const TabProfilesSubMenu: VFC = ({ tabMasterManager }) => { + return + + ; +}; + +/** + * Menu items for the Tab Profiles context menu. + */ +const TabProfileMenuItems: VFC = ({ tabMasterManager }) => { + return ( + <> + showModal()}> + Create Profile + + + {/*
*/} + + + ); +}; + +/** + * The overwrite menu for Tab Profiles. + */ +const OverwriteTabProfileMenu: VFC = ({ tabMasterManager }) => { + return ( + + {Object.keys(tabMasterManager.tabProfileManager?.tabProfiles ?? {}).map(snapshotName => { + return ( + showModal()}> + {snapshotName} + + ); + })} + + ); +}; + +/** + * The apply menu for Tab Profiles. + */ +const ApplyTabProfile: VFC = ({ tabMasterManager }) => { + return ( + + {Object.keys(tabMasterManager.tabProfileManager?.tabProfiles ?? {}).map(snapshotName => { + return ( + tabMasterManager.tabProfileManager?.apply(snapshotName, tabMasterManager)}> + {snapshotName} + + ); + })} + + ); +}; + + diff --git a/src/components/modals/TabGroupModals.tsx b/src/components/modals/TabGroupModals.tsx deleted file mode 100644 index cb0c768..0000000 --- a/src/components/modals/TabGroupModals.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { ConfirmModal, TextField } from 'decky-frontend-lib'; -import { VFC, useState } from 'react'; -import { TabMasterManager } from '../../state/TabMasterManager'; - -export interface CreateTabGroupModalProps { - tabMasterManager: TabMasterManager, - closeModal?: () => void, -} - -export const CreateTabGroupModal: VFC = ({ tabMasterManager, closeModal }) => { - const [name, setName] = useState(''); - const visibleTabs = tabMasterManager.getTabs().visibleTabsList; - - return ( - { - tabMasterManager.tabGroupManager?.write(name, visibleTabs.map(tabContainer => tabContainer.id)); - closeModal!(); - }} - onCancel={() => closeModal!()} - > - setName(e?.target.value)} /> - {visibleTabs.map(tabContainer =>
{tabContainer.title}
)} -
- ); -}; - -export interface OverwriteTabGroupModalProps extends CreateTabGroupModalProps { - groupName: string; -} - -export const OverwriteTabGroupModal: VFC = ({ groupName, tabMasterManager, closeModal }) => { - const { visibleTabsList, tabsMap } = tabMasterManager.getTabs(); - const existingTabs = tabMasterManager.tabGroupManager!.tabGroups[groupName].map(tabId => tabsMap.get(tabId)); - - return ( - { - tabMasterManager.tabGroupManager?.write(groupName, visibleTabsList.map(tabContainer => tabContainer.id)); - closeModal!(); - }} - onCancel={() => closeModal!()} - > -
-
- New Tabs: - {visibleTabsList.map(tabContainer =>
{tabContainer.title}
)} -
-
- Existing Tabs: - {existingTabs.map(tabContainer =>
{tabContainer?.title}
)} -
-
-
- ); -}; - diff --git a/src/components/modals/TabProfileModals.tsx b/src/components/modals/TabProfileModals.tsx new file mode 100644 index 0000000..2c08f84 --- /dev/null +++ b/src/components/modals/TabProfileModals.tsx @@ -0,0 +1,103 @@ +import { ConfirmModal, Field, TextField, afterPatch, quickAccessControlsClasses } from 'decky-frontend-lib'; +import { VFC, useEffect, useState, Fragment } from 'react'; +import { TabMasterManager } from '../../state/TabMasterManager'; +import { TabMasterContextProvider } from "../../state/TabMasterContext"; +import { TabProfileModalStyles } from "../styles/TabProfileModalStyles"; + +export interface CreateTabProfileModalProps { + tabMasterManager: TabMasterManager, + closeModal?: () => void, +} + +export const CreateTabProfileModal: VFC = ({ tabMasterManager, closeModal }) => { + const [name, setName] = useState(''); + const visibleTabs = tabMasterManager.getTabs().visibleTabsList; + const [patchInput, setPatchInput] = useState(true); + + const nameInputElement = ; + + //reference to input field class component instance, which has a focus method + let inputComponentInstance: any; + + if (patchInput) { + afterPatch(nameInputElement.type.prototype, 'render', function (_: any, ret: any) { + //@ts-ignore get reference to instance + inputComponentInstance = this; + return ret; + }, { singleShot: true }); + } + + useEffect(() => { + inputComponentInstance.Focus(); + setPatchInput(false); + }, []); + + function onNameChange(e: React.ChangeEvent) { + setName(e?.target.value); + } + + return ( + + +
+ { + tabMasterManager.tabProfileManager?.write(name, visibleTabs.map(tabContainer => tabContainer.id)); + closeModal!(); + }} + onCancel={() => closeModal!()} + > +
+ +
+ Profile Name +
+ {nameInputElement} + + } /> +
+ {visibleTabs.map(tabContainer =>
{tabContainer.title}
)} +
+
+
+ ); +}; + +export interface OverwriteTabProfileModalProps extends CreateTabProfileModalProps { + profileName: string; +} + +export const OverwriteTabProfileModal: VFC = ({ profileName, tabMasterManager, closeModal }) => { + const { visibleTabsList, tabsMap } = tabMasterManager.getTabs(); + const existingTabs = tabMasterManager.tabProfileManager!.tabProfiles[profileName].map(tabId => tabsMap.get(tabId)); + + return ( + + +
+ { + tabMasterManager.tabProfileManager?.write(profileName, visibleTabsList.map(tabContainer => tabContainer.id)); + closeModal!(); + }} + onCancel={() => closeModal!()} + > +
+
+ New Tabs: + {visibleTabsList.map(tabContainer =>
{tabContainer.title}
)} +
+
+ Existing Tabs: + {existingTabs.map(tabContainer =>
{tabContainer?.title}
)} +
+
+
+
+
+ ); +}; + diff --git a/src/components/styles/TabProfileModalStyles.tsx b/src/components/styles/TabProfileModalStyles.tsx new file mode 100644 index 0000000..21d0862 --- /dev/null +++ b/src/components/styles/TabProfileModalStyles.tsx @@ -0,0 +1,56 @@ +import { gamepadDialogClasses } from "decky-frontend-lib"; +import { VFC } from "react"; + +// New modal background should be "radial-gradient(155.42% 100% at 0% 0%, #060a0e 0 0%, #0e141b 100%)" + +/** + * CSS styling for TabMaster's Tab profile modals. + */ +export const TabProfileModalStyles: VFC<{}> = ({}) => { + return ( + + ); +} diff --git a/src/lib/controllers/PythonInterop.ts b/src/lib/controllers/PythonInterop.ts index fb9500d..ddcfc56 100644 --- a/src/lib/controllers/PythonInterop.ts +++ b/src/lib/controllers/PythonInterop.ts @@ -1,6 +1,6 @@ import { ServerAPI } from "decky-frontend-lib"; import { validateTabs } from "../Utils"; -import { TabGroupDictionary } from '../../state/TabGroupManager'; +import { TabProfileDictionary } from '../../state/TabProfileManager'; /** * Class for frontend -> backend communication. @@ -182,11 +182,11 @@ export class PythonInterop { } /** - * Gets the user's tab groups. - * @returns A promise resolving the user's tab groups. + * Gets the user's tab profiles. + * @returns A promise resolving the user's tab profiles. */ - static async getTabGroups(): Promise { - let result = await PythonInterop.serverAPI.callPluginMethod<{}, TabGroupDictionary>("get_tab_groups", {}); + static async getTabProfiles(): Promise { + let result = await PythonInterop.serverAPI.callPluginMethod<{}, TabProfileDictionary>("get_tab_profiles", {}); if (result.success) { return result.result; @@ -267,12 +267,12 @@ export class PythonInterop { } /** - * Sets the tab groups. - * @param tabGroups The tab groups. - * @returns A promise resolving to whether or not the tab groups were successfully set. + * Sets the user's tab profiles. + * @param tabProfiles The tab profiles. + * @returns A promise resolving to whether or not the tab profiles were successfully set. */ - static async setTabGroups(tabGroups: TabGroupDictionary): Promise { - let result = await PythonInterop.serverAPI.callPluginMethod<{ tab_groups: TabGroupDictionary }, void>("set_tab_groups", { tab_groups: tabGroups }); + static async setTabProfiles(tabProfiles: TabProfileDictionary): Promise { + let result = await PythonInterop.serverAPI.callPluginMethod<{ tab_profiles: TabProfileDictionary }, void>("set_tab_profiles", { tab_profiles: tabProfiles }); if (result.success) { return result.result; diff --git a/src/state/TabGroupManager.tsx b/src/state/TabGroupManager.tsx deleted file mode 100644 index a93583e..0000000 --- a/src/state/TabGroupManager.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { PythonInterop } from '../lib/controllers/PythonInterop'; -import { TabMasterManager } from './TabMasterManager'; - -export type TabGroupDictionary = { - [name: string]: string[]; //array of ordered tab ids -}; - -export class TabGroupManager { - tabGroups: TabGroupDictionary; - - /** - * Creates a new TabGroupManager. - * @param tabGroups The existing tab groups the current user has. - */ - constructor(tabGroups: TabGroupDictionary) { - this.tabGroups = tabGroups; - } - - /** - * Writes a tab group. - * @param tabGroupName The name of the tab group to write. - * @param tabIds The list of ids of the tabs that are included in this group. - */ - write(tabGroupName: string, tabIds: string[]) { - this.tabGroups[tabGroupName] = tabIds; - this.save(); - } - - /** - * Applies a tab group. - * @param tabGroupName The name of the tab group to apply. - * @param tabMasterManager The plugin manager. - */ - apply(tabGroupName: string, tabMasterManager: TabMasterManager) { - tabMasterManager.getTabs().tabsMap.forEach(tabContainer => tabContainer.position = -1); - tabMasterManager.reorderTabs(this.tabGroups[tabGroupName]); - } - - /** - * Saves all changes made to the tab groups. - */ - private save() { - PythonInterop.setTabGroups(this.tabGroups); - } -} diff --git a/src/state/TabMasterManager.tsx b/src/state/TabMasterManager.tsx index 25ebb0a..cf3dfcc 100644 --- a/src/state/TabMasterManager.tsx +++ b/src/state/TabMasterManager.tsx @@ -9,7 +9,7 @@ import { LogController } from "../lib/controllers/LogController"; import { PresetName, PresetOptions, getPreset } from '../presets/presets'; import { MicroSDeckInterop } from '../lib/controllers/MicroSDeckInterop'; import { TabErrorController } from '../lib/controllers/TabErrorController'; -import { TabGroupDictionary, TabGroupManager } from './TabGroupManager'; +import { TabProfileDictionary, TabProfileManager } from './TabProfileManager'; /** * Converts a list of filters into a 1D array. @@ -67,7 +67,7 @@ export class TabMasterManager { private collectionRemoveReaction: IReactionDisposer | undefined; - public tabGroupManager: TabGroupManager | undefined; + public tabProfileManager: TabProfileManager | undefined; /** * Creates a new TabMasterManager. @@ -523,12 +523,12 @@ export class TabMasterManager { } } }); - PythonInterop.getTabGroups().then((res: TabGroupDictionary | Error) => { + PythonInterop.getTabProfiles().then((res: TabProfileDictionary | Error) => { if (res instanceof Error) { - LogController.log("TabMaster couldn't load tab groups"); + LogController.log("TabMaster couldn't load tab profiles"); LogController.error(res.message); } else { - this.tabGroupManager = new TabGroupManager(res); + this.tabProfileManager = new TabProfileManager(res); } }); diff --git a/src/state/TabProfileManager.tsx b/src/state/TabProfileManager.tsx new file mode 100644 index 0000000..b0d67ff --- /dev/null +++ b/src/state/TabProfileManager.tsx @@ -0,0 +1,45 @@ +import { PythonInterop } from '../lib/controllers/PythonInterop'; +import { TabMasterManager } from './TabMasterManager'; + +export type TabProfileDictionary = { + [name: string]: string[]; //array of ordered tab ids +}; + +export class TabProfileManager { + tabProfiles: TabProfileDictionary; + + /** + * Creates a new TabProfileManager. + * @param tabProfiles The existing tab profiles the current user has. + */ + constructor(tabProfiles: TabProfileDictionary) { + this.tabProfiles = tabProfiles; + } + + /** + * Writes a tab profile. + * @param tabProfileName The name of the tab profile to write. + * @param tabIds The list of ids of the tabs that are included in this profile. + */ + write(tabProfileName: string, tabIds: string[]) { + this.tabProfiles[tabProfileName] = tabIds; + this.save(); + } + + /** + * Applies a tab profile. + * @param tabProfileName The name of the tab profile to apply. + * @param tabMasterManager The plugin manager. + */ + apply(tabProfileName: string, tabMasterManager: TabMasterManager) { + tabMasterManager.getTabs().tabsMap.forEach(tabContainer => tabContainer.position = -1); + tabMasterManager.reorderTabs(this.tabProfiles[tabProfileName]); + } + + /** + * Saves all changes made to the tab profiles. + */ + private save() { + PythonInterop.setTabProfiles(this.tabProfiles); + } +}