feat: Refactor API interactions and enhance user settings management
- Updated ezpp API to include user info retrieval and improved error handling. - Introduced new writable stores for current user info and loading states. - Added gamemode enums and utility functions for better gamemode handling. - Refactored global state management to use consistent naming conventions. - Enhanced loading and login components to provide better user feedback. - Updated user settings to include preferred mode and type. - Improved layout and page components for better state management and user experience.
This commit is contained in:
parent
892f2cea07
commit
651592c333
@ -1,14 +1,22 @@
|
|||||||
import type { EZPPUser } from '@/types';
|
import type { EZPPUser, EZPPUserInfoResponse, EZPPUserResponse } from '@/types';
|
||||||
import { betterFetch } from '@better-fetch/fetch';
|
import { betterFetch } from '@better-fetch/fetch';
|
||||||
|
|
||||||
const BANCHO_ENDPOINT = 'https://c.ez-pp.farm/';
|
const BANCHO_ENDPOINT = 'https://c.ez-pp.farm/';
|
||||||
|
const API_ENDPOINT = 'https://api.ez-pp.farm/';
|
||||||
const ENDPOINT = 'https://ez-pp.farm/';
|
const ENDPOINT = 'https://ez-pp.farm/';
|
||||||
|
|
||||||
|
const timeout = 5000; // 5 seconds;
|
||||||
|
|
||||||
export const ezppfarm = {
|
export const ezppfarm = {
|
||||||
ping: async (): Promise<number | undefined> => {
|
ping: async (): Promise<number | undefined> => {
|
||||||
try {
|
try {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const request = await betterFetch(BANCHO_ENDPOINT);
|
const request = await betterFetch(BANCHO_ENDPOINT, {
|
||||||
|
timeout,
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'EZPPLauncher',
|
||||||
|
},
|
||||||
|
});
|
||||||
if (request.error) return undefined;
|
if (request.error) return undefined;
|
||||||
const ping = Date.now() - start;
|
const ping = Date.now() - start;
|
||||||
return ping;
|
return ping;
|
||||||
@ -23,32 +31,22 @@ export const ezppfarm = {
|
|||||||
| {
|
| {
|
||||||
code: number;
|
code: number;
|
||||||
message: string;
|
message: string;
|
||||||
user?: {
|
user?: EZPPUser;
|
||||||
id: number;
|
|
||||||
donor: boolean;
|
|
||||||
name: string;
|
|
||||||
email: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
| undefined
|
| undefined
|
||||||
> => {
|
> => {
|
||||||
const request = await betterFetch<{
|
const request = await betterFetch<EZPPUserResponse>(`${ENDPOINT}login/check`, {
|
||||||
code: number;
|
|
||||||
message: string;
|
|
||||||
user?: EZPPUser;
|
|
||||||
}>('https://ez-pp.farm/login/check', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
timeout,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
}),
|
}),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'User-Agent':
|
'User-Agent': 'EZPPLauncher',
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log(request.error);
|
|
||||||
if (request.error) {
|
if (request.error) {
|
||||||
if (request.error.status >= 500 && request.error.status < 600)
|
if (request.error.status >= 500 && request.error.status < 600)
|
||||||
throw new Error('Server not reachable');
|
throw new Error('Server not reachable');
|
||||||
@ -56,4 +54,18 @@ export const ezppfarm = {
|
|||||||
}
|
}
|
||||||
return request.data;
|
return request.data;
|
||||||
},
|
},
|
||||||
|
getUserInfo: async (userId: number) => {
|
||||||
|
const request = await betterFetch<EZPPUserInfoResponse>(`${API_ENDPOINT}v1/get_player_info`, {
|
||||||
|
timeout,
|
||||||
|
query: {
|
||||||
|
id: userId,
|
||||||
|
scope: 'all',
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': 'EZPPLauncher',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return request.error ? undefined : request.data;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
4
src/lib/data.ts
Normal file
4
src/lib/data.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import type { EZPPUserInfo } from './types';
|
||||||
|
|
||||||
|
export const currentUserInfo = writable<EZPPUserInfo | undefined>(undefined);
|
186
src/lib/gamemode.ts
Normal file
186
src/lib/gamemode.ts
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
export enum Gamemodes {
|
||||||
|
VANILLA_OSU = 0,
|
||||||
|
VANILLA_TAIKO = 1,
|
||||||
|
VANILLA_CATCH = 2,
|
||||||
|
VANILLA_MANIA = 3,
|
||||||
|
|
||||||
|
RELAX_OSU = 4,
|
||||||
|
RELAX_TAIKO = 5,
|
||||||
|
RELAX_CATCH = 6,
|
||||||
|
|
||||||
|
AUTOPILOT_OSU = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Mode {
|
||||||
|
OSU = 0,
|
||||||
|
TAIKO = 1,
|
||||||
|
CATCH = 2,
|
||||||
|
MANIA = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Type {
|
||||||
|
VANILLA = 0,
|
||||||
|
RELAX = 4,
|
||||||
|
AUTOPILOT = 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validModes = [Mode.OSU, Mode.TAIKO, Mode.CATCH, Mode.MANIA];
|
||||||
|
export const validTypes = [Type.VANILLA, Type.RELAX, Type.AUTOPILOT];
|
||||||
|
export const validModeTypeCombinations = [0, 1, 2, 3, 4, 5, 6, 8];
|
||||||
|
export const validModeTypeCombinationsSorted = [0, 4, 8, 1, 5, 2, 6, 3];
|
||||||
|
|
||||||
|
export const validMode = (modeStr: string) => modeStrToInt(modeStr) !== undefined;
|
||||||
|
export const validType = (typeStr: string) => typeStrToInt(typeStr) !== undefined;
|
||||||
|
|
||||||
|
export const modeStrToInt = (modeStr: 'osu' | 'taiko' | 'catch' | 'mania' | string) => {
|
||||||
|
switch (modeStr) {
|
||||||
|
case 'taiko':
|
||||||
|
return Mode.TAIKO;
|
||||||
|
case 'catch':
|
||||||
|
return Mode.CATCH;
|
||||||
|
case 'mania':
|
||||||
|
return Mode.MANIA;
|
||||||
|
case 'osu':
|
||||||
|
return Mode.OSU;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const modeIntToStr = (modeInt: number) => {
|
||||||
|
switch (modeInt) {
|
||||||
|
case Mode.TAIKO:
|
||||||
|
return 'taiko';
|
||||||
|
case Mode.CATCH:
|
||||||
|
return 'catch';
|
||||||
|
case Mode.MANIA:
|
||||||
|
return 'mania';
|
||||||
|
case Mode.OSU:
|
||||||
|
return 'osu';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typeStrToInt = (typeStr: 'vanilla' | 'relax' | 'autopilot' | string) => {
|
||||||
|
switch (typeStr) {
|
||||||
|
case 'relax':
|
||||||
|
return Type.RELAX;
|
||||||
|
case 'autopilot':
|
||||||
|
return Type.AUTOPILOT;
|
||||||
|
case 'vanilla':
|
||||||
|
return Type.VANILLA;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typeIntToStr = (typeInt: number) => {
|
||||||
|
switch (typeInt) {
|
||||||
|
case Type.RELAX:
|
||||||
|
return 'relax';
|
||||||
|
case Type.AUTOPILOT:
|
||||||
|
return 'autopilot';
|
||||||
|
case Type.VANILLA:
|
||||||
|
return 'vanilla';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGamemodeInt = (
|
||||||
|
mode: 'osu' | 'taiko' | 'catch' | 'mania' | string | undefined,
|
||||||
|
type: 'vanilla' | 'relax' | 'autopilot' | string | undefined
|
||||||
|
) => {
|
||||||
|
let modee = 0;
|
||||||
|
switch (mode) {
|
||||||
|
case 'taiko':
|
||||||
|
modee += Mode.TAIKO;
|
||||||
|
break;
|
||||||
|
case 'catch':
|
||||||
|
modee += Mode.CATCH;
|
||||||
|
break;
|
||||||
|
case 'mania':
|
||||||
|
modee += Mode.MANIA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'relax':
|
||||||
|
modee += Type.RELAX;
|
||||||
|
break;
|
||||||
|
case 'autopilot':
|
||||||
|
modee += Type.AUTOPILOT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modee;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getGamemodeName = (
|
||||||
|
mode: 'osu' | 'taiko' | 'catch' | 'mania' | string | undefined,
|
||||||
|
type: 'vanilla' | 'relax' | 'autopilot' | string | undefined
|
||||||
|
) => {
|
||||||
|
let modeStr = '';
|
||||||
|
switch (mode) {
|
||||||
|
case 'taiko':
|
||||||
|
modeStr += 'taiko!';
|
||||||
|
break;
|
||||||
|
case 'catch':
|
||||||
|
modeStr += 'catch!';
|
||||||
|
break;
|
||||||
|
case 'mania':
|
||||||
|
modeStr += 'mania!';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
modeStr += 'osu!';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'relax':
|
||||||
|
modeStr += 'rx';
|
||||||
|
break;
|
||||||
|
case 'autopilot':
|
||||||
|
modeStr += 'ap';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
modeStr += 'vn';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return modeStr;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getModeAndTypeFromGamemode = (gamemode: number) => {
|
||||||
|
let mode = Mode.OSU;
|
||||||
|
let type = Type.VANILLA;
|
||||||
|
const vanillaMode = gamemode % 4;
|
||||||
|
|
||||||
|
switch (vanillaMode) {
|
||||||
|
case Mode.TAIKO:
|
||||||
|
mode = Mode.TAIKO;
|
||||||
|
break;
|
||||||
|
case Mode.CATCH:
|
||||||
|
mode = Mode.CATCH;
|
||||||
|
break;
|
||||||
|
case Mode.MANIA:
|
||||||
|
mode = Mode.MANIA;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typee = gamemode - vanillaMode;
|
||||||
|
switch (typee) {
|
||||||
|
case Type.RELAX:
|
||||||
|
type = Type.RELAX;
|
||||||
|
break;
|
||||||
|
case Type.AUTOPILOT:
|
||||||
|
type = Type.AUTOPILOT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
mode,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isValidGamemode = (gamemodeInt: number) => {
|
||||||
|
return validModeTypeCombinations.includes(gamemodeInt);
|
||||||
|
};
|
@ -3,15 +3,18 @@ import { ezppfarm } from './api/ezpp';
|
|||||||
import type { Component } from 'svelte';
|
import type { Component } from 'svelte';
|
||||||
import Loading from '../pages/Loading.svelte';
|
import Loading from '../pages/Loading.svelte';
|
||||||
|
|
||||||
export const current_view = writable<Component>(Loading);
|
export const currentView = writable<Component>(Loading);
|
||||||
export const first_startup = writable<boolean>(false);
|
|
||||||
|
|
||||||
export const server_ping = writable<number | undefined>(undefined);
|
export const currentLoadingInfo = writable<string>('Initializing...');
|
||||||
export const server_connection_fails = writable(0);
|
|
||||||
|
|
||||||
export const online_friends = writable<number | undefined>(undefined);
|
export const firstStartup = writable<boolean>(false);
|
||||||
|
|
||||||
export const beatmap_sets = writable<number | undefined>(undefined);
|
export const serverPing = writable<number | undefined>(undefined);
|
||||||
|
export const serverConnectionFails = writable(0);
|
||||||
|
|
||||||
|
export const onlineFriends = writable<number | undefined>(undefined);
|
||||||
|
|
||||||
|
export const beatmapSets = writable<number | undefined>(undefined);
|
||||||
|
|
||||||
export const setupValues = () => {
|
export const setupValues = () => {
|
||||||
updatePing();
|
updatePing();
|
||||||
@ -27,23 +30,19 @@ export const setupValues = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const updatePing = async () => {
|
const updatePing = async () => {
|
||||||
const serverPing = await ezppfarm.ping();
|
const currentServerPing = await ezppfarm.ping();
|
||||||
if (!serverPing) {
|
if (!currentServerPing) {
|
||||||
server_connection_fails.update((num) => num + 1);
|
serverConnectionFails.update((num) => num + 1);
|
||||||
} else {
|
} else {
|
||||||
server_connection_fails.set(0);
|
serverConnectionFails.set(0);
|
||||||
server_ping.set(serverPing);
|
serverPing.set(currentServerPing);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateFriends = async () => {
|
const updateFriends = async () => {
|
||||||
await new Promise((res) => setTimeout(res, Math.random() * 300));
|
await new Promise((res) => setTimeout(res, Math.random() * 300));
|
||||||
const onlineFriends = Math.round(Math.random() * 10);
|
const currentOnlineFriends = Math.round(Math.random() * 10);
|
||||||
online_friends.set(onlineFriends);
|
onlineFriends.set(currentOnlineFriends);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateBeatmapSets = async () => {
|
const updateBeatmapSets = async () => {};
|
||||||
await new Promise((res) => setTimeout(res, Math.random() * 1500));
|
|
||||||
const beatmapSets = Math.round(Math.random() * 5000);
|
|
||||||
beatmap_sets.set(beatmapSets);
|
|
||||||
};
|
|
||||||
|
@ -1,6 +1,97 @@
|
|||||||
|
export type EZPPUserResponse = {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
user?: EZPPUser;
|
||||||
|
};
|
||||||
|
|
||||||
export type EZPPUser = {
|
export type EZPPUser = {
|
||||||
id: number;
|
id: number;
|
||||||
donor: boolean;
|
donor: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EZPPUserInfoResponse = {
|
||||||
|
status: string;
|
||||||
|
player: EZPPUserInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EZPPUserInfo = {
|
||||||
|
info: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
safe_name: string;
|
||||||
|
priv: number;
|
||||||
|
country: string;
|
||||||
|
silence_end: number;
|
||||||
|
donor_end: number;
|
||||||
|
creation_time: number;
|
||||||
|
latest_activity: number;
|
||||||
|
clan_id: number;
|
||||||
|
clan_priv: number;
|
||||||
|
preferred_mode: number;
|
||||||
|
preferred_type: number;
|
||||||
|
play_style: number;
|
||||||
|
custom_badge_enabled: number;
|
||||||
|
custom_badge_name: string;
|
||||||
|
custom_badge_icon: string;
|
||||||
|
custom_badge_color: string;
|
||||||
|
userpage_content: string;
|
||||||
|
recentFailed: number;
|
||||||
|
social_discord: string;
|
||||||
|
social_youtube: string;
|
||||||
|
social_twitter: string;
|
||||||
|
social_twitch: string;
|
||||||
|
social_github: string;
|
||||||
|
social_osu: string;
|
||||||
|
clan: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
tag: string;
|
||||||
|
owner: number;
|
||||||
|
created_at: Date;
|
||||||
|
};
|
||||||
|
username_history: string[];
|
||||||
|
};
|
||||||
|
stats: {
|
||||||
|
[key: string]: {
|
||||||
|
id: number;
|
||||||
|
mode: number;
|
||||||
|
tscore: number;
|
||||||
|
rscore: number;
|
||||||
|
pp: number;
|
||||||
|
plays: number;
|
||||||
|
playtime: number;
|
||||||
|
acc: number;
|
||||||
|
max_combo: number;
|
||||||
|
total_hits: number;
|
||||||
|
replay_views: number;
|
||||||
|
xh_count: number;
|
||||||
|
x_count: number;
|
||||||
|
sh_count: number;
|
||||||
|
s_count: number;
|
||||||
|
a_count: number;
|
||||||
|
level: number;
|
||||||
|
level_progress: number;
|
||||||
|
rank: number;
|
||||||
|
country_rank: number;
|
||||||
|
history: {
|
||||||
|
pp: number[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
events: {
|
||||||
|
userId: number;
|
||||||
|
name: string;
|
||||||
|
mapId: number;
|
||||||
|
setId: number;
|
||||||
|
artist: string;
|
||||||
|
title: string;
|
||||||
|
version: string;
|
||||||
|
mode: number;
|
||||||
|
rank: number;
|
||||||
|
grade: string;
|
||||||
|
event: 'GAINED' | 'LOST';
|
||||||
|
time: Date;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
@ -3,9 +3,12 @@ import { Config } from './config';
|
|||||||
|
|
||||||
export const userSettings = writable<Config>(new Config('user_settings', false));
|
export const userSettings = writable<Config>(new Config('user_settings', false));
|
||||||
|
|
||||||
export const customCursor = writable<boolean>(true);
|
export const customCursor = writable<boolean>(false);
|
||||||
export const cursorSmoothening = writable<boolean>(true);
|
export const cursorSmoothening = writable<boolean>(false);
|
||||||
export const cursorSmoothness = writable<number>(180);
|
export const cursorSmoothness = writable<number>(180);
|
||||||
export const reduceAnimations = writable<boolean>(false);
|
export const reduceAnimations = writable<boolean>(false);
|
||||||
|
|
||||||
export const osuInstallationPath = writable<string>('');
|
export const osuInstallationPath = writable<string>('');
|
||||||
|
|
||||||
|
export const preferredMode = writable<number>(0);
|
||||||
|
export const preferredType = writable<number>(0);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { createAudioStore } from "@elron/svelte-audio-store";
|
import { createAudioStore } from '@elron/svelte-audio-store';
|
||||||
import { type ClassValue, clsx } from "clsx";
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
const sounds = {
|
const sounds = {
|
||||||
menuHeartbeat: "/audio/menuHeartbeat.mp3",
|
menuHeartbeat: '/audio/menuHeartbeat.mp3',
|
||||||
menuBack: "/audio/menuBack.wav",
|
menuBack: '/audio/menuBack.wav',
|
||||||
menuHit: "/audio/menuHit.wav",
|
menuHit: '/audio/menuHit.wav',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gameSounds = createAudioStore(sounds);
|
export const gameSounds = createAudioStore(sounds);
|
||||||
@ -23,3 +23,30 @@ export const playAudio = (path: string, volume: number) => {
|
|||||||
audio.volume = volume;
|
audio.volume = volume;
|
||||||
audio.play();
|
audio.play();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isNumber = (value: unknown) => {
|
||||||
|
if (typeof value === 'number' || typeof value === 'string') {
|
||||||
|
return value.toString().match(/^-?\d+(\.\d+)?$/) !== null;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatTimeReadable = (initialSeconds: number) => {
|
||||||
|
let seconds = initialSeconds;
|
||||||
|
|
||||||
|
const days = Math.floor(seconds / (24 * 3600));
|
||||||
|
seconds -= days * 24 * 3600;
|
||||||
|
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
seconds -= hours * 3600;
|
||||||
|
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
if (days > 0) result += `${days}d `;
|
||||||
|
if (hours > 0) result += `${hours}h `;
|
||||||
|
result += `${minutes}m`;
|
||||||
|
|
||||||
|
return result.trim();
|
||||||
|
};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import Badge from '@/components/ui/badge/badge.svelte';
|
import Badge from '@/components/ui/badge/badge.svelte';
|
||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
import * as Select from '@/components/ui/select';
|
import * as Select from '@/components/ui/select';
|
||||||
import { beatmap_sets, current_view, server_connection_fails, server_ping } from '@/global';
|
import { beatmapSets, currentView, serverConnectionFails, serverPing } from '@/global';
|
||||||
import {
|
import {
|
||||||
LoaderCircle,
|
LoaderCircle,
|
||||||
Logs,
|
Logs,
|
||||||
@ -14,19 +14,26 @@
|
|||||||
Gamepad2,
|
Gamepad2,
|
||||||
WifiOff,
|
WifiOff,
|
||||||
Settings2,
|
Settings2,
|
||||||
|
Drum,
|
||||||
|
Cherry,
|
||||||
|
Piano,
|
||||||
|
Circle,
|
||||||
|
LogOut,
|
||||||
|
LogIn,
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
import { Circle } from 'radix-icons-svelte';
|
|
||||||
import NumberFlow from '@number-flow/svelte';
|
import NumberFlow from '@number-flow/svelte';
|
||||||
import * as AlertDialog from '@/components/ui/alert-dialog';
|
import * as AlertDialog from '@/components/ui/alert-dialog';
|
||||||
import Progress from '@/components/ui/progress/progress.svelte';
|
import Progress from '@/components/ui/progress/progress.svelte';
|
||||||
import { numberHumanReadable } from '@/utils';
|
import { formatTimeReadable, numberHumanReadable } from '@/utils';
|
||||||
import { scale } from 'svelte/transition';
|
import { fade, scale } from 'svelte/transition';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import Label from '@/components/ui/label/label.svelte';
|
import Label from '@/components/ui/label/label.svelte';
|
||||||
import {
|
import {
|
||||||
cursorSmoothening,
|
cursorSmoothening,
|
||||||
customCursor,
|
customCursor,
|
||||||
osuInstallationPath,
|
osuInstallationPath,
|
||||||
|
preferredMode,
|
||||||
|
preferredType,
|
||||||
reduceAnimations,
|
reduceAnimations,
|
||||||
userSettings,
|
userSettings,
|
||||||
} from '@/userSettings';
|
} from '@/userSettings';
|
||||||
@ -35,11 +42,30 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import Login from './Login.svelte';
|
import Login from './Login.svelte';
|
||||||
import { currentUser } from '@/userAuthentication';
|
import { currentUser, userAuth } from '@/userAuthentication';
|
||||||
|
import {
|
||||||
|
getGamemodeInt,
|
||||||
|
getGamemodeName,
|
||||||
|
getModeAndTypeFromGamemode,
|
||||||
|
modeIntToStr,
|
||||||
|
typeIntToStr,
|
||||||
|
validModeTypeCombinationsSorted,
|
||||||
|
} from '@/gamemode';
|
||||||
|
import { currentUserInfo } from '@/data';
|
||||||
|
|
||||||
let selectedTab = $state('home');
|
let selectedTab = $state('home');
|
||||||
let launching = $state(false);
|
let launching = $state(false);
|
||||||
|
|
||||||
|
let selectedGamemode = $derived(
|
||||||
|
getGamemodeInt(modeIntToStr($preferredMode), typeIntToStr($preferredType))
|
||||||
|
);
|
||||||
|
let selectedMode = $derived(getModeAndTypeFromGamemode(selectedGamemode).mode);
|
||||||
|
let selectedType = $derived(getModeAndTypeFromGamemode(selectedGamemode).type);
|
||||||
|
|
||||||
|
const updateGamemode = (newGamemode: string) => {
|
||||||
|
selectedGamemode = Number(newGamemode);
|
||||||
|
};
|
||||||
|
|
||||||
const browse_osu_installation = async () => {
|
const browse_osu_installation = async () => {
|
||||||
const selectedPath = await open({
|
const selectedPath = await open({
|
||||||
directory: true,
|
directory: true,
|
||||||
@ -48,9 +74,9 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (typeof selectedPath === 'string') {
|
if (typeof selectedPath === 'string') {
|
||||||
/* if (selectedPath === $osuInstallationPath) {
|
if (selectedPath === $osuInstallationPath) {
|
||||||
return;
|
return;
|
||||||
} */
|
}
|
||||||
const validFolder: boolean = await invoke('valid_osu_folder', { folder: selectedPath });
|
const validFolder: boolean = await invoke('valid_osu_folder', { folder: selectedPath });
|
||||||
if (!validFolder) {
|
if (!validFolder) {
|
||||||
toast.error(
|
toast.error(
|
||||||
@ -62,6 +88,13 @@
|
|||||||
$userSettings.value('osu_installation_path').set(selectedPath);
|
$userSettings.value('osu_installation_path').set(selectedPath);
|
||||||
$userSettings.save();
|
$userSettings.save();
|
||||||
toast.success('osu! installation path set successfully.');
|
toast.success('osu! installation path set successfully.');
|
||||||
|
|
||||||
|
const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', {
|
||||||
|
folder: selectedPath,
|
||||||
|
});
|
||||||
|
if (beatmapSetCount) {
|
||||||
|
beatmapSets.set(beatmapSetCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -94,27 +127,92 @@
|
|||||||
<div class="flex flex-row gap-2">
|
<div class="flex flex-row gap-2">
|
||||||
<!-- <Badge variant="destructive">Owner</Badge> -->
|
<!-- <Badge variant="destructive">Owner</Badge> -->
|
||||||
{#if !$currentUser}
|
{#if !$currentUser}
|
||||||
<Button variant="outline" size="sm" onclick={() => current_view.set(Login)}>Login</Button>
|
<Button variant="outline" size="sm" onclick={() => currentView.set(Login)}>
|
||||||
|
<LogIn size={16} />
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
{:else}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onclick={async () => {
|
||||||
|
$userAuth.value('username').del();
|
||||||
|
$userAuth.value('password').del();
|
||||||
|
await $userAuth.save();
|
||||||
|
toast.success('Logout successful!', {
|
||||||
|
description: 'See you soon!',
|
||||||
|
});
|
||||||
|
currentUser.set(undefined);
|
||||||
|
currentUserInfo.set(undefined);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LogOut size={16} />
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if $currentUser}
|
||||||
<div class="flex flex-col gap-6 h-full px-3">
|
<div class="flex flex-col gap-6 h-full px-3">
|
||||||
<Select.Root type="single">
|
<div
|
||||||
|
in:scale={{ duration: $reduceAnimations ? 0 : 400, start: 0.98 }}
|
||||||
|
out:scale={{ duration: $reduceAnimations ? 0 : 400, start: 0.98 }}
|
||||||
|
>
|
||||||
|
<Select.Root
|
||||||
|
type="single"
|
||||||
|
value={selectedGamemode.toFixed()}
|
||||||
|
onValueChange={updateGamemode}
|
||||||
|
>
|
||||||
<Select.Trigger
|
<Select.Trigger
|
||||||
class="border-theme-800/90 bg-theme-900/90 !text-muted-foreground font-semibold"
|
class="border-theme-800/90 bg-theme-900/90 !text-muted-foreground font-semibold"
|
||||||
>
|
>
|
||||||
<div class="flex flex-row items-center gap-2">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<Circle class="text-muted-foreground" />
|
{#if selectedMode === 0}
|
||||||
osu!vn
|
<Circle size={16} class="text-theme-200" />
|
||||||
|
{:else if selectedMode === 1}
|
||||||
|
<Drum size={16} class="text-theme-200" />
|
||||||
|
{:else if selectedMode === 2}
|
||||||
|
<Cherry size={16} class="text-theme-200" />
|
||||||
|
{:else if selectedMode === 3}
|
||||||
|
<Piano size={16} class="text-theme-200" />
|
||||||
|
{/if}
|
||||||
|
{getGamemodeName(modeIntToStr(selectedMode), typeIntToStr(selectedType))}
|
||||||
</div>
|
</div>
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
<Select.Content class="bg-theme-950 border border-theme-900 rounded-lg">
|
<Select.Content class="bg-theme-950 border border-theme-900 rounded-lg">
|
||||||
<Select.Item value="light">osu!vn</Select.Item>
|
{#each validModeTypeCombinationsSorted as gamemode}
|
||||||
<Select.Item value="dark">osu!rx</Select.Item>
|
{@const gamemod = getModeAndTypeFromGamemode(gamemode)}
|
||||||
<Select.Item value="system">osu!ap</Select.Item>
|
<Select.Item value={gamemode.toFixed()}>
|
||||||
|
<div class="flex flex-row gap-2 items-center">
|
||||||
|
{#if gamemod.mode === 0}
|
||||||
|
<Circle size={16} class="text-theme-200" />
|
||||||
|
{:else if gamemod.mode === 1}
|
||||||
|
<Drum size={16} class="text-theme-200" />
|
||||||
|
{:else if gamemod.mode === 2}
|
||||||
|
<Cherry size={16} class="text-theme-200" />
|
||||||
|
{:else if gamemod.mode === 3}
|
||||||
|
<Piano size={16} class="text-theme-200" />
|
||||||
|
{/if}
|
||||||
|
{getGamemodeName(modeIntToStr(gamemod.mode), typeIntToStr(gamemod.type))}
|
||||||
|
</div>
|
||||||
|
</Select.Item>
|
||||||
|
{/each}
|
||||||
</Select.Content>
|
</Select.Content>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
<div class="bg-theme-900/90 border border-theme-800/90 rounded-lg p-2">
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-theme-900/90 border border-theme-800/90 rounded-lg p-2"
|
||||||
|
in:scale={{
|
||||||
|
duration: $reduceAnimations ? 0 : 400,
|
||||||
|
delay: $reduceAnimations ? 0 : 50,
|
||||||
|
start: 0.98,
|
||||||
|
}}
|
||||||
|
out:scale={{
|
||||||
|
duration: $reduceAnimations ? 0 : 400,
|
||||||
|
delay: $reduceAnimations ? 0 : 50,
|
||||||
|
start: 0.98,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class="flex flex-row items-center gap-2">
|
<div class="flex flex-row items-center gap-2">
|
||||||
<Logs class="text-muted-foreground" size="16" />
|
<Logs class="text-muted-foreground" size="16" />
|
||||||
<span class="font-semibold text-muted-foreground text-sm">Mode Stats</span>
|
<span class="font-semibold text-muted-foreground text-sm">Mode Stats</span>
|
||||||
@ -122,22 +220,88 @@
|
|||||||
<div class="grid grid-cols-2 mt-2 border-t border-theme-800 pt-2 pb-2">
|
<div class="grid grid-cols-2 mt-2 border-t border-theme-800 pt-2 pb-2">
|
||||||
<div class="flex flex-col gap-0.5">
|
<div class="flex flex-col gap-0.5">
|
||||||
<span class="text-sm text-muted-foreground font-semibold">Rank</span>
|
<span class="text-sm text-muted-foreground font-semibold">Rank</span>
|
||||||
<span class="text-lg font-semibold text-theme-50">#727</span>
|
<div class="flex items-center h-full text-lg font-semibold text-theme-50">
|
||||||
|
{#if $currentUserInfo}
|
||||||
|
<div in:fade>
|
||||||
|
<NumberFlow
|
||||||
|
trend={0}
|
||||||
|
prefix="#"
|
||||||
|
value={$currentUserInfo.stats[selectedGamemode].rank ?? 0}
|
||||||
|
></NumberFlow>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div in:fade>
|
||||||
|
<LoaderCircle class="animate-spin" size={21} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-0.5">
|
<div class="flex flex-col gap-0.5">
|
||||||
<span class="text-sm text-muted-foreground font-semibold">PP</span>
|
<span class="text-sm text-muted-foreground font-semibold">PP</span>
|
||||||
<span class="text-lg font-semibold text-theme-50">727</span>
|
<div class="flex items-center h-full text-lg font-semibold text-theme-50">
|
||||||
|
{#if $currentUserInfo}
|
||||||
|
<div in:fade>
|
||||||
|
<NumberFlow trend={0} value={$currentUserInfo.stats[selectedGamemode].pp ?? 0}
|
||||||
|
></NumberFlow>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div in:fade>
|
||||||
|
<LoaderCircle class="animate-spin" size={21} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-[1fr_auto] border-t border-theme-800 pt-2">
|
<div class="grid grid-cols-[1fr_auto] border-t border-theme-800 pt-2">
|
||||||
<span class="text-sm text-muted-foreground font-semibold">Accuracy</span>
|
<span class="text-sm text-muted-foreground font-semibold">Accuracy</span>
|
||||||
<span class="text-sm font-semibold text-end text-theme-50">72.72%</span>
|
<div
|
||||||
|
class="flex items-center flex-row-reverse h-full text-sm text-end font-semibold text-theme-50"
|
||||||
|
>
|
||||||
|
{#if $currentUserInfo}
|
||||||
|
<div in:fade>
|
||||||
|
<NumberFlow
|
||||||
|
trend={0}
|
||||||
|
suffix="%"
|
||||||
|
value={$currentUserInfo.stats[selectedGamemode].acc.toFixed(2) ?? 0}
|
||||||
|
></NumberFlow>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div in:fade>
|
||||||
|
<LoaderCircle class="animate-spin" size={21} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="text-sm text-muted-foreground font-semibold">Play Count</span>
|
<span class="text-sm text-muted-foreground font-semibold">Play Count</span>
|
||||||
<span class="text-sm font-semibold text-end text-theme-50">727</span>
|
<div
|
||||||
|
class="flex items-center flex-row-reverse h-full text-sm text-end font-semibold text-theme-50"
|
||||||
|
>
|
||||||
|
{#if $currentUserInfo}
|
||||||
|
<div in:fade>
|
||||||
|
<NumberFlow trend={0} value={$currentUserInfo.stats[selectedGamemode].plays ?? 0}
|
||||||
|
></NumberFlow>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div in:fade>
|
||||||
|
<LoaderCircle class="animate-spin" size={21} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="text-sm text-muted-foreground font-semibold">Play Time</span>
|
<span class="text-sm text-muted-foreground font-semibold">Play Time</span>
|
||||||
<span class="text-sm font-semibold text-end text-theme-50">727h</span>
|
<div
|
||||||
|
class="flex items-center flex-row-reverse h-full text-sm text-end font-semibold text-theme-50"
|
||||||
|
>
|
||||||
|
{#if $currentUserInfo}
|
||||||
|
<div in:fade>
|
||||||
|
{formatTimeReadable($currentUserInfo.stats[selectedGamemode].playtime ?? 0)}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div in:fade>
|
||||||
|
<LoaderCircle class="animate-spin" size={21} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="mt-auto bg-theme-900/90 border border-theme-800/90 rounded-lg p-2">
|
<!-- <div class="mt-auto bg-theme-900/90 border border-theme-800/90 rounded-lg p-2">
|
||||||
@ -150,6 +314,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-6 w-full h-full bg-theme-900/40 p-6">
|
<div class="flex flex-col gap-6 w-full h-full bg-theme-900/40 p-6">
|
||||||
<div
|
<div
|
||||||
@ -188,21 +353,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="relative font-bold text-xl text-blue-400">
|
<div class="relative font-bold text-xl text-blue-400">
|
||||||
<div
|
<div
|
||||||
class="absolute top-1 left-1/2 -translate-x-1/2 {!$beatmap_sets
|
class="absolute top-1 left-1/2 -translate-x-1/2 {!$beatmapSets
|
||||||
? 'opacity-100'
|
? 'opacity-100'
|
||||||
: 'opacity-0'} transition-opacity duration-1000"
|
: 'opacity-0'} transition-opacity duration-1000"
|
||||||
>
|
>
|
||||||
<LoaderCircle class="animate-spin" />
|
<LoaderCircle class="animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="{!$beatmap_sets
|
class="{!$beatmapSets
|
||||||
? 'opacity-0'
|
? 'opacity-0'
|
||||||
: 'opacity-100'} transition-opacity duration-1000"
|
: 'opacity-100'} transition-opacity duration-1000"
|
||||||
>
|
>
|
||||||
{#if $reduceAnimations}
|
{#if $reduceAnimations}
|
||||||
<span>{numberHumanReadable($beatmap_sets ?? 0)}</span>
|
<span>{numberHumanReadable($beatmapSets ?? 0)}</span>
|
||||||
{:else}
|
{:else}
|
||||||
<NumberFlow value={$beatmap_sets ?? 0} trend={0} />
|
<NumberFlow value={$beatmapSets ?? 0} trend={0} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -249,38 +414,38 @@
|
|||||||
class="bg-theme-800/90 border border-theme-700/90 rounded-lg px-2 py-4 w-full flex flex-col gap-1 items-center justify-center"
|
class="bg-theme-800/90 border border-theme-700/90 rounded-lg px-2 py-4 w-full flex flex-col gap-1 items-center justify-center"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-center p-2 rounded-lg {$server_connection_fails > 1
|
class="flex items-center justify-center p-2 rounded-lg {$serverConnectionFails > 1
|
||||||
? 'bg-red-500/20'
|
? 'bg-red-500/20'
|
||||||
: 'bg-green-500/20'}"
|
: 'bg-green-500/20'}"
|
||||||
>
|
>
|
||||||
{#if $server_connection_fails > 1}
|
{#if $serverConnectionFails > 1}
|
||||||
<WifiOff class="text-red-500" size="26" />
|
<WifiOff class="text-red-500" size="26" />
|
||||||
{:else}
|
{:else}
|
||||||
<Wifi class="text-green-500" size="26" />
|
<Wifi class="text-green-500" size="26" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="relative font-bold text-xl {$server_connection_fails > 1
|
class="relative font-bold text-xl {$serverConnectionFails > 1
|
||||||
? 'text-red-400'
|
? 'text-red-400'
|
||||||
: 'text-green-400'}"
|
: 'text-green-400'}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="absolute top-1 left-1/2 -translate-x-1/2 {!$server_ping ||
|
class="absolute top-1 left-1/2 -translate-x-1/2 {!$serverPing ||
|
||||||
$server_connection_fails > 1
|
$serverConnectionFails > 1
|
||||||
? 'opacity-100'
|
? 'opacity-100'
|
||||||
: 'opacity-0'} transition-opacity duration-1000"
|
: 'opacity-0'} transition-opacity duration-1000"
|
||||||
>
|
>
|
||||||
<LoaderCircle class="animate-spin" />
|
<LoaderCircle class="animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="{!$server_ping || $server_connection_fails > 1
|
class="{!$serverPing || $serverConnectionFails > 1
|
||||||
? 'opacity-0'
|
? 'opacity-0'
|
||||||
: 'opacity-100'} transition-opacity duration-1000"
|
: 'opacity-100'} transition-opacity duration-1000"
|
||||||
>
|
>
|
||||||
{#if $reduceAnimations}
|
{#if $reduceAnimations}
|
||||||
<span>{$server_ping}ms</span>
|
<span>{$serverPing}ms</span>
|
||||||
{:else}
|
{:else}
|
||||||
<NumberFlow value={$server_ping ?? 0} trend={0} suffix="ms" />
|
<NumberFlow value={$serverPing ?? 0} trend={0} suffix="ms" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -289,6 +454,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
size="lg"
|
size="lg"
|
||||||
|
disabled={launching || $osuInstallationPath === ''}
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
launching = true;
|
launching = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -297,7 +463,7 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Play />
|
<Play />
|
||||||
Launch {$server_connection_fails > 1 ? 'offline' : ''}
|
Launch {$serverConnectionFails > 1 ? 'offline' : ''}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -320,7 +486,7 @@
|
|||||||
|
|
||||||
<span class="text-sm text-muted-foreground font-semibold">Beatmap Sets</span>
|
<span class="text-sm text-muted-foreground font-semibold">Beatmap Sets</span>
|
||||||
<span class="text-sm font-semibold text-end text-theme-50"
|
<span class="text-sm font-semibold text-end text-theme-50"
|
||||||
>{numberHumanReadable($beatmap_sets ?? 0)}</span
|
>{numberHumanReadable($beatmapSets ?? 0)}</span
|
||||||
>
|
>
|
||||||
|
|
||||||
<span class="text-sm text-muted-foreground font-semibold">Skins</span>
|
<span class="text-sm text-muted-foreground font-semibold">Skins</span>
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Logo from '$assets/logo.png';
|
import Logo from '$assets/logo.png';
|
||||||
import { estimateRefreshRate } from '@/displayUtils';
|
import { estimateRefreshRate } from '@/displayUtils';
|
||||||
import { current_view, first_startup } from '@/global';
|
import { beatmapSets, currentLoadingInfo, currentView, firstStartup } from '@/global';
|
||||||
import { cursorSmoothness } from '@/userSettings';
|
import {
|
||||||
|
cursorSmoothness,
|
||||||
|
osuInstallationPath,
|
||||||
|
preferredMode,
|
||||||
|
preferredType,
|
||||||
|
userSettings,
|
||||||
|
} from '@/userSettings';
|
||||||
import { animate, utils } from 'animejs';
|
import { animate, utils } from 'animejs';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import SetupWizard from './SetupWizard.svelte';
|
import SetupWizard from './SetupWizard.svelte';
|
||||||
@ -10,6 +16,8 @@
|
|||||||
import { currentUser, userAuth } from '@/userAuthentication';
|
import { currentUser, userAuth } from '@/userAuthentication';
|
||||||
import { ezppfarm } from '@/api/ezpp';
|
import { ezppfarm } from '@/api/ezpp';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import { currentUserInfo } from '@/data';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
let ezppLogo: HTMLImageElement;
|
let ezppLogo: HTMLImageElement;
|
||||||
let spinnerCircle: SVGCircleElement;
|
let spinnerCircle: SVGCircleElement;
|
||||||
@ -54,7 +62,8 @@
|
|||||||
|
|
||||||
const username = $userAuth.value('username').get('');
|
const username = $userAuth.value('username').get('');
|
||||||
const password = $userAuth.value('password').get('');
|
const password = $userAuth.value('password').get('');
|
||||||
|
if (username.length > 0 && password.length > 0) {
|
||||||
|
currentLoadingInfo.set('Logging in...');
|
||||||
try {
|
try {
|
||||||
const loginResult = await ezppfarm.login(username, password);
|
const loginResult = await ezppfarm.login(username, password);
|
||||||
if (loginResult && loginResult.user) {
|
if (loginResult && loginResult.user) {
|
||||||
@ -73,6 +82,40 @@
|
|||||||
description: 'There was an issue connecting to the server. Please try again later.',
|
description: 'There was an issue connecting to the server. Please try again later.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if ($currentUser) {
|
||||||
|
currentLoadingInfo.set('Loading user info...');
|
||||||
|
const userInfo = await ezppfarm.getUserInfo($currentUser.id);
|
||||||
|
if (userInfo) {
|
||||||
|
currentUserInfo.set(userInfo.player);
|
||||||
|
|
||||||
|
preferredMode.set(userInfo.player.info.preferred_mode);
|
||||||
|
preferredType.set(userInfo.player.info.preferred_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$firstStartup) {
|
||||||
|
currentLoadingInfo.set('Checking osu installation path...');
|
||||||
|
const validFolder: boolean = await invoke('valid_osu_folder', {
|
||||||
|
folder: $osuInstallationPath,
|
||||||
|
});
|
||||||
|
if (!validFolder) {
|
||||||
|
osuInstallationPath.set('');
|
||||||
|
$userSettings.value('osu_installation_path').del();
|
||||||
|
await $userSettings.save();
|
||||||
|
toast.error('Oops...', {
|
||||||
|
description: 'Your previously set osu! installation path seems to be invalid.',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
currentLoadingInfo.set('Counting beatmapsets...');
|
||||||
|
const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', {
|
||||||
|
folder: $osuInstallationPath,
|
||||||
|
});
|
||||||
|
if (beatmapSetCount) {
|
||||||
|
beatmapSets.set(beatmapSetCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
animate(ezppLogo, {
|
animate(ezppLogo, {
|
||||||
opacity: [1, 0],
|
opacity: [1, 0],
|
||||||
@ -88,8 +131,8 @@
|
|||||||
onComplete: () => {},
|
onComplete: () => {},
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if ($first_startup) current_view.set(SetupWizard);
|
if ($firstStartup) currentView.set(SetupWizard);
|
||||||
else current_view.set(Launch);
|
else currentView.set(Launch);
|
||||||
}, 250);
|
}, 250);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -144,4 +187,5 @@
|
|||||||
bind:this={ezppLogo}
|
bind:this={ezppLogo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="text-theme-200 font-semibold">{$currentLoadingInfo}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
import Button from '@/components/ui/button/button.svelte';
|
import Button from '@/components/ui/button/button.svelte';
|
||||||
import Input from '@/components/ui/input/input.svelte';
|
import Input from '@/components/ui/input/input.svelte';
|
||||||
import Label from '@/components/ui/label/label.svelte';
|
import Label from '@/components/ui/label/label.svelte';
|
||||||
import { current_view } from '@/global';
|
import { currentView } from '@/global';
|
||||||
import { currentUser, userAuth } from '@/userAuthentication';
|
import { currentUser, userAuth } from '@/userAuthentication';
|
||||||
import { animate } from 'animejs';
|
import { animate } from 'animejs';
|
||||||
import { LoaderCircle } from 'lucide-svelte';
|
import { LoaderCircle } from 'lucide-svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import Launch from './Launch.svelte';
|
import Launch from './Launch.svelte';
|
||||||
|
import { currentUserInfo } from '@/data';
|
||||||
|
import { preferredMode, preferredType } from '@/userSettings';
|
||||||
|
|
||||||
let username = $state('');
|
let username = $state('');
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
@ -52,7 +54,7 @@
|
|||||||
await $userAuth.save();
|
await $userAuth.save();
|
||||||
|
|
||||||
currentUser.set(loginResult.user);
|
currentUser.set(loginResult.user);
|
||||||
current_view.set(Launch);
|
currentView.set(Launch);
|
||||||
} else {
|
} else {
|
||||||
toast.error('Login failed!', {
|
toast.error('Login failed!', {
|
||||||
description: 'Please check your username and password.',
|
description: 'Please check your username and password.',
|
||||||
@ -65,6 +67,16 @@
|
|||||||
});
|
});
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($currentUser) {
|
||||||
|
const userInfo = await ezppfarm.getUserInfo($currentUser.id);
|
||||||
|
if (userInfo) {
|
||||||
|
currentUserInfo.set(userInfo.player);
|
||||||
|
|
||||||
|
preferredMode.set(userInfo.player.info.preferred_mode);
|
||||||
|
preferredType.set(userInfo.player.info.preferred_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import Checkbox from '@/components/ui/checkbox/checkbox.svelte';
|
import Checkbox from '@/components/ui/checkbox/checkbox.svelte';
|
||||||
import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings';
|
import { cursorSmoothening, customCursor, reduceAnimations, userSettings } from '@/userSettings';
|
||||||
import Label from '@/components/ui/label/label.svelte';
|
import Label from '@/components/ui/label/label.svelte';
|
||||||
import { current_view } from '@/global';
|
import { beatmapSets, currentView } from '@/global';
|
||||||
import Launch from './Launch.svelte';
|
import Launch from './Launch.svelte';
|
||||||
import Confetti from 'svelte-confetti';
|
import Confetti from 'svelte-confetti';
|
||||||
|
|
||||||
@ -65,6 +65,13 @@
|
|||||||
autoDetectedOsuPath = false;
|
autoDetectedOsuPath = false;
|
||||||
manualSelectValid = true;
|
manualSelectValid = true;
|
||||||
$userSettings.value('osu_installation_path').set(osuInstallationPath);
|
$userSettings.value('osu_installation_path').set(osuInstallationPath);
|
||||||
|
|
||||||
|
const beatmapSetCount: number | null = await invoke('get_beatmapsets_count', {
|
||||||
|
folder: osuInstallationPath,
|
||||||
|
});
|
||||||
|
if (beatmapSetCount) {
|
||||||
|
beatmapSets.set(beatmapSetCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,7 +97,7 @@
|
|||||||
class="mt-4"
|
class="mt-4"
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
await $userSettings.save();
|
await $userSettings.save();
|
||||||
current_view.set(Launch);
|
currentView.set(Launch);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
import Titlebar from '@/components/ui/titlebar/titlebar.svelte';
|
import Titlebar from '@/components/ui/titlebar/titlebar.svelte';
|
||||||
import { first_startup, setupValues } from '@/global';
|
import { currentLoadingInfo, firstStartup, setupValues } from '@/global';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import OsuCursor from '@/components/ui/osu-cursor/OsuCursor.svelte';
|
import OsuCursor from '@/components/ui/osu-cursor/OsuCursor.svelte';
|
||||||
import {
|
import {
|
||||||
@ -68,9 +68,10 @@
|
|||||||
window.Buffer = Buffer;
|
window.Buffer = Buffer;
|
||||||
disableReload();
|
disableReload();
|
||||||
setupValues();
|
setupValues();
|
||||||
const firstStartup = await $userSettings.init();
|
const isFirstStartup = await $userSettings.init();
|
||||||
$userAuth.init();
|
$userAuth.init();
|
||||||
|
|
||||||
|
currentLoadingInfo.set('Loading config...');
|
||||||
const config_custom_cursor = $userSettings.value('custom_cursor');
|
const config_custom_cursor = $userSettings.value('custom_cursor');
|
||||||
const config_cursor_smoothening = $userSettings.value('cursor_smoothening');
|
const config_cursor_smoothening = $userSettings.value('cursor_smoothening');
|
||||||
const config_reduce_animations = $userSettings.value('reduce_animations');
|
const config_reduce_animations = $userSettings.value('reduce_animations');
|
||||||
@ -85,7 +86,7 @@
|
|||||||
cursorSmoothening.subscribe((val) => config_cursor_smoothening.set(val));
|
cursorSmoothening.subscribe((val) => config_cursor_smoothening.set(val));
|
||||||
reduceAnimations.subscribe((val) => config_reduce_animations.set(val));
|
reduceAnimations.subscribe((val) => config_reduce_animations.set(val));
|
||||||
|
|
||||||
first_startup.set(firstStartup);
|
firstStartup.set(isFirstStartup);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { current_view } from '@/global';
|
import { currentView } from '@/global';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
const View = $derived($current_view);
|
const View = $derived($currentView);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key View}
|
{#key View}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user