diff --git a/apps/oneclient/frontend/src/bindings.gen.ts b/apps/oneclient/frontend/src/bindings.gen.ts index a5af6301..e8d29256 100644 --- a/apps/oneclient/frontend/src/bindings.gen.ts +++ b/apps/oneclient/frontend/src/bindings.gen.ts @@ -255,11 +255,8 @@ export type VersionType = */ "old_beta" -const ARGS_MAP = { 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'oneclient':'{"openDevTools":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}', 'core':'{"createCluster":["options"],"writeSettings":["setting"],"getPackageBody":["provider","body"],"getUser":["uuid"],"getMultiplePackages":["provider","slugs"],"removeCape":["access_token"],"openMsaLogin":[],"installModpack":["modpack","cluster_id"],"fetchLoggedInProfile":["access_token"],"getLogByName":["id","name"],"getUsers":[],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"killProcess":["pid"],"getClusters":[],"fetchMinecraftProfile":["uuid"],"getScreenshots":["id"],"getWorlds":["id"],"readSettings":[],"launchCluster":["id","uuid"],"getLoadersForVersion":["mc_version"],"getGameVersions":[],"getUsersFromAuthor":["provider","author"],"getRunningProcessesByClusterId":["cluster_id"],"changeCape":["access_token","cape_uuid"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getClusterById":["id"],"setDefaultUser":["uuid"],"getGlobalProfile":[],"updateClusterById":["id","request"],"removeCluster":["id"],"getRunningProcesses":[],"isClusterRunning":["cluster_id"],"updateClusterProfile":["name","profile"],"removeUser":["uuid"],"getLogs":["id"],"getDefaultUser":["fallback"],"searchPackages":["provider","query"],"getProfileOrDefault":["name"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"open":["input"],"convertUsernameUUID":["username_uuid"],"createSettingsProfile":["name"],"changeSkin":["access_token","skin_url","skin_variant"],"getPackage":["provider","slug"]}', 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}' } -export type Router = { 'oneclient': { openDevTools: () => Promise, -getClustersGroupedByMajor: () => Promise>, -getBundlesFor: (clusterId: number) => Promise }, -'core': { getClusters: () => Promise, +const ARGS_MAP = { 'oneclient':'{"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"],"openDevTools":[]}', 'core':'{"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"removeCape":["access_token"],"getGameVersions":[],"installModpack":["modpack","cluster_id"],"createSettingsProfile":["name"],"getPackageBody":["provider","body"],"getLogByName":["id","name"],"getScreenshots":["id"],"getWorlds":["id"],"getMultiplePackages":["provider","slugs"],"changeCape":["access_token","cape_uuid"],"convertUsernameUUID":["username_uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"fetchMinecraftProfile":["uuid"],"getPackage":["provider","slug"],"getUsersFromAuthor":["provider","author"],"createCluster":["options"],"getGlobalProfile":[],"getRunningProcesses":[],"setDefaultUser":["uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"writeSettings":["setting"],"updateClusterById":["id","request"],"open":["input"],"readSettings":[],"getRunningProcessesByClusterId":["cluster_id"],"isClusterRunning":["cluster_id"],"getLoadersForVersion":["mc_version"],"removeCluster":["id"],"launchCluster":["id","uuid"],"getProfileOrDefault":["name"],"updateClusterProfile":["name","profile"],"getLogs":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getUser":["uuid"],"searchPackages":["provider","query"],"killProcess":["pid"],"getClusterById":["id"],"getUsers":[],"getClusters":[],"openMsaLogin":[],"removeUser":["uuid"],"fetchLoggedInProfile":["access_token"],"getDefaultUser":["fallback"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}' } +export type Router = { 'core': { getClusters: () => Promise, getClusterById: (id: number) => Promise, removeCluster: (id: number) => Promise, createCluster: (options: CreateCluster) => Promise, @@ -307,7 +304,10 @@ open: (input: string) => Promise }, openCluster: (folderName: string) => Promise }, 'events': { ingress: (event: IngressPayload) => Promise, message: (event: MessagePayload) => Promise, -process: (event: ProcessPayload) => Promise } }; +process: (event: ProcessPayload) => Promise }, +'oneclient': { openDevTools: () => Promise, +getClustersGroupedByMajor: () => Promise>, +getBundlesFor: (clusterId: number) => Promise } }; export type { InferCommandOutput } diff --git a/apps/oneclient/frontend/src/routes/app/account/route.tsx b/apps/oneclient/frontend/src/routes/app/account/route.tsx index 6ce3ec92..1aaaf996 100644 --- a/apps/oneclient/frontend/src/routes/app/account/route.tsx +++ b/apps/oneclient/frontend/src/routes/app/account/route.tsx @@ -2,39 +2,49 @@ import type { MinecraftCredentials } from '@/bindings.gen'; import { LoaderSuspense } from '@/components'; import { bindings } from '@/main'; import { createFileRoute, Outlet } from '@tanstack/react-router'; +import { MissingAccountData } from './skins'; export interface AccountsRouteSearchParams { - profile: MinecraftCredentials; + profile: MinecraftCredentials | undefined; + search: boolean; } export const Route = createFileRoute('/app/account')({ component: RouteComponent, validateSearch: (search): AccountsRouteSearchParams => { return { - profile: search.profile as MinecraftCredentials, + profile: search.profile as MinecraftCredentials | undefined, + search: search.profile === undefined, }; }, async beforeLoad({ context, search }) { + // TODO instead replace this with a refresh access_token function. Waiting on binding from @LynithDev + const { profile, search: validSearch } = search; + if (!profile) + return { profileData: null, profile: null, validSearch }; + const query = context.queryClient.ensureQueryData({ - queryKey: ['fetchLoggedInProfile', search.profile.access_token], - queryFn: () => bindings.core.fetchLoggedInProfile(search.profile.access_token), + queryKey: ['fetchLoggedInProfile', profile.access_token], + queryFn: () => bindings.core.fetchLoggedInProfile(profile.access_token), }); const profileData = await query; return { profileData, - profile: search.profile, + profile, + validSearch, }; }, }); function RouteComponent() { + const { profileData, validSearch } = Route.useRouteContext(); + return ( <> - + {profileData === null ? : } - ); } diff --git a/apps/oneclient/frontend/src/routes/app/account/skins.tsx b/apps/oneclient/frontend/src/routes/app/account/skins.tsx index 07871e65..41bb2bd3 100644 --- a/apps/oneclient/frontend/src/routes/app/account/skins.tsx +++ b/apps/oneclient/frontend/src/routes/app/account/skins.tsx @@ -102,26 +102,36 @@ function useSkinHistory() { return [skins, setSkins, loaded] as const; } +export function MissingAccountData({ validSearch }: { validSearch: boolean }) { + return ( + } headerSmall={<>}> +

{validSearch ? 'Please select an account before going to the skins page' : 'Missing Profile auth. Please log out and log back in'}

+
+ ); +} + function RouteComponent() { - const { profileData, profile, queryClient } = Route.useRouteContext(); + const { profileData, profile, queryClient, validSearch } = Route.useRouteContext(); const [capes, setCapes] = useState>([]); const [selectedCape, setSelectedCape] = useState(''); const [shouldShowElytra, setShouldShowElytra] = useState(false); useEffect(() => { + if (!profileData) + return; setCapes([{ url: '', id: '' }, ...profileData.capes.map(cape => ({ url: cape.url, id: cape.id }))]); }, []); const [skins, setSkins, loaded] = useSkinHistory(); const [selectedSkin, setSelectedSkin] = useState({ skin_url: getSkinUrl(null), is_slim: false }); - const skinData: Skin = { is_slim: profileData.skins[0].variant === 'slim', skin_url: getSkinUrl(profileData.skins[0].url) }; + const skinData: Skin = { is_slim: profileData?.skins[0].variant === 'slim', skin_url: getSkinUrl(profileData?.skins[0].url) }; useEffect(() => { if (!loaded) return; - setSkins(prev => [...prev, skinData]); // ✅ always merges with latest + setSkins(prev => [...prev, skinData]); setSelectedSkin(skinData); }, [loaded]); @@ -155,6 +165,8 @@ function RouteComponent() { const saveSkinToAccount = async () => { try { + if (!profile) + return; await bindings.core.changeSkin(profile.access_token, selectedSkin.skin_url, selectedSkin.is_slim ? 'slim' : 'classic'); if (selectedCape === '') { await bindings.core.removeCape(profile.access_token); @@ -200,6 +212,9 @@ function RouteComponent() { setAnimationName(data.name); }; + if (profileData === null) + return ; + return (