Complete private authentication, server integrations and activity reporting
Add Fabric/Forge version builds and Paper integration, preserve permanent player identities across renames, harden session authorization, and surface privacy-conscious launcher/server activity in the panel.
This commit is contained in:
commit
a1f19e86c6
74 files changed
+2108
-95
No files matched your search
@@ -13,7 +13,16 @@ export function mockEmit(event: string, payload: unknown) {
|
||||
}
|
||||
|
||||
export async function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
|
||||
if (inTauri) return tauriInvoke<T>(cmd, args);
|
||||
if (inTauri) {
|
||||
const result = await tauriInvoke<T>(cmd, args);
|
||||
const kinds: Record<string, string> = {
|
||||
bootstrap: 'launcher_open', save_settings: 'settings_changed', select_account: 'account_selected',
|
||||
cancel_launch: 'launch_cancelled', delete_instance_data: 'instance_deleted', clear_cache: 'cache_cleared',
|
||||
install_update: 'update_installed', open_folder: 'folder_opened',
|
||||
};
|
||||
if (kinds[cmd]) void tauriInvoke('record_activity', { kind: kinds[cmd], instanceId: args?.instanceId ?? null }).catch(() => {});
|
||||
return result;
|
||||
}
|
||||
const { mockInvoke } = await import('./mock');
|
||||
return mockInvoke(cmd, args ?? {}) as Promise<T>;
|
||||
}
|
||||
@@ -32,7 +41,10 @@ export async function windowAction(action: 'minimize' | 'maximize' | 'close' | '
|
||||
const w = getCurrentWindow();
|
||||
if (action === 'minimize') await w.minimize();
|
||||
else if (action === 'maximize') await w.toggleMaximize();
|
||||
else if (action === 'close') await w.close();
|
||||
else if (action === 'close') {
|
||||
await tauriInvoke('record_activity', { kind: 'launcher_close', instanceId: null }).catch(() => {});
|
||||
await w.close();
|
||||
}
|
||||
else await w.startDragging();
|
||||
}
|
||||
|
||||
@@ -43,6 +55,7 @@ export async function openUrl(url: string) {
|
||||
}
|
||||
const { openUrl } = await import('@tauri-apps/plugin-opener');
|
||||
await openUrl(url);
|
||||
void tauriInvoke('record_activity', { kind: 'link_opened', instanceId: null }).catch(() => {});
|
||||
}
|
||||
|
||||
export async function pickFile(title: string, filters?: { name: string; extensions: string[] }[]): Promise<string | null> {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import { BIND_GROUPS, defaults, fromKeyboard, fromMouse, keyLabel } from '../lib/keys';
|
||||
import { bytes, gb } from '../lib/format';
|
||||
import { errorText, invoke, openUrl, pickFile } from '../lib/tauri';
|
||||
import type { Gc, PlayerProfile, UpdateInfo } from '../lib/types';
|
||||
import type { Account, Gc, PlayerProfile, UpdateInfo } from '../lib/types';
|
||||
|
||||
const serverAccount = $derived(activeAccount()?.kind === 'panel');
|
||||
const tabs = $derived([
|
||||
@@ -33,6 +33,21 @@
|
||||
const advanced = $derived(b?.features.allow_advanced_java !== false);
|
||||
const themeAllowed = $derived(b?.features.allow_user_theme !== false);
|
||||
const kindLabel = { panel: 'Server account', offline: 'Offline' };
|
||||
let newUsername = $state('');
|
||||
let renamePassword = $state('');
|
||||
let renaming = $state(false);
|
||||
async function renameAccount() {
|
||||
renaming = true;
|
||||
try {
|
||||
const account = await invoke<Account>('set_username', { username: newUsername, password: renamePassword });
|
||||
app.accounts = app.accounts.map((a) => a.id === account.id ? account : a);
|
||||
newUsername = '';
|
||||
renamePassword = '';
|
||||
await refresh();
|
||||
toast('Username updated. Your UUID and inventory are unchanged.');
|
||||
} catch (e) { toast(errorText(e), 'error'); }
|
||||
finally { renamePassword = ''; renaming = false; }
|
||||
}
|
||||
|
||||
// ---- controls ----
|
||||
let capturing = $state<string | null>(null);
|
||||
@@ -205,6 +220,13 @@
|
||||
</div>
|
||||
{:else if app.settingsTab === 'skin'}
|
||||
<h1>Skin & cape</h1>
|
||||
<form class="card glass col" onsubmit={(e) => { e.preventDefault(); renameAccount(); }}>
|
||||
<h3>Username</h3>
|
||||
<p class="muted small">Your UUID, inventory and server progress stay with your account. Close Minecraft first. Previous names remain reserved to you.</p>
|
||||
<label>New username<input bind:value={newUsername} required minlength="3" maxlength="16" pattern="[A-Za-z0-9_]+" autocomplete="username" /></label>
|
||||
<label>Current password<input bind:value={renamePassword} required type="password" autocomplete="current-password" /></label>
|
||||
<button disabled={renaming || !!app.running} type="submit">{renaming ? 'Updating…' : 'Change username'}</button>
|
||||
</form>
|
||||
<p class="lead">Stored on {b?.name ?? 'the server'} — visible to everyone on servers that use its sign-in.</p>
|
||||
{#if profileError}
|
||||
<div class="card glass"><p class="warn small">{profileError}</p></div>
|
||||
@@ -396,7 +418,7 @@
|
||||
</div>
|
||||
<div class="card glass col">
|
||||
<Toggle bind:checked={s.check_updates} onchange={saveSettings} label="Check for launcher updates" />
|
||||
<Toggle bind:checked={s.send_stats} onchange={saveSettings} label="Share play stats with the server" help="Lets your admins see which instances are popular. Only sends the instance and your username." />
|
||||
<p class="help">Official server accounts report launcher activity and gameplay to the panel, linked to your permanent UUID. Chat is counted; message text, passwords and command arguments are not collected.</p>
|
||||
</div>
|
||||
{:else if app.settingsTab === 'storage'}
|
||||
<h1>Storage</h1>
|
||||
|
||||
Reference in new issue
Block a user