feat: account popup from onelauncher (fixes #363)

This commit is contained in:
Szedann 2025-09-04 16:34:35 +02:00
parent 107dd69c49
commit 035657cb79
2 changed files with 77 additions and 54 deletions

View file

@ -1,76 +1,99 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import { AccountAvatar } from '@/components/AccountAvatar';
import { bindings } from '@/main';
import { useCommandMut, useCommandSuspense } from '@onelauncher/common';
import { useQueryClient } from '@tanstack/react-query';
import { Button as AriaButton } from 'react-aria-components';
import { useCommand, useCommandMut } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { Link } from '@tanstack/react-router';
import { PlusIcon, Settings01Icon } from '@untitled-theme/icons-react';
import { DialogTrigger } from 'react-aria-components';
import { twMerge } from 'tailwind-merge';
import { AccountAvatar } from '../AccountAvatar';
import { AddAccountModal } from './AddAccountModal';
import { Overlay } from './Overlay';
import { Popup } from './Popup';
export function AccountPopup() {
const { data: currentAccount } = useCommandSuspense(['getDefaultUser'], () => bindings.core.getDefaultUser(true));
const { data: accounts } = useCommandSuspense(['getUsers', 'accountPopup'], bindings.core.getUsers, {
select(data) {
data.sort((a, b) => {
if (currentAccount?.id === a.id)
return -1;
else if (currentAccount?.id === b.id)
return 1;
return a.username.localeCompare(b.username);
});
const users = useCommand(['getUsers'], bindings.core.getUsers);
const defaultUser = useCommand(['getDefaultUser'], () => bindings.core.getDefaultUser(false));
return data;
},
});
const queryClient = useQueryClient();
const { mutate: setDefaultUser } = useCommandMut(bindings.core.setDefaultUser, {
onSuccess() {
queryClient.invalidateQueries({
queryKey: ['getDefaultUser'],
});
},
});
const setDefaultUser = (user: MinecraftCredentials) => {
bindings.core.setDefaultUser(user.id);
};
return (
<Popup placement="top left">
<div className="flex flex-col gap-1 min-w-40">
{accounts.map(profile => (
<AccountRow
key={profile.id}
onPress={() => setDefaultUser(profile.id)}
profile={profile}
selected={profile.id === currentAccount?.id}
/>
<div className="min-w-3xs">
{defaultUser.data && (
<div>
<AccountEntry
loggedIn
onClick={() => { }}
user={defaultUser.data}
/>
</div>
)}
{users.data?.filter(user => user.id !== defaultUser.data?.id).map(user => (
<div key={user.id}>
<AccountEntry
onClick={() => setDefaultUser(user)}
user={user}
/>
</div>
))}
<div className="flex flex-row justify-between">
<div className="self-center">
<DialogTrigger>
<Button color="ghost">
<PlusIcon />
Add Account
</Button>
<Overlay>
<AddAccountModal />
</Overlay>
</DialogTrigger>
</div>
<div className="flex flex-row">
<Link to="/app/accounts">
<Button color="ghost" size="iconLarge">
<Settings01Icon className="stroke-fg-primary" />
</Button>
</Link>
</div>
</div>
</div>
</Popup>
);
}
function AccountRow({
selected = false,
profile,
onPress,
export default AccountPopup;
function AccountEntry({
onClick,
user,
loggedIn = false,
}: {
selected?: boolean;
profile: MinecraftCredentials;
onPress: () => void;
onClick: () => void;
user: MinecraftCredentials;
loggedIn?: boolean;
}) {
return (
<AriaButton
className={twMerge(
'flex flex-row items-center justify-start p-1 gap-2 hover:bg-component-bg-hover pressed:bg-component-bg-pressed rounded-lg',
selected && 'bg-brand/80 pointer-events-none',
)}
onPress={onPress}
slot="close"
<div
className={twMerge('flex flex-row justify-between p-2 rounded-lg', !loggedIn && 'hover:bg-component-bg-hover active:bg-component-bg-pressed hover:text-fg-primary-hover')}
onClick={onClick}
>
<AccountAvatar className="aspect-square h-8 rounded-md" uuid={profile.id} />
<div className="text-left flex flex-col">
<p className="flex items-center gap-1 text-fg-primary font-medium">{profile.username}</p>
<div className="flex flex-1 flex-row justify-start gap-x-3">
<AccountAvatar className="h-8 w-8 rounded-md" uuid={user.id} />
<div className="flex flex-col items-center justify-center">
<div className="flex flex-col items-start justify-between">
<p className="h-[18px] font-semibold">{user.username}</p>
{loggedIn && <p className="text-xs">Logged in</p>}
</div>
</div>
</div>
</AriaButton>
</div>
);
}

View file

@ -18,7 +18,7 @@ const popupVariants = tv({
],
dialog: [
'flex flex-col items-center p-1 gap-2 relative',
'border border-component-border-hover bg-page-elevated shadow-md rounded-lg',
'border border-component-border-hover bg-page-elevated shadow-md rounded-xl',
],
},
});