From dab365f04812f7d1eaa55804908e945642602ff5 Mon Sep 17 00:00:00 2001 From: LynithDev <61880709+LynithDev@users.noreply.github.com> Date: Sun, 28 Jul 2024 21:43:43 +0200 Subject: [PATCH] feat: Global Account Controller implented with Context's --- apps/desktop/build.rs | 2 + .../permissions/onelauncher/default.toml | 2 + apps/desktop/src/api/commands.rs | 20 +++ apps/frontend/src/ui/App.tsx | 2 + apps/frontend/src/ui/components/Navbar.tsx | 10 +- .../src/ui/components/game/PlayerHead.tsx | 18 +-- .../ui/components/overlay/AccountPopup.tsx | 75 ----------- .../overlay/account/AccountsPopup.tsx | 94 +++++++++++++ .../overlay/account/AddAccountModal.tsx | 127 ++++++++++++++++++ apps/frontend/src/ui/hooks/useAccount.ts | 11 -- .../pages/settings/game/SettingsAccounts.tsx | 111 ++------------- 11 files changed, 275 insertions(+), 197 deletions(-) delete mode 100644 apps/frontend/src/ui/components/overlay/AccountPopup.tsx create mode 100644 apps/frontend/src/ui/components/overlay/account/AccountsPopup.tsx create mode 100644 apps/frontend/src/ui/components/overlay/account/AddAccountModal.tsx delete mode 100644 apps/frontend/src/ui/hooks/useAccount.ts diff --git a/apps/desktop/build.rs b/apps/desktop/build.rs index f5b2cf71..99152bce 100644 --- a/apps/desktop/build.rs +++ b/apps/desktop/build.rs @@ -10,6 +10,8 @@ fn main() { "get_users", "get_user", "remove_user", + "get_default_user", + "set_default_user", // Cluster "create_cluster", "remove_cluster", diff --git a/apps/desktop/permissions/onelauncher/default.toml b/apps/desktop/permissions/onelauncher/default.toml index a55b8d12..a8df8266 100644 --- a/apps/desktop/permissions/onelauncher/default.toml +++ b/apps/desktop/permissions/onelauncher/default.toml @@ -6,6 +6,8 @@ permissions = [ "allow-get-users", "allow-get-user", "allow-remove-user", + "allow-get-default-user", + "allow-set-default-user", # Cluster "allow-create-cluster", diff --git a/apps/desktop/src/api/commands.rs b/apps/desktop/src/api/commands.rs index 98f196ff..97e50f4b 100644 --- a/apps/desktop/src/api/commands.rs +++ b/apps/desktop/src/api/commands.rs @@ -25,6 +25,8 @@ macro_rules! collect_commands { auth_login, get_users, get_user, + get_default_user, + set_default_user, remove_user, // Cluster create_cluster, @@ -212,6 +214,24 @@ pub async fn get_user(uuid: Uuid) -> Result { Ok(minecraft::get_user(uuid).await?) } +#[specta::specta] +#[tauri::command] +pub async fn get_default_user() -> Result, String> { + let uuid = minecraft::get_default_user().await?; + + match uuid { + Some(uuid) => Ok(Some(minecraft::get_user(uuid).await?)), + None => Ok(None), + } +} + +#[specta::specta] +#[tauri::command] +pub async fn set_default_user(uuid: Uuid) -> Result<(), String> { + minecraft::set_default_user(uuid).await?; + Ok(()) +} + #[specta::specta] #[tauri::command] pub async fn auth_login(handle: AppHandle) -> Result, String> { diff --git a/apps/frontend/src/ui/App.tsx b/apps/frontend/src/ui/App.tsx index f605a19f..83bb3ce5 100644 --- a/apps/frontend/src/ui/App.tsx +++ b/apps/frontend/src/ui/App.tsx @@ -8,6 +8,7 @@ import NotificationOverlay from './components/overlay/notifications/Notification import { SettingsProvider } from './hooks/useSettings'; import { ClusterModalController } from './components/overlay/cluster/ClusterCreationModal'; import { MultiProvider } from './components/MultiProvider'; +import { AccountControllerProvider } from './components/overlay/account/AddAccountModal'; import { AppInfo } from '~utils/program-info'; function App(props: ParentProps) { @@ -50,6 +51,7 @@ function GlobalContexts(props: ParentProps) { values={[ SettingsProvider, ClusterModalController, + AccountControllerProvider, ]} > {props.children} diff --git a/apps/frontend/src/ui/components/Navbar.tsx b/apps/frontend/src/ui/components/Navbar.tsx index 5a48ee4d..f7a95873 100644 --- a/apps/frontend/src/ui/components/Navbar.tsx +++ b/apps/frontend/src/ui/components/Navbar.tsx @@ -3,13 +3,13 @@ import { createSignal } from 'solid-js'; import { Bell01Icon, Cloud01Icon, Settings01Icon, TerminalBrowserIcon } from '@untitled-theme/icons-solid'; import { open } from '@tauri-apps/plugin-shell'; import { WEBSITE } from '@onelauncher/client'; -import useAccount from '../hooks/useAccount'; import PolyfrostFull from './logos/PolyfrostFull'; -import AccountPopup from './overlay/AccountPopup'; +import AccountPopup from './overlay/account/AccountsPopup'; import PlayerHead from './game/PlayerHead'; import Button from './base/Button'; import NotificationPopup from './overlay/notifications/NotificationPopup'; import Popup from './overlay/Popup'; +import useAccountController from './overlay/account/AddAccountModal'; interface NavbarLinkProps { path: string; @@ -33,9 +33,9 @@ function NavbarLink(props: NavbarLinkProps) { function Navbar() { const [profileMenuOpen, setProfileMenuOpen] = createSignal(false); const [notificationMenuOpen, setNotificationMenuOpen] = createSignal(false); - const navigate = useNavigate(); + const controller = useAccountController(); - const account = useAccount(); + const navigate = useNavigate(); let profileButton!: HTMLButtonElement; let notificationButton!: HTMLButtonElement; @@ -81,7 +81,7 @@ function Navbar() { ref={profileButton} onClick={() => setProfileMenuOpen(!profileMenuOpen())} > - + diff --git a/apps/frontend/src/ui/components/game/PlayerHead.tsx b/apps/frontend/src/ui/components/game/PlayerHead.tsx index 0d395ee5..0f5d0285 100644 --- a/apps/frontend/src/ui/components/game/PlayerHead.tsx +++ b/apps/frontend/src/ui/components/game/PlayerHead.tsx @@ -3,7 +3,7 @@ import type { JSX } from 'solid-js'; import steveSrc from '../../../assets/images/steve.png'; type PlayerHeadProps = JSX.IntrinsicElements['img'] & { - uuid: string; + uuid: string | null | undefined; onError?: () => any; }; @@ -24,13 +24,15 @@ function PlayerHead(props: PlayerHeadProps) { {...rest} /> - setLoading(false)} - onError={() => props.onError && props.onError()} - class={`image-render-pixel ${isLoading() ? 'hidden' : ''} ${split.class}`} - {...rest} - /> + + setLoading(false)} + onError={() => props.onError && props.onError()} + class={`image-render-pixel ${isLoading() ? 'hidden' : ''} ${split.class}`} + {...rest} + /> + ); } diff --git a/apps/frontend/src/ui/components/overlay/AccountPopup.tsx b/apps/frontend/src/ui/components/overlay/AccountPopup.tsx deleted file mode 100644 index 5f48f91e..00000000 --- a/apps/frontend/src/ui/components/overlay/AccountPopup.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { LogOut01Icon, PlusIcon, Settings01Icon } from '@untitled-theme/icons-solid'; -import { useNavigate } from '@solidjs/router'; -import Button from '../base/Button'; -import PlayerHead from '../game/PlayerHead'; -import useAccount from '../../hooks/useAccount'; -import Popup from './Popup'; - -interface AccountComponentProps { - username: string; - uuid: string; - loggedIn?: boolean; -} - -function AccountComponent(props: AccountComponentProps) { - return ( -
-
- -
-
-

{props.username}

- {props.loggedIn &&

Logged in

} -
-
-
- {props.loggedIn && ( - - )} -
- ); -} - -function AccountPopup(props: Popup.PopupProps) { - const loggedInAccount = useAccount(); - const navigate = useNavigate(); - - return ( - -
-
- - -
- - - -
- -
-
- -
-
- -
-
- -
-
- - ); -} - -export default AccountPopup; diff --git a/apps/frontend/src/ui/components/overlay/account/AccountsPopup.tsx b/apps/frontend/src/ui/components/overlay/account/AccountsPopup.tsx new file mode 100644 index 00000000..9c330dff --- /dev/null +++ b/apps/frontend/src/ui/components/overlay/account/AccountsPopup.tsx @@ -0,0 +1,94 @@ +import { For, Show } from 'solid-js'; +import { LogOut01Icon, PlusIcon, Settings01Icon } from '@untitled-theme/icons-solid'; +import { useNavigate } from '@solidjs/router'; +import Button from '../../base/Button'; +import PlayerHead from '../../game/PlayerHead'; +import Popup from '../Popup'; +import useAccountController from './AddAccountModal'; +import type { MinecraftCredentials } from '~bindings'; + +interface AccountComponentProps { + account: MinecraftCredentials | null | undefined; + loggedIn?: boolean; +} + +function AccountComponent(props: AccountComponentProps) { + return ( + +
+
+ +
+
+

{props.account!.username}

+ {props.loggedIn &&

Logged in

} +
+
+
+ {props.loggedIn && ( + + )} +
+
+ ); +} + +function AccountPopup(props: Popup.PopupProps) { + const controller = useAccountController(); + + const filteredAccounts = () => controller.accounts()?.filter(account => account.id !== controller.defaultAccount()?.id) ?? []; + + const navigate = useNavigate(); + + return ( + +
+
+ + + +
+ + {account => ( + + )} + +
+ + +
+
+ +
+
+ +
+
+ +
+
+ + ); +} + +export default AccountPopup; diff --git a/apps/frontend/src/ui/components/overlay/account/AddAccountModal.tsx b/apps/frontend/src/ui/components/overlay/account/AddAccountModal.tsx new file mode 100644 index 00000000..6b01b502 --- /dev/null +++ b/apps/frontend/src/ui/components/overlay/account/AddAccountModal.tsx @@ -0,0 +1,127 @@ +import { type Accessor, type Context, Match, type ParentProps, type Resource, type Setter, Switch, createContext, createSignal, useContext } from 'solid-js'; +import { LinkExternal01Icon } from '@untitled-theme/icons-solid'; +import Modal from '../Modal'; +import type { MinecraftCredentials } from '~bindings'; +import { bridge } from '~imports'; +import useCommand, { tryResult } from '~ui/hooks/useCommand'; +import Button from '~ui/components/base/Button'; + +interface AccountControllerContextFunc { + displayAddAccount: () => void; + refetch: () => void; + accounts: Resource; + defaultAccount: Resource; + setDefaultAccount: (uuid: string) => Promise; + removeAccount: (uuid: string) => Promise; +} + +const AccountControllerContext = createContext() as Context; + +export function AccountControllerProvider(props: ParentProps) { + const [accounts, { refetch: refetchAccounts }] = useCommand(bridge.commands.getUsers); + const [defaultAccount, { refetch: refetchDefaultAccount }] = useCommand(bridge.commands.getDefaultUser); + const [visible, setVisible] = createSignal(false); + + function refetch() { + refetchAccounts(); + refetchDefaultAccount(); + } + + async function setDefaultAccount(uuid: string) { + await tryResult(bridge.commands.setDefaultUser, uuid); + } + + async function removeAccount(uuid: string) { + await tryResult(bridge.commands.removeUser, uuid); + refetch(); + } + + const func: AccountControllerContextFunc = { + displayAddAccount: () => setVisible(true), + refetch, + accounts, + defaultAccount, + setDefaultAccount, + removeAccount, + }; + + return ( + + {props.children} + + + ); +} + +export function useAccountController() { + return useContext(AccountControllerContext); +} + +export default useAccountController; + +enum ModalStage { + Tasks, + WaitingForCode, + LoggingIn, +} + +interface AddAccountModalProps { + visible: Accessor; + setVisible: Setter; + refetch: () => any; +} + +function AddAccountModal(props: AddAccountModalProps) { + const [stage, setStage] = createSignal(ModalStage.Tasks); + + function start() { + setStage(ModalStage.WaitingForCode); + + // tryResult(bridge.commands.beginMsa).then((res) => { + // console.log(res); + // }); + } + + // function finish() { + // props.setVisible(false); + // props.refetch(); + // } + + return ( + props.setVisible(false)} + />, +
- - - ); } -enum ModalStage { - Tasks, - WaitingForCode, - LoggingIn, -} - -interface AddAccountModalProps { - visible: Accessor; - setVisible: Setter; - refetch: () => any; -} - -function AddAccountModal(props: AddAccountModalProps) { - const [stage, setStage] = createSignal(ModalStage.Tasks); - - function start() { - setStage(ModalStage.WaitingForCode); - - // tryResult(bridge.commands.beginMsa).then((res) => { - // console.log(res); - // }); - } - - function finish() { - props.setVisible(false); - props.refetch(); - } - - return ( - props.setVisible(false)} - />, -