feat: Common delete modal + delete modal when removing accounts + remove default user if the user being removed is the default

This commit is contained in:
LynithDev 2024-07-29 14:30:24 +02:00
parent ca636e65c0
commit af1ea68a32
6 changed files with 137 additions and 69 deletions

View file

@ -1,7 +1,6 @@
use std::path::PathBuf;
use interpulse::api::minecraft::Version;
use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION};
use onelauncher::data::{Loader, ManagedPackage, MinecraftCredentials, PackageData, Settings};
use onelauncher::package::content;
use onelauncher::store::{Cluster, ClusterPath};

View file

@ -5,7 +5,7 @@ import type {
Ref,
Setter,
} from 'solid-js';
import { For } from 'solid-js';
import { For, createEffect, createSignal, on } from 'solid-js';
import { mergeRefs } from '@solid-primitives/refs';
import Button from '../base/Button';
import FullscreenOverlay from './FullscreenOverlay';
@ -67,6 +67,110 @@ Modal.Simple = function (props: ModalSimpleProps) {
);
};
type ModalDeleteProps = MakeOptional<ModalSimpleProps, 'title'> & {
onCancel?: () => void;
onDelete?: () => void;
timeLeft?: number;
};
Modal.Delete = function (props: ModalDeleteProps) {
const [timeLeft, setTimeLeft] = createSignal(1);
const [intervalId, setIntervalId] = createSignal<NodeJS.Timeout | undefined>(undefined);
createEffect(on(() => props.visible(), (visible) => {
startInterval(visible);
if (visible !== true)
onCancel();
}));
function clearIntervalId() {
if (intervalId())
clearInterval(intervalId());
}
function startInterval(visible: boolean) {
if (visible !== true)
return;
clearIntervalId();
setTimeLeft(props.timeLeft || 3);
const intervalId = setInterval(() => {
setTimeLeft((prev) => {
const next = prev - 1;
if (next <= 0)
clearIntervalId();
return next;
});
}, 1000);
setIntervalId(intervalId);
}
function onCancel() {
if (props.visible() === true)
props.setVisible(false);
if (props.onCancel)
props.onCancel();
}
function onDelete() {
if (props.visible() === true)
props.setVisible(false);
if (props.onDelete)
props.onDelete();
}
return (
<Modal.Simple
title={props.title || 'Confirm Delete'}
ref={props.ref}
setVisible={props.setVisible}
visible={props.visible}
mount={props.mount}
zIndex={props.zIndex}
buttons={props.buttons || [
<Button
buttonStyle="secondary"
children="Cancel"
onClick={onCancel}
/>,
<Button
buttonStyle="danger"
children={`Delete${timeLeft() > 0 ? ` (${timeLeft()}s)` : ''}`}
disabled={timeLeft() > 0}
onClick={onDelete}
/>,
]}
>
{props.children || (
<>
<div class="flex flex-col justify-center items-center gap-y-3">
<p>Are you sure you want to delete this item?</p>
<p class="text-danger uppercase w-82 line-height-normal">
Doing this will
{' '}
<span class="underline font-bold">delete</span>
{' '}
your entire
{' '}
<br />
data
{' '}
<span class="underline font-bold">FOREVER</span>
.
</p>
</div>
</>
)}
</Modal.Simple>
);
};
function ModalButton(button: { props: ModalButtonProps }) {
return (
<>

View file

@ -12,7 +12,7 @@ interface AccountControllerContextFunc {
accounts: Resource<MinecraftCredentials[]>;
defaultAccount: Resource<MinecraftCredentials | null>;
setDefaultAccount: (uuid: string) => Promise<void>;
removeAccount: (uuid: string) => Promise<void>;
removeAccount: (uuid: string, force?: boolean) => Promise<void>;
}
const AccountControllerContext = createContext<AccountControllerContextFunc>() as Context<AccountControllerContextFunc>;
@ -22,6 +22,9 @@ export function AccountControllerProvider(props: ParentProps) {
const [defaultAccount, { refetch: refetchDefaultAccount }] = useCommand(bridge.commands.getDefaultUser);
const [visible, setVisible] = createSignal(false);
const [deleteModalUuid, setDeleteModalUuid] = createSignal<string>();
const deleteModalVisible = () => deleteModalUuid() !== undefined;
function refetch() {
refetchAccounts();
refetchDefaultAccount();
@ -31,7 +34,17 @@ export function AccountControllerProvider(props: ParentProps) {
await tryResult(bridge.commands.setDefaultUser, uuid);
}
async function removeAccount(uuid: string) {
async function removeAccount(uuid: string, force?: boolean) {
if (force !== true)
setDeleteModalUuid(uuid);
else
await _forceRemoveAccount(uuid);
}
async function _forceRemoveAccount(uuid: string | undefined) {
if (uuid === undefined)
return;
await tryResult(bridge.commands.removeUser, uuid);
refetch();
}
@ -49,6 +62,12 @@ export function AccountControllerProvider(props: ParentProps) {
<AccountControllerContext.Provider value={func}>
{props.children}
<AddAccountModal visible={visible} setVisible={setVisible} refetch={refetch} />
<Modal.Delete
setVisible={value => setDeleteModalUuid(value ? deleteModalUuid() : undefined)}
visible={deleteModalVisible}
onDelete={() => _forceRemoveAccount(deleteModalUuid())}
/>
</AccountControllerContext.Provider>
);
}

View file

@ -1,5 +1,5 @@
import { PlayIcon, Share07Icon, Trash01Icon } from '@untitled-theme/icons-solid';
import { type Accessor, type Setter, createEffect, createSignal, on } from 'solid-js';
import { createSignal } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import ClusterCover from '../../components/game/ClusterCover';
import LoaderIcon from '../../components/game/LoaderIcon';
@ -30,76 +30,15 @@ function ClusterOverview() {
<Banner {...cluster} showDeletePrompt={() => setDeleteVisible(true)} />
</ScrollableContainer>
<DeleteModal
<Modal.Delete
visible={deleteVisible}
setVisible={setDeleteVisible}
deleteCluster={deleteCluster}
onDelete={deleteCluster}
/>
</Sidebar.Page>
);
}
interface DeleteModalProps {
visible: Accessor<boolean>;
setVisible: Setter<boolean>;
deleteCluster: () => void;
};
function DeleteModal(props: DeleteModalProps) {
const [timeLeft, setTimeLeft] = createSignal(3);
createEffect(on(() => props.visible(), (visible) => {
if (visible !== true)
return;
setTimeLeft(3);
const interval = setInterval(() => {
setTimeLeft((prev) => {
const next = prev - 1;
if (next <= 0)
clearInterval(interval);
return next;
});
}, 1000);
}));
return (
<Modal.Simple
visible={props.visible}
setVisible={props.setVisible}
title="Delete Cluster"
buttons={[
<Button
buttonStyle="secondary"
children="Cancel"
onClick={() => props.setVisible(false)}
/>,
<Button
buttonStyle="danger"
children={`Delete${timeLeft() > 0 ? ` (${timeLeft()}s)` : ''}`}
disabled={timeLeft() > 0}
onClick={props.deleteCluster}
/>,
]}
>
<div class="flex flex-col justify-center items-center gap-y-3">
<p>Are you sure you want to delete this cluster?</p>
<p class="text-danger uppercase max-w-82 line-height-normal">
Doing this will
{' '}
<span class="underline font-bold">delete</span>
{' '}
your entire cluster data
{' '}
<span class="underline font-bold">FOREVER</span>
.
</p>
</div>
</Modal.Simple>
);
}
function Banner(props: Cluster & { showDeletePrompt: () => void }) {
async function launch() {
const [_uuid, _pid] = await tryResult(bridge.commands.runCluster, props.uuid);

View file

@ -12,7 +12,9 @@ async fn main() -> onelauncher::Result<()> {
let _logger = onelauncher::start_logger();
let _state = State::get().await?;
modrinth().await?;
// modrinth().await?;
// launch_and_authenticate().await?;
authenticate_mc().await?;
Ok(())
}

View file

@ -305,6 +305,11 @@ impl MinecraftState {
#[tracing::instrument(skip(self))]
pub async fn remove(&mut self, id: Uuid) -> crate::Result<Option<MinecraftCredentials>> {
let val = self.users.remove(&id);
if self.default_user == Some(id) {
self.default_user = None;
}
self.save().await?;
Ok(val)
}