Added account deletion

This commit is contained in:
WardPearce
2026-02-14 22:09:23 +13:00
parent f839c9fb08
commit a2e299a759
4 changed files with 100 additions and 22 deletions
@@ -0,0 +1,38 @@
<script lang="ts">
import { _ } from '$lib/i18n';
import { logout } from '$lib/misc';
let clickCount = $state(0);
const clicksToDelte = 3;
let deleteDebounce: ReturnType<typeof setTimeout> | undefined;
async function deleteAccount() {
clickCount++;
if (deleteDebounce) clearTimeout(deleteDebounce);
deleteDebounce = setTimeout(() => {
clickCount = 0;
}, 10000);
if (clicksToDelte - clickCount === 0) {
await fetch('/api/user/delete', { method: 'DELETE' });
logout();
}
}
</script>
<h6>Passsword reset</h6>
<h6>Delete account</h6>
<div class="space"></div>
<button class="tertiary" onclick={deleteAccount}>
<i>warning</i>
<span>{$_('layout.deleteAccount')}</span>
{#if clicksToDelte - clickCount > 0}
<div class="tooltip">
{$_('layout.clickXmoreTimesToDelete', {
clicksTillDelete: clicksToDelte - clickCount
})}
</div>
{/if}
</button>
@@ -7,15 +7,17 @@
import Player from './Player.svelte';
import Ryd from './RYD.svelte';
import SponsorBlock from './SponsorBlock.svelte';
import { isAndroidTvStore } from '$lib/store';
import { isAndroidTvStore, rawMasterKeyStore } from '$lib/store';
import About from './About.svelte';
import Engine from './Engine.svelte';
import { isUnrestrictedPlatform } from '$lib/misc';
import { isOwnBackend } from '$lib/shared';
import InternalAccount from './InternalAccount.svelte';
let activeTab = $state('interface');
const isActive = (id: string) => activeTab === id;
const tabs: { id: string; label: string; icon: string; component: Component }[] = [
let tabs: { id: string; label: string; icon: string; component: Component }[] = $state([
{ id: 'interface', label: $_('layout.interface'), icon: 'grid_view', component: Interface },
{ id: 'player', label: $_('layout.player.title'), icon: 'smart_display', component: Player },
{ id: 'ryd', label: 'Return YT Dislike', icon: 'thumb_down', component: Ryd },
@@ -33,7 +35,11 @@
icon: 'info',
component: About
}
];
]);
let tabIds: string[] = $state([]);
setTabIds();
if (isUnrestrictedPlatform()) {
tabs.splice(1, 0, {
@@ -44,7 +50,28 @@
});
}
const tabIds = tabs.map((tab) => tab.id);
function setTabIds() {
tabs.forEach((tab) => {
tabIds.push(tab.id);
});
}
rawMasterKeyStore.subscribe((value) => {
if (isOwnBackend()?.internalAuth && value) {
tabs.splice(1, 0, {
id: 'account',
label: $_('layout.materialiousAccount'),
icon: 'person',
component: InternalAccount
});
} else {
tabs = tabs.filter((tab) => {
return tab.id !== 'account';
});
}
setTabIds();
});
let dialogType = $state('');
@@ -102,24 +129,26 @@
<div style="height: 100%;">
<nav class="wrap s">
<button class="large small-round surface-container-highest max" data-ui="#tab-menu">
<i>{tabs[tabIds.indexOf(activeTab)].icon}</i>
<span>{tabs[tabIds.indexOf(activeTab)].label}</span>
<menu style="width: 100%;" data-ui="#tab-menu" id="tab-menu">
{#each tabs as tab (tab)}
<li
onclick={() => {
activeTab = tab.id;
}}
role="presentation"
data-ui="#tab-menu"
>
<i>{tab.icon}</i>
<span>{tab.label}</span>
</li>
{/each}
</menu>
</button>
{#if tabIds}
<button class="large small-round surface-container-highest max" data-ui="#tab-menu">
<i>{tabs[tabIds.indexOf(activeTab)]?.icon}</i>
<span>{tabs[tabIds.indexOf(activeTab)]?.label}</span>
<menu style="width: 100%;" data-ui="#tab-menu" id="tab-menu">
{#each tabs as tab (tab)}
<li
onclick={() => {
activeTab = tab.id;
}}
role="presentation"
data-ui="#tab-menu"
>
<i>{tab.icon}</i>
<span>{tab.label}</span>
</li>
{/each}
</menu>
</button>
{/if}
</nav>
<div class="s">
@@ -170,6 +170,7 @@
"engine": "Backend engine",
"syncParty": "Sync party",
"about": "About",
"materialiousAccount": "Account",
"syncPartyWarning": "Please note your IP will be visible to users you invite.",
"startSyncParty": "Start sync party",
"endSyncParty": "End sync party",
@@ -185,6 +186,8 @@
"displayThumbnailAvatars": "Thumbnail avatars (Slow)",
"searchHistory": "Search history",
"companionUrl": "Companion URL",
"deleteAccount": "Delete account",
"clickXmoreTimesToDelete": "Click {{clicksTillDelete}} more time(s) to delete",
"theme": {
"theme": "Theme",
"darkMode": "Dark mode",
@@ -0,0 +1,8 @@
import { getUser } from '$lib/server/user';
export async function DELETE({ locals }) {
const user = await getUser(locals.userId);
await user.delete();
return new Response();
}