refactor/cleanup (#426)

This commit is contained in:
Jacob 2025-12-12 20:44:54 +08:00 • committed by GitHub
parent cbdb43b796
commit d5a5bd7f05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 551 additions and 360 deletions

122
Cargo.lock generated
View file

@ -971,6 +971,26 @@ dependencies = [
"tiny-keccak",
]
[[package]]
name = "const_format"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "convert_case"
version = "0.4.0"
@ -1275,7 +1295,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@ -2041,6 +2061,19 @@ dependencies = [
"winapi",
]
[[package]]
name = "git2"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff"
dependencies = [
"bitflags 2.6.0",
"libc",
"libgit2-sys",
"log",
"url",
]
[[package]]
name = "glib"
version = "0.18.5"
@ -2561,6 +2594,12 @@ dependencies = [
"once_cell",
]
[[package]]
name = "is_debug"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fe266d2e243c931d8190177f20bf7f24eed45e96f39e87dc49a27b32d12d407"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
@ -2742,6 +2781,18 @@ version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "libgit2-sys"
version = "0.18.3+1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487"
dependencies = [
"cc",
"libc",
"libz-sys",
"pkg-config",
]
[[package]]
name = "libloading"
version = "0.7.4"
@ -2800,6 +2851,18 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@ -3176,6 +3239,15 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
@ -3527,6 +3599,7 @@ dependencies = [
"serde_with",
"sha1",
"sha2",
"shadow-rs",
"specta",
"sysinfo",
"tauri",
@ -5262,6 +5335,19 @@ dependencies = [
"digest",
]
[[package]]
name = "shadow-rs"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72d18183cef626bce22836103349c7050d73db799be0171386b80947d157ae32"
dependencies = [
"const_format",
"git2",
"is_debug",
"time",
"tzdb",
]
[[package]]
name = "sharded-slab"
version = "0.1.7"
@ -6504,7 +6590,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.11",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
@ -6830,6 +6918,32 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "tz-rs"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14eff19b8dc1ace5bf7e4d920b2628ae3837f422ff42210cb1567cbf68b5accf"
[[package]]
name = "tzdb"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0be2ea5956f295449f47c0b825c5e109022ff1a6a53bb4f77682a87c2341fbf5"
dependencies = [
"iana-time-zone",
"tz-rs",
"tzdb_data",
]
[[package]]
name = "tzdb_data"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c4c81d75033770e40fbd3643ce7472a1a9fd301f90b7139038228daf8af03ec"
dependencies = [
"tz-rs",
]
[[package]]
name = "uds_windows"
version = "1.1.0"
@ -6930,6 +7044,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"

View file

@ -173,6 +173,7 @@ sysinfo = { version = "=0.34.2", features = [ "serde" ] }
thiserror = { version = "2" }
whoami = { version = "=1.6.0" }
opener = { version = "=0.8.2" }
shadow-rs = { version = "1.4.0", default-features = false }
# macro
paste = { version = "1" }

View file

@ -1,20 +1,3 @@
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
let output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.output()
.unwrap();
let git_hash = String::from_utf8(output.stdout).unwrap();
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
let start = SystemTime::now();
let timestamp = start
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp);
tauri_build::build();
}

View file

@ -38,6 +38,13 @@
"fs:allow-data-read-recursive",
"fs:allow-data-write-recursive",
"os:default"
"os:allow-arch",
"os:deny-exe-extension",
"os:allow-family",
"os:deny-hostname",
"os:allow-locale",
"os:allow-os-type",
"os:allow-platform",
"os:allow-version"
]
}

View file

@ -1,7 +1,7 @@
// === API ===
pub const CURSEFORGE_API_KEY: &'static str =
"$2a$10$6utA1UNSmFPrE/Lh7b7ndeeGmiOkjKNY8kpFB0fsmE/d42ZAfFgCe";
pub const DISCORD_CLIENT_ID: &'static str = "1274084193193955408";
pub const DISCORD_CLIENT_ID: &'static str = "1426999264633946334";
// === Meta ===
// Cannot contain trailing slash

View file

@ -33,7 +33,7 @@ async fn initialize_core() -> LauncherResult<()> {
launcher_name: "OneClient".to_string(),
launcher_version: env!("CARGO_PKG_VERSION").to_string(),
launcher_website: "https://polyfrost.org/".to_string(),
discord_client_id: None, //Some(constants::DISCORD_CLIENT_ID.to_string()), // disabled for now
discord_client_id: Some(constants::DISCORD_CLIENT_ID.to_string()),
fetch_attempts: 3,
logger_filter: Some(format!(
"{}={level},onelauncher_core={level}",

View file

@ -301,22 +301,10 @@ export type VersionType =
*/
"old_beta"
const ARGS_MAP = { 'debug':'{"getFamily":[],"getPlatform":[],"getCommitHash":[],"getArch":[],"openDevTools":[],"getVersion":[],"isInDev":[],"getType":[],"getLocale":[],"getBuildTimestamp":[]}', 'oneclient':'{"getVersions":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"]}', 'core':'{"getPackageBody":["provider","body"],"createSettingsProfile":["name"],"getProfileOrDefault":["name"],"setDefaultUser":["uuid"],"getUsersFromAuthor":["provider","author"],"removeCluster":["id"],"writeSettings":["setting"],"getUser":["uuid"],"readSettings":[],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"updateClusterProfile":["name","profile"],"convertUsernameUUID":["username_uuid"],"open":["input"],"getRunningProcesses":[],"installModpack":["modpack","cluster_id"],"searchPackages":["provider","query"],"changeSkin":["access_token","skin_url","skin_variant"],"launchCluster":["id","uuid"],"changeCape":["access_token","cape_uuid"],"getGlobalProfile":[],"getClusters":[],"getWorlds":["id"],"getMultiplePackages":["provider","slugs"],"removeUser":["uuid"],"removeCape":["access_token"],"getUsers":[],"getPackage":["provider","slug"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getRunningProcessesByClusterId":["cluster_id"],"fetchMinecraftProfile":["uuid"],"getClusterById":["id"],"getGameVersions":[],"getDefaultUser":["fallback"],"getLogs":["id"],"getLogByName":["id","name"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getLoadersForVersion":["mc_version"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"fetchLoggedInProfile":["access_token"],"openMsaLogin":[],"getScreenshots":["id"],"isClusterRunning":["cluster_id"],"killProcess":["pid"],"createCluster":["options"],"updateClusterById":["id","request"]}', 'events':'{"process":["event"],"message":["event"],"ingress":["event"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' }
const ARGS_MAP = { 'debug':'{"getType":[],"getGitCommitHash":[],"getBuildTimestamp":[],"getPlatform":[],"getOsVersion":[],"getArch":[],"getPackageVersion":[],"openDevTools":[],"getFamily":[],"isInDev":[],"getLocale":[]}', 'oneclient':'{"getClustersGroupedByMajor":[],"getVersions":[],"getBundlesFor":["cluster_id"]}', 'events':'{"message":["event"],"process":["event"],"ingress":["event"]}', 'core':'{"getScreenshots":["id"],"getPackageBody":["provider","body"],"convertUsernameUUID":["username_uuid"],"writeSettings":["setting"],"createCluster":["options"],"createSettingsProfile":["name"],"getMultiplePackages":["provider","slugs"],"updateClusterProfile":["name","profile"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"openMsaLogin":[],"getClusters":[],"installModpack":["modpack","cluster_id"],"fetchMinecraftProfile":["uuid"],"killProcess":["pid"],"getUsers":[],"searchPackages":["provider","query"],"readSettings":[],"getLoadersForVersion":["mc_version"],"getRunningProcessesByClusterId":["cluster_id"],"getProfileOrDefault":["name"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getUser":["uuid"],"getRunningProcesses":[],"fetchLoggedInProfile":["access_token"],"getLogs":["id"],"isClusterRunning":["cluster_id"],"getWorlds":["id"],"removeCape":["access_token"],"updateClusterById":["id","request"],"getGameVersions":[],"launchCluster":["id","uuid"],"removeCluster":["id"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getLinkedPackages":["cluster_id"],"getClusterById":["id"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"removeUser":["uuid"],"changeSkin":["access_token","skin_url","skin_variant"],"getUsersFromAuthor":["provider","author"],"getGlobalProfile":[],"changeCape":["access_token","cape_uuid"],"getLogByName":["id","name"],"getDefaultUser":["fallback"],"setDiscordRPCMessage":["message"],"getPackage":["provider","slug"],"open":["input"],"setDefaultUser":["uuid"]}', 'folders':'{"openCluster":["folder_name"],"fromCluster":["folder_name"]}' }
export type Router = { 'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getPlatform: () => Promise<string>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string | null>,
getType: () => Promise<string>,
getVersion: () => Promise<string>,
getCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest> },
@ -356,6 +344,7 @@ getPackageVersions: (provider: Provider, slug: string, mcVersion: string | null,
downloadPackage: (provider: Provider, packageId: string, versionId: string, clusterId: number, skipCompatibility: boolean | null) => Promise<PackageModel>,
downloadExternalPackage: (package: ExternalPackage, clusterId: number, force: boolean | null, skipCompatibility: boolean | null) => Promise<PackageModel | null>,
getUsersFromAuthor: (provider: Provider, author: PackageAuthor) => Promise<ManagedUser[]>,
getLinkedPackages: (clusterId: number) => Promise<PackageModel[]>,
installModpack: (modpack: ModpackArchive, clusterId: number) => Promise<null>,
fetchMinecraftProfile: (uuid: string) => Promise<MojangPlayerProfile>,
fetchLoggedInProfile: (accessToken: string) => Promise<MojangFullPlayerProfile>,
@ -364,7 +353,21 @@ changeSkin: (accessToken: string, skinUrl: string, skinVariant: SkinVariant) =>
changeCape: (accessToken: string, capeUuid: string) => Promise<MojangFullPlayerProfile>,
removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
open: (input: string) => Promise<null> } };
setDiscordRPCMessage: (message: string) => Promise<null>,
open: (input: string) => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string | null>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> } };
export type { InferCommandOutput }

View file

@ -1,8 +1,8 @@
import type { ClusterModel, ModpackFile } from '@/bindings.gen';
import { ModCard, useModCardContext } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { twMerge } from 'tailwind-merge';
import { ModCard, useModCardContext } from '.';
interface BundleProps {
files: Array<ModpackFile>;

View file

@ -1,10 +1,10 @@
import type { ClusterModel, ModpackFile, ModpackFileKind } from '@/bindings.gen';
import MissingLogo from '@/assets/misc/missingLogo.svg';
import { DownloadModButton, ModTag } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { createContext, useContext, useEffect, useState } from 'react';
import { twMerge } from 'tailwind-merge';
import { DownloadModButton, ModTag } from '.';
export interface ModInfo {
name: string;

View file

@ -1,7 +1,7 @@
import type { ClusterModel, ModpackArchive } from '@/bindings.gen';
import { Bundle, useModCardContext } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { Button, Tab, TabContent, TabList, TabPanel, Tabs } from '@onelauncher/common/components';
import { Bundle, useModCardContext } from '.';
function getBundleName(name: string): string {
return (name.match(/\[(.*?)\]/)?.[1]) ?? 'Loading...';

View file

@ -1,5 +1,5 @@
import type { ClusterModel } from '@/bindings.gen';
import type { ModInfo } from '.';
import type { ModInfo } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { Link } from '@tanstack/react-router';
import { twMerge } from 'tailwind-merge';

View file

@ -1,12 +1,11 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import { Overlay, RemoveAccountModal } from '@/components';
import { Button } from '@onelauncher/common/components';
import { Trash01Icon } from '@untitled-theme/icons-react';
import { DialogTrigger } from 'react-aria-components';
import { Overlay, RemoveAccountModal } from './overlay';
export function DeleteAccountButton({ profile, onPress }: { profile: MinecraftCredentials; onPress: () => void }) {
return (
<DialogTrigger>
<Overlay.Trigger>
<Button className="group w-8 h-8" color="ghost" size="icon">
<Trash01Icon className="group-hover:stroke-danger" />
</Button>
@ -14,6 +13,6 @@ export function DeleteAccountButton({ profile, onPress }: { profile: MinecraftCr
<Overlay>
<RemoveAccountModal onPress={onPress} profile={profile} />
</Overlay>
</DialogTrigger>
</Overlay.Trigger>
);
}

View file

@ -1,4 +1,5 @@
import type { ExternalPackage, ManagedVersionDependency, ModpackFile, Provider } from '@/bindings.gen';
import { getModMetaDataName, Overlay } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { useCommandMut } from '@onelauncher/common';
@ -6,8 +7,6 @@ import { Button } from '@onelauncher/common/components';
import { useNavigate } from '@tanstack/react-router';
import { useEffect, useImperativeHandle, useState } from 'react';
import { DialogTrigger } from 'react-aria-components';
import { getModMetaDataName } from './Bundle';
import { Overlay } from './overlay';
export interface DownloadModsRef {
openDownloadDialog: (nextPath?: string) => void;

View file

@ -1,11 +1,11 @@
import type React from 'react';
import type { HTMLProps } from 'react';
import { Overlay } from '@/components';
import { bindings } from '@/main';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { LinkExternal01Icon } from '@untitled-theme/icons-react';
import { useState } from 'react';
import { twMerge } from 'tailwind-merge';
import { Overlay } from './overlay';
export interface ExternalLinkProps extends HTMLProps<HTMLAnchorElement> {
href?: string | undefined;

View file

@ -1,16 +1,17 @@
import type { ClusterModel } from '@/bindings.gen';
import type { ButtonProps } from '@onelauncher/common/components';
import { KillMinecraft, NoAccountPopup, Overlay } from '@/components';
import { useIsRunning } from '@/hooks/useClusters';
import { useLaunchCluster } from '@/hooks/useLaunchCluster';
import { bindings } from '@/main';
import { useCommandSuspense } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { useNavigate } from '@tanstack/react-router';
import { useState } from 'react';
import { DialogTrigger } from 'react-aria-components';
import { tv } from 'tailwind-variants';
import { NoAccountPopup, Overlay } from './overlay';
export type LaunchButtonProps = Omit<ButtonProps, 'children' | 'onClick'> & {
clusterId: number | undefined | null;
cluster: ClusterModel;
};
const launchButtonVariants = tv({
@ -21,32 +22,73 @@ const launchButtonVariants = tv({
},
});
function PromptForOnboarding({ launch, setSkipPackagesCheck, cluster }: { launch: () => void; setSkipPackagesCheck: React.Dispatch<React.SetStateAction<boolean>>; cluster: ClusterModel }) {
const navigate = useNavigate();
const onClickYes = () => {
localStorage.setItem('selectedClusters', JSON.stringify([{ mc_version: cluster.mc_version, mc_loader: cluster.mc_loader }]));
navigate({ to: `/onboarding/preferences/versionCategory` });
};
const onClickLaunch = () => {
setSkipPackagesCheck(true);
launch();
};
return (
<Overlay.Dialog>
<Overlay.Title>Not Setup</Overlay.Title>
<div className="flex flex-col items-center">
<p className="max-w-sm text-fg-secondary">It seems like you haven't setup this version</p>
<p className="max-w-sm text-fg-secondary">Do you want to setup this version?</p>
</div>
<Overlay.Buttons
buttons={[
{ color: 'primary', key: 'Yes', children: 'Yes', size: 'normal', onClick: onClickYes },
{
color: 'secondary',
key: 'anyways',
children: 'Launch Anyways',
size: 'normal',
onClick: onClickLaunch,
},
]}
/>
</Overlay.Dialog>
);
}
export function LaunchButton({
clusterId,
cluster,
isDisabled,
className,
...rest
}: LaunchButtonProps) {
const { data: currentAccount } = useCommandSuspense(['getDefaultUser'], () => bindings.core.getDefaultUser(true));
const launchCluster = useLaunchCluster(clusterId);
const isRunning = useIsRunning(clusterId);
const [open, setOpen] = useState<boolean>(false);
const { data: installedPackages } = useCommandSuspense(['getLinkedPackages', cluster.id], () => bindings.core.getLinkedPackages(cluster.id));
const launchCluster = useLaunchCluster(cluster.id);
const isRunning = useIsRunning(cluster.id);
const [reason, setReason] = useState<'account' | 'packages' | 'kill' | null>(null);
const [skipPackagesCheck, setSkipPackagesCheck] = useState(false);
const launch = () => {
if (currentAccount === null) {
setOpen(true);
}
else {
launchCluster();
setOpen(false);
}
if (isRunning)
return setReason('kill');
if (!currentAccount)
return setReason('account');
if (installedPackages.length === 0 && !skipPackagesCheck)
return setReason('packages');
launchCluster();
setSkipPackagesCheck(false);
setReason(null);
};
return (
<DialogTrigger isOpen={open} onOpenChange={setOpen}>
<Overlay.Trigger isOpen={reason !== null} onOpenChange={open => !open && setReason(null)}>
<Button
className={launchButtonVariants({ isRunning, className })}
isDisabled={isDisabled || isRunning}
isDisabled={isDisabled}
onPress={launch}
{...rest}
>
@ -54,8 +96,10 @@ export function LaunchButton({
</Button>
<Overlay>
<NoAccountPopup />
{reason === 'kill' && <KillMinecraft setOpen={() => setReason(null)} />}
{reason === 'packages' && <PromptForOnboarding cluster={cluster} launch={launch} setSkipPackagesCheck={setSkipPackagesCheck} />}
{reason === 'account' && <NoAccountPopup />}
</Overlay>
</DialogTrigger>
</Overlay.Trigger>
);
}

View file

@ -1,9 +1,9 @@
import type { SpinnerProps } from '@/components';
import type { HTMLAttributes, ReactNode } from 'react';
import type { SpinnerProps } from './Spinner';
import { Spinner } from '@/components';
import { motion } from 'motion/react';
import { Suspense } from 'react';
import { twMerge } from 'tailwind-merge';
import { Spinner } from './Spinner';
interface LoaderProps extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;

View file

@ -1,8 +1,8 @@
import { ExternalLink } from '@/components';
import ReactMarkdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import { twMerge } from 'tailwind-merge';
import { ExternalLink } from './ExternalLink';
import styles from './Markdown.module.css';
export function Markdown({

View file

@ -1,6 +1,7 @@
import type { ButtonProps } from '@onelauncher/common/components';
import type { LinkProps, RegisteredRouter } from '@tanstack/react-router';
import LauncherLogo from '@/assets/logos/oneclient.svg?react';
import { AccountAvatar, AccountPopup, Overlay, SuperSecretDevOptions } from '@/components';
import { bindings } from '@/main';
import { useCommand } from '@onelauncher/common';
import { Button, Popup } from '@onelauncher/common/components';
@ -8,9 +9,6 @@ import { Link, useNavigate } from '@tanstack/react-router';
import { Window } from '@tauri-apps/api/window';
import { MinusIcon, Settings02Icon, SquareIcon, XCloseIcon } from '@untitled-theme/icons-react';
import { twMerge } from 'tailwind-merge';
import { AccountAvatar } from './AccountAvatar';
import { Overlay, SuperSecretDevOptions } from './overlay';
import { AccountPopup } from './overlay/AccountPopup';
export function Navbar() {
const { data: currentAccount } = useCommand(['getDefaultUser'], () => bindings.core.getDefaultUser(true));

View file

@ -5,7 +5,7 @@ type BooleanSetting = [
(value: boolean) => void,
];
export default function SettingsSwitch({ setting }: { setting: BooleanSetting }) {
export function SettingsSwitch({ setting }: { setting: BooleanSetting }) {
return (
<Switch
isSelected={setting[0]}

View file

@ -8,7 +8,7 @@ export interface SettingsRowProps {
children?: JSX.Element;
}
function SettingsRow({ disabled, description, icon, title, children }: SettingsRowProps) {
export function SettingsRow({ disabled, description, icon, title, children }: SettingsRowProps) {
return (
<div
className={`

View file

@ -118,7 +118,7 @@ SheetPage.Content = ({
</motion.div>
);
function GoBackButton() {
export function GoBackButton() {
const router = useRouter();
const canGoBack = useCanGoBack();

View file

@ -11,7 +11,7 @@ export function Stepper({ steps, currentStepIndex }: VerticalStepperProps) {
<div className="max-w-md mx-auto">
{steps.map((step, index) => (
<div
className={twMerge('transition-all duration-300 relative flex items-center cursor-pointer py-2 pl-4 border-l-3 border-gray-600 font-medium text-gray-400 text-base data-active:border-brand data-active:text-white data-active:text-lg data-complete:border-brand', index === 0 ? 'pt-0' : '', index === steps.length - 1 ? 'pb-0' : '')}
className={twMerge('after:transition-all after:duration-300 after:text-brand transition-all duration-300 relative flex items-center cursor-pointer py-2 pl-4 font-medium text-gray-400 text-base partial-leftline-0% data-active:text-white data-active:text-lg data-active:partial-leftline-100% data-complete:partial-leftline-100%')}
data-active={index === currentStepIndex || null}
data-complete={index < currentStepIndex || null}
key={index}

View file

@ -1,13 +1,21 @@
export * from './AccountAvatar';
export * from './BrowserPackageItem';
export * from './Bundle';
export * from './DeleteAccountButton';
export * from './DownloadMods';
export * from './ExternalLink';
export * from './GameBackground';
export * from './LaunchButton';
export * from './Loader';
export * from './LogViewer';
export * from './ManageSkinButton';
export * from './Markdown';
export * from './Navbar';
export * from './overlay';
export * from './SettingsRow';
export * from './SettingSwitch';
export * from './SheetPage';
export * from './SkinViewer';
export * from './Spinner';
export * from './Stepper';
export * from './Tabs';

View file

@ -1,17 +1,11 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import { AccountAvatar, AddAccountModal, DeleteAccountButton, ManageSkinButton, Overlay, Popup } from '@/components';
import { bindings } from '@/main';
import { useCommand } 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 { DeleteAccountButton } from '../DeleteAccountButton';
import { ManageSkinButton } from '../ManageSkinButton';
import { AddAccountModal } from './AddAccountModal';
import { Overlay } from './Overlay';
import { Popup } from './Popup';
export function AccountPopup() {
const users = useCommand(['getUsers'], bindings.core.getUsers);
@ -40,47 +34,40 @@ export function AccountPopup() {
<div className="min-w-3xs">
{defaultUser.data && (
<div>
<AccountEntry
loggedIn
onClick={() => { }}
onDelete={() => deleteUser(defaultUser.data as MinecraftCredentials)}
user={defaultUser.data}
/>
</div>
<AccountEntry
loggedIn
onClick={() => { }}
onDelete={() => deleteUser(defaultUser.data as MinecraftCredentials)}
user={defaultUser.data}
/>
)}
{users.data?.filter(user => user.id !== defaultUser.data?.id).map(user => (
<div key={user.id}>
<AccountEntry
onClick={() => setDefaultUser(user)}
onDelete={() => deleteUser(user)}
user={user}
/>
</div>
<AccountEntry
key={user.id}
onClick={() => setDefaultUser(user)}
onDelete={() => deleteUser(user)}
user={user}
/>
))}
<div className="flex flex-row justify-between">
<div className="self-center">
<Overlay.Trigger>
<Button color="ghost">
<PlusIcon />
<p>Add Account</p>
</Button>
<DialogTrigger>
<Button color="ghost">
<PlusIcon />
Add Account
</Button>
<Overlay>
<AddAccountModal />
</Overlay>
</Overlay.Trigger>
<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>
<Link to="/app/accounts">
<Button color="ghost" size="iconLarge">
<Settings01Icon className="stroke-fg-primary" />
</Button>
</Link>
</div>
</div>
</Popup>
@ -89,23 +76,9 @@ export function AccountPopup() {
export default AccountPopup;
function AccountEntry({
onClick,
onDelete,
user,
loggedIn = false,
}: {
onClick: () => void;
onDelete: () => void;
user: MinecraftCredentials;
loggedIn?: boolean;
}) {
function AccountEntry({ onClick, onDelete, user, loggedIn = false }: { onClick: () => void; onDelete: () => void; user: MinecraftCredentials; loggedIn?: boolean }) {
return (
<Button
className={twMerge('w-full 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')}
color="ghost"
onClick={onClick}
>
<Button className={twMerge('w-full 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')} color="ghost" onClick={onClick}>
<div className="flex flex-1 flex-row justify-start gap-x-3">
<div className="flex flex-1 flex-row justify-start gap-x-3">
<AccountAvatar className="h-8 w-8 rounded-md" uuid={user.id} />

View file

@ -1,12 +1,20 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import { AccountAvatar } from '@/components/AccountAvatar';
import { AccountAvatar, Overlay } from '@/components';
import { bindings } from '@/main';
import { useCommandMut } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { useQueryClient } from '@tanstack/react-query';
import { Overlay } from './Overlay';
export function AddAccountModal() {
return (
<Overlay.Dialog>
<Overlay.Title>Add Account</Overlay.Title>
<p className="text-fg-secondary">Add a new account to OneClient</p>
<AddAccountModalButton />
</Overlay.Dialog>
);
}
export function AddAccountModalButton() {
const queryClient = useQueryClient();
const { data: profile, isPending, mutate: login } = useCommandMut(bindings.core.openMsaLogin, {
@ -25,47 +33,18 @@ export function AddAccountModal() {
};
return (
<Overlay.Dialog>
<Overlay.Title>Add Account</Overlay.Title>
<p className="max-w-sm text-fg-secondary">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Fugiat aut unde, rem esse natus iusto impedit doloribus laborum laboriosam amet? Totam, commodi sed ducimus dicta praesentium sunt? A, soluta iusto.
</p>
{profile
? (
<>
<AccountRow profile={profile} />
<Button
className="w-full"
color="primary"
size="large"
slot="close"
>
Close
</Button>
</>
)
: (
<Button
className="w-full"
color="primary"
isPending={isPending}
onClick={onClick}
size="large"
>
Add Account
</Button>
)}
</Overlay.Dialog>
profile
? (
<>
<AccountRow profile={profile} />
<Overlay.Buttons buttons={[{ color: 'primary', children: 'Close', slot: 'close' }]} />
</>
)
: <Overlay.Buttons buttons={[{ color: 'primary', children: 'Add Account', isPending, onClick }]} />
);
}
function AccountRow({
profile,
}: {
profile: MinecraftCredentials;
}) {
function AccountRow({ profile }: { profile: MinecraftCredentials }) {
return (
<div className="flex flex-row items-center justify-start gap-2">
<AccountAvatar className="aspect-square h-12 rounded-sm " uuid={profile.id} />

View file

@ -1,9 +1,8 @@
import { ModList, Overlay, useModCardContext } from '@/components';
import { bindings } from '@/main';
import { useCommandSuspense } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { useState } from 'react';
import { ModList, useModCardContext } from '../Bundle';
import { Overlay } from './Overlay';
export function BundleModListModal({ clusterId, name }: { clusterId: number; name: string }) {
const { data: cluster } = useCommandSuspense(['getClusterById'], () => bindings.core.getClusterById(clusterId));

View file

@ -1,8 +1,8 @@
import { Overlay } from '@/components';
import { bindings } from '@/main';
import { Button } from '@onelauncher/common/components';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { useEffect, useState } from 'react';
import { Overlay } from './Overlay';
export type DebugInfoArray = Array<{ title: string; value: string }>;
export interface DebugInfoData {
@ -12,9 +12,10 @@ export interface DebugInfoData {
family: string;
locale: string;
type: string;
version: string;
osVersion: string;
commitHash: string;
buildTimestamp: string;
buildVersion: string;
}
export function useDebugInfo(): DebugInfoArray {
@ -25,9 +26,10 @@ export function useDebugInfo(): DebugInfoArray {
family: 'UNKNOWN',
locale: 'UNKNOWN',
type: 'UNKNOWN',
version: 'UNKNOWN',
osVersion: 'UNKNOWN',
commitHash: 'UNKNOWN',
buildTimestamp: 'UNKNOWN',
buildVersion: 'UNKNOWN',
});
useEffect(() => {
@ -39,9 +41,10 @@ export function useDebugInfo(): DebugInfoArray {
family,
locale,
type,
version,
osVersion,
commitHash,
buildTimestamp,
buildVersion,
] = await Promise.all([
bindings.debug.isInDev(),
bindings.debug.getPlatform(),
@ -49,9 +52,10 @@ export function useDebugInfo(): DebugInfoArray {
bindings.debug.getFamily(),
bindings.debug.getLocale(),
bindings.debug.getType(),
bindings.debug.getVersion(),
bindings.debug.getCommitHash(),
bindings.debug.getOsVersion(),
bindings.debug.getGitCommitHash(),
bindings.debug.getBuildTimestamp(),
bindings.debug.getPackageVersion(),
]);
setDevInfo({
@ -61,9 +65,10 @@ export function useDebugInfo(): DebugInfoArray {
family,
locale: locale ?? 'UNKNOWN',
type,
version,
osVersion,
commitHash,
buildTimestamp,
buildTimestamp: new Date(buildTimestamp).getTime().toString(),
buildVersion,
});
};
@ -77,9 +82,10 @@ export function useDebugInfo(): DebugInfoArray {
{ title: 'Family', value: devInfo.family },
{ title: 'Locale', value: devInfo.locale },
{ title: 'Type', value: devInfo.type },
{ title: 'Version', value: devInfo.version },
{ title: 'Os Version', value: devInfo.osVersion },
{ title: 'Commit Hash', value: devInfo.commitHash },
{ title: 'Build Timestamp', value: devInfo.buildTimestamp },
{ title: 'Version', value: devInfo.buildVersion },
];
}
@ -87,7 +93,7 @@ export function copyDebugInfo(debugInfo: DebugInfoArray) {
const timestamp = Math.floor(new Date().getTime() / 1000);
const lines = [`**Data exported at:** <t:${timestamp}> (\`${timestamp}\`)`, ...debugInfo.map((lineData) => {
if (lineData.title === 'Build Timestamp')
return `**${lineData.title}:** <t:${lineData.value}> (\`${lineData.value}\`)`;
return `**${lineData.title}:** <t:${Math.floor(Number(lineData.value) / 1000)}> (\`${Math.floor(Number(lineData.value) / 1000)}\`)`;
return `**${lineData.title}:** \`${lineData.value}\``;
})];
writeText(lines.join('\n'));
@ -111,7 +117,7 @@ export function RawDebugInfo({ debugInfo }: { debugInfo: DebugInfoArray }) {
{debugInfo.map((lineData) => {
let line = '';
if (lineData.title === 'Build Timestamp')
line = `${lineData.title}: ${new Date(Number(lineData.value) * 1000)}`;
line = `${lineData.title}: ${new Date(Number(lineData.value))}`;
else line = `${lineData.title}: ${lineData.value}`;
return <p key={lineData.title}>{line}</p>;

View file

@ -1,6 +1,6 @@
import { Overlay } from '@/components';
import { Button, TextField } from '@onelauncher/common/components';
import { useState } from 'react';
import { Overlay } from './Overlay';
export function ImportSkinModal({ importFromURL, importFromUsername }: { importFromURL: (url: string) => void; importFromUsername: (username: string) => void }) {
const [input, setInput] = useState<string>('');

View file

@ -0,0 +1,19 @@
import { Overlay } from '@/components';
import { bindings } from '@/main';
import { useCommandSuspense } from '@onelauncher/common';
export function KillMinecraft({ setOpen }: { setOpen: React.Dispatch<React.SetStateAction<boolean>> }) {
const { data: foundAllProcess } = useCommandSuspense(['getRunningProcesses'], () => bindings.core.getRunningProcesses());
const kill = () => {
foundAllProcess.forEach(process => bindings.core.killProcess(process.pid));
setOpen(false);
};
return (
<Overlay.Dialog>
<Overlay.Title>Minecraft is running</Overlay.Title>
<p className="max-w-sm text-fg-secondary">Do you want to kill minecraft?</p>
<Overlay.Buttons buttons={[{ color: 'danger', key: 'Yes', children: 'Yes', size: 'normal', onClick: kill }, { color: 'secondary', key: 'No', children: 'No', size: 'normal', slot: 'close' }]} />
</Overlay.Dialog>
);
}

View file

@ -1,23 +1,11 @@
import { Button } from '@onelauncher/common/components';
import { DialogTrigger } from 'react-aria-components';
import { AddAccountModal } from './AddAccountModal';
import { Overlay } from './Overlay';
import { AddAccountModalButton, Overlay } from '@/components';
export function NoAccountPopup() {
return (
<Overlay.Dialog>
<Overlay.Title>No Account</Overlay.Title>
<p className="max-w-sm text-fg-secondary">Please add an account before you start minecraft</p>
<DialogTrigger>
<Button className="w-full" size="large">
Add Account
</Button>
<Overlay>
<AddAccountModal />
</Overlay>
</DialogTrigger>
<AddAccountModalButton />
</Overlay.Dialog>
);
}

View file

@ -1,14 +0,0 @@
import type { OverlayProps } from './Overlay';
import { Overlay } from './Overlay';
export interface PolyBundleModalProps extends OverlayProps {
}
export function PolyBundleModal() {
return (
<Overlay.Dialog>
<Overlay.Title>test</Overlay.Title>
</Overlay.Dialog>
);
}

View file

@ -1,7 +1,6 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import { AccountAvatar } from '@/components/AccountAvatar';
import { AccountAvatar, Overlay } from '@/components';
import { Button } from '@onelauncher/common/components';
import { Overlay } from './Overlay';
export function RemoveAccountModal({
profile,

View file

@ -1,5 +1,5 @@
import { Overlay } from '@/components';
import { Button } from '@onelauncher/common/components';
import { Overlay } from './Overlay';
export function RemoveSkinCapeModal({ onPress }: { onPress: () => void }) {
return (

View file

@ -1,3 +1,4 @@
import { Overlay, RawDebugInfo, SettingsRow, SettingsSwitch, useDebugInfo } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { Button } from '@onelauncher/common/components';
@ -5,10 +6,6 @@ import { Link } from '@tanstack/react-router';
import { dataDir, join } from '@tauri-apps/api/path';
import { BatteryEmptyIcon, Code02Icon, FolderIcon, LinkExternal01Icon, Truck01Icon } from '@untitled-theme/icons-react';
import { useEffect, useState } from 'react';
import SettingsRow from '../SettingsRow';
import SettingsSwitch from '../SettingSwitch';
import { RawDebugInfo, useDebugInfo } from './DebugInfo';
import { Overlay } from './Overlay';
export function SuperSecretDevOptions() {
const { createSetting } = useSettings();

View file

@ -2,6 +2,7 @@ import type { IngressType } from '@/bindings.gen';
import type { ToastData, ToastId } from '@/utils/toast';
import type { HTMLAttributes } from 'react';
import type { ToastContentProps, ToastOptions } from 'react-toastify';
import { Spinner } from '@/components';
import { bindings } from '@/main';
import { toast, toastUpdate } from '@/utils/toast';
import { Button } from '@onelauncher/common/components';
@ -9,7 +10,6 @@ import { AlertCircleIcon, AlertTriangleIcon, CheckCircleIcon, InfoCircleIcon, XC
import { useEffect, useRef } from 'react';
import { cssTransition, ToastContainer } from 'react-toastify';
import { tv } from 'tailwind-variants';
import { Spinner } from '../Spinner';
import styles from './Toasts.module.css';
const toastVariants = tv({

View file

@ -3,6 +3,7 @@ export * from './AddAccountModal';
export * from './BundleModListModal';
export * from './DebugInfo';
export * from './ImportSkinModal';
export * from './KillMinecraft';
export * from './NoAccountPopup';
export * from './Overlay';
export * from './Popup';

View file

@ -31,7 +31,7 @@ export function useActiveCluster() {
const { data: clusters } = useLastPlayedClusters();
return clusters.find(c => c.id === clusterId) ?? clusters.at(0);
return clusters.find(c => c.id === clusterId) ?? clusters[0];
}
export function useIsRunning(clusterId: number | undefined | null) {

View file

@ -1,11 +1,14 @@
import type { QueryClient } from '@tanstack/react-query';
import type { NavigateOptions, ToOptions } from '@tanstack/react-router';
import { Toasts } from '@/components/overlay';
import { Toasts } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { useCommand } from '@onelauncher/common';
import { TanStackDevtools } from '@tanstack/react-devtools';
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools';
import { createRootRouteWithContext, Outlet, useRouter } from '@tanstack/react-router';
import { createRootRouteWithContext, Outlet, useLocation, useRouter } from '@tanstack/react-router';
import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';
import { useEffect } from 'react';
import { RouterProvider } from 'react-aria-components';
interface AppRouterContext {
@ -23,9 +26,58 @@ export const Route = createRootRouteWithContext<AppRouterContext>()({
component: RootRoute,
});
type URLPath = Exclude<ToOptions['to'], undefined>;
const ResolvedPathNames: Record<URLPath, string> = {
'.': 'UNKNOWN',
'..': 'UNKNOWN',
'/': 'Viewing Home',
'/app': 'Viewing Homepage',
'/app/account': 'Viewing Account',
'/app/account/skins': 'Viewing Skin Manager',
'/app/cluster': 'Viewing Clusters',
'/app/cluster/browser': 'Viewing {clusterName}\'s mods',
'/app/cluster/browser/package': 'Browsing {packageName}',
'/app/cluster/logs': 'Viewing {clusterName}\'s logs',
'/app/cluster/mods': 'Viewing {clusterName}\'s mods',
'/app/cluster/overview': 'Viewing {clusterName}',
'/app/cluster/process': 'Viewing {clusterName}',
'/app/cluster/settings': 'Viewing {clusterName}\'s settings',
'/app/settings': 'Viewing Settings',
'/app/settings/appearance': 'Viewing Settings',
'/app/settings/developer': 'Viewing Settings',
'/app/settings/minecraft': 'Viewing Settings',
'/app/accounts': 'Viewing Accounts',
'/app/clusters': 'Viewing Clusters',
'/onboarding': 'Preparing OneClient',
'/onboarding/account': 'Preparing OneClient',
'/onboarding/finished': 'Preparing OneClient',
'/onboarding/language': 'Preparing OneClient',
'/onboarding/preferences/version': 'Preparing OneClient',
'/onboarding/preferences/versionCategory': 'Preparing OneClient',
'/onboarding/preferences': 'Preparing OneClient',
};
// Credit - https://github.com/DuckySoLucky/hypixel-discord-chat-bridge/blob/d3ea84a26ebf094c8191d50b4954549e2dd4dc7f/src/contracts/helperFunctions.js#L216-L225
function ReplaceVariables(template: string, variables: Record<string, any>) {
return template.replace(/\{(\w+)\}/g, (match: any, name: string | number) => variables[name] ?? match);
}
function useDiscordRPC() {
const location = useLocation();
const clusterId = location.search.clusterId ?? 0;
const provider = location.search.provider ?? 'Modrinth';
const packageId = location.search.packageId ?? '8pJYUDNi';
const { data: cluster } = useCommand(['getClusterById', clusterId], () => bindings.core.getClusterById(clusterId));
const { data: managedPackage } = useCommand(['getPackage', provider, packageId], () => bindings.core.getPackage(provider, packageId));
useEffect(() => {
bindings.core.setDiscordRPCMessage(ReplaceVariables(ResolvedPathNames[location.pathname as URLPath], { clusterName: cluster?.name ?? 'UNKNOWN', packageName: managedPackage?.name ?? 'UNKNOWN' }));
}, [location.pathname, location.search.clusterId, cluster?.name, managedPackage?.name]);
}
function RootRoute() {
const router = useRouter();
const { setting } = useSettings();
useDiscordRPC();
return (
<RouterProvider
navigate={(to, options) => router.navigate({ to, ...options })}
@ -46,7 +98,7 @@ function DevTools() {
return (
<TanStackDevtools
config={{
position: 'top-left',
position: 'bottom-left',
}}
plugins={[
{

View file

@ -1,8 +1,8 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import { LoaderSuspense } from '@/components';
import { bindings } from '@/main';
import { MissingAccountData } from '@/routes/app/account/skins';
import { createFileRoute, Outlet } from '@tanstack/react-router';
import { MissingAccountData } from './skins';
export interface AccountsRouteSearchParams {
profile: MinecraftCredentials | undefined;

View file

@ -1,7 +1,5 @@
import type { PlayerAnimation } from 'skinview3d';
import { SheetPage, SkinViewer } from '@/components';
import { ImportSkinModal, RemoveSkinCapeModal } from '@/components/overlay';
import { Overlay } from '@/components/overlay/Overlay';
import { ImportSkinModal, Overlay, RemoveSkinCapeModal, SheetPage, SkinViewer } from '@/components';
import { bindings } from '@/main';
import { getSkinUrl } from '@/utils/minecraft';
import { toast } from '@/utils/toast';
@ -12,7 +10,6 @@ import { save } from '@tauri-apps/plugin-dialog';
import { exists, mkdir, readTextFile, writeFile, writeTextFile } from '@tauri-apps/plugin-fs';
import { Download01Icon, PlusIcon, Trash01Icon } from '@untitled-theme/icons-react';
import { useEffect, useState } from 'react';
import { DialogTrigger } from 'react-aria-components';
import { CrouchAnimation, FlyingAnimation, HitAnimation, IdleAnimation, WalkingAnimation } from 'skinview3d';
interface Skin {
@ -283,7 +280,7 @@ function SkinHistoryRow({ selected, animation, setSelectedSkin, skins, setSkins,
</div>
<div className="flex flex-row h-fit gap-2">
<DialogTrigger>
<Overlay.Trigger>
<Button className="border border-component-border rounded-xl bg-component-border w-[75px] h-[120px]" color="ghost">
<div className="flex flex-col justify-center items-center content-center h-full">
<PlusIcon className="scale-200" />
@ -292,7 +289,7 @@ function SkinHistoryRow({ selected, animation, setSelectedSkin, skins, setSkins,
<Overlay>
<ImportSkinModal importFromURL={importFromURL} importFromUsername={importFromUsername} />
</Overlay>
</DialogTrigger>
</Overlay.Trigger>
{skins.map(skinData => (
<RenderSkin
animation={animation}
@ -358,7 +355,7 @@ function RenderSkin({ skin, selected, animation, setSelectedSkin, setSkins, cape
{selected.skin_url === skin.skin_url
? <></>
: (
<DialogTrigger>
<Overlay.Trigger>
<Button className="group w-8 h-8 absolute top-0 right-0" color="ghost" size="icon">
<Trash01Icon className="group-hover:stroke-danger" />
</Button>
@ -366,7 +363,7 @@ function RenderSkin({ skin, selected, animation, setSelectedSkin, setSkins, cape
<Overlay>
<RemoveSkinCapeModal onPress={() => setSkins(prev => prev.filter(skinData => skinData.skin_url !== skin.skin_url))} />
</Overlay>
</DialogTrigger>
</Overlay.Trigger>
)}
<Button
className="group w-8 h-8 absolute bottom-0 right-0"

View file

@ -1,15 +1,13 @@
import type { MinecraftCredentials } from '@/bindings.gen';
import type { ButtonProps } from '@onelauncher/common/components';
import { AccountAvatar, DeleteAccountButton, ManageSkinButton, SheetPage, SkinViewer } from '@/components';
import { AddAccountModal } from '@/components/overlay';
import { Overlay } from '@/components/overlay/Overlay';
import { AccountAvatar, AddAccountModal, DeleteAccountButton, ManageSkinButton, Overlay, SheetPage, SkinViewer } from '@/components';
import { usePlayerProfile } from '@/hooks/usePlayerProfile';
import { bindings } from '@/main';
import { useCommandMut, useCommandSuspense } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { useQueryClient } from '@tanstack/react-query';
import { createFileRoute } from '@tanstack/react-router';
import { Button as AriaButton, DialogTrigger } from 'react-aria-components';
import { Button as AriaButton } from 'react-aria-components';
import { twMerge } from 'tailwind-merge';
export const Route = createFileRoute('/app/accounts')({
@ -166,7 +164,7 @@ function AddAccountButton({
size: ButtonProps['size'];
}) {
return (
<DialogTrigger>
<Overlay.Trigger>
<Button color="secondary" size={size}>
Add Account
</Button>
@ -174,6 +172,6 @@ function AddAccountButton({
<Overlay>
<AddAccountModal />
</Overlay>
</DialogTrigger>
</Overlay.Trigger>
);
}

View file

@ -1,7 +1,5 @@
import type { ManagedPackage, ManagedUser, ManagedVersion, PackageDonationUrl } from '@/bindings.gen';
import { LoaderSuspense } from '@/components';
import { ExternalLink } from '@/components/ExternalLink';
import { Markdown } from '@/components/Markdown';
import { ExternalLink, LoaderSuspense, Markdown } from '@/components';
import { bindings } from '@/main';
import { abbreviateNumber, formatAsRelative, upperFirst, useCommand, useCommandMut, useCommandSuspense } from '@onelauncher/common';
import { Button, Show, Tooltip } from '@onelauncher/common/components';

View file

@ -1,6 +1,5 @@
import type { Key } from 'react-aria-components';
import { LoaderContainer, SheetPage, useSheetPageContext } from '@/components';
import { LogViewer } from '@/components/LogViewer';
import { LoaderContainer, LogViewer, SheetPage, useSheetPageContext } from '@/components';
import { bindings } from '@/main';
import { getMessageFromError, useCommand, useCommandSuspense } from '@onelauncher/common';
import { Dropdown } from '@onelauncher/common/components';
@ -17,7 +16,7 @@ function RouteComponent() {
const { data: fileNames } = useCommandSuspense(['getLogs', cluster.id], () => bindings.core.getLogs(cluster.id));
const [activeFileName, setActiveFileName] = useState<Key | undefined>(fileNames[0] || undefined);
const [activeFileName, setActiveFileName] = useState<Key | null>(fileNames[0] || null);
return (
<SheetPage.Content>

View file

@ -1,5 +1,5 @@
import type { ModCardContextApi } from '@/components/Bundle';
import { ModCardContext, ModList } from '@/components/Bundle';
import type { ModCardContextApi } from '@/components';
import { ModCardContext, ModList } from '@/components';
import { bindings } from '@/main';
import { useCommandSuspense } from '@onelauncher/common';
import { createFileRoute } from '@tanstack/react-router';

View file

@ -1,4 +1,4 @@
import { SheetPage } from '@/components/SheetPage';
import { SheetPage } from '@/components';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/app/cluster/overview')({

View file

@ -1,6 +1,5 @@
import type { LogViewerRef } from '@/components/LogViewer';
import { LoaderContainer, SheetPage, useSheetPageContext } from '@/components';
import { LogViewer } from '@/components/LogViewer';
import type { LogViewerRef } from '@/components';
import { LoaderContainer, LogViewer, SheetPage, useSheetPageContext } from '@/components';
import { bindings } from '@/main';
import { getMessageFromError, useAsyncEffect, useCommand } from '@onelauncher/common';
import { createFileRoute } from '@tanstack/react-router';

View file

@ -1,6 +1,4 @@
import { LoaderSuspense, Tab, TabList } from '@/components';
import { LaunchButton } from '@/components/LaunchButton';
import { SheetPage } from '@/components/SheetPage';
import { LaunchButton, LoaderSuspense, SheetPage, Tab, TabList } from '@/components';
import { useIsRunning } from '@/hooks/useClusters';
import { bindings } from '@/main';
import { prettifyLoader } from '@/utils/loaders';
@ -128,7 +126,7 @@ function HeaderLarge() {
</Button>
<LaunchButton
clusterId={cluster.id}
cluster={cluster}
size="large"
/>
</div>
@ -155,7 +153,7 @@ function HeaderSmall() {
</Button>
<LaunchButton
clusterId={cluster.id}
cluster={cluster}
color="primary"
/>
</div>

View file

@ -1,5 +1,4 @@
import { SheetPage } from '@/components';
import SettingsRow from '@/components/SettingsRow';
import { SettingsRow, SheetPage } from '@/components';
import { useClusterProfile } from '@/hooks/useSettings';
import { Switch, TextField } from '@onelauncher/common/components';
import { createFileRoute } from '@tanstack/react-router';

View file

@ -1,7 +1,5 @@
import type { GameLoader } from '@/bindings.gen';
import { GameBackground } from '@/components';
import { LaunchButton } from '@/components/LaunchButton';
import { SheetPage } from '@/components/SheetPage';
import { GameBackground, LaunchButton, SheetPage } from '@/components';
import { bindings } from '@/main';
import useClusterStore from '@/stores/clusterStore';
import { prettifyLoader } from '@/utils/loaders';
@ -105,6 +103,21 @@ function RouteComponent() {
});
}, [cluster, navigate]);
if (!cluster)
return (
<SheetPage
headerLarge={<HeaderLarge />}
headerSmall={<HeaderSmall />}
>
<div className="relative flex flex-row gap-4">
<SheetPage.Content className="sticky top-8 w-86 h-min flex flex-col p-2 gap-2 outline outline-ghost-overlay">
<p>Something wen't wrong while getting a cluster</p>
<p>Please reload and if this does not work please contact support</p>
</SheetPage.Content>
</div>
</SheetPage>
);
return (
<SheetPage
headerLarge={<HeaderLarge />}
@ -139,7 +152,7 @@ function RouteComponent() {
<GameBackground className="aspect-video w-full rounded-xl outline-2 outline-ghost-overlay" name={versionInfo.backgroundName} />
<div className="flex flex-col px-4 pt-2 pb-4 gap-2">
<h2 className="text-xxl font-medium">Version {cluster?.mc_version}</h2>
<h2 className="text-xxl font-medium">Version {cluster.mc_version}</h2>
<p className="text-sm text-fg-secondary">{versionInfo.longDescription}</p>
{minorVersions.length > 1 && (
@ -196,7 +209,7 @@ function RouteComponent() {
)}
<div className="w-full flex flex-row gap-4 mt-2">
<LaunchButton className="flex-1" clusterId={cluster?.id} size="large" />
<LaunchButton className="flex-1" cluster={cluster} size="large" />
<Button
className="flex-1"

View file

@ -1,7 +1,6 @@
import type { ClusterModel, GameLoader } from '@/bindings.gen';
import type { ButtonProps } from 'react-aria-components';
import { GameBackground } from '@/components';
import { LaunchButton } from '@/components/LaunchButton';
import { GameBackground, LaunchButton } from '@/components';
import { useActiveCluster, useLastPlayedClusters } from '@/hooks/useClusters';
import { bindings } from '@/main';
import useAppShellStore from '@/stores/appShellStore';
@ -40,7 +39,7 @@ function RouteComponent() {
<motion.div {...animations.slideInUp} className="flex flex-row transition-[height] h-52 gap-6">
{lastPlayedClusters.slice(0, 3).map(cluster => (
<RecentsCard
active={activeCluster?.id === cluster.id}
active={activeCluster.id === cluster.id}
key={cluster.folder_name}
loader={cluster.mc_loader}
onPress={() => setActiveClusterId(cluster.id)}
@ -60,20 +59,11 @@ function RouteComponent() {
);
}
function ActiveClusterInfo({
cluster,
}: {
cluster: ClusterModel | undefined;
}) {
const versionInfo = getVersionInfoOrDefault(cluster?.mc_version);
const navigate = useNavigate({
from: Route.id,
});
function ActiveClusterInfo({ cluster }: { cluster: ClusterModel }) {
const versionInfo = getVersionInfoOrDefault(cluster.mc_version);
const navigate = useNavigate({ from: Route.id });
const viewCluster = () => {
if (!cluster)
return;
navigate({
to: '/app/cluster/overview',
search: {
@ -93,14 +83,14 @@ function ActiveClusterInfo({
position: 'relative',
left: '-50%',
}}
key={(cluster?.mc_version ?? Math.random()) + (cluster?.mc_loader ?? '')}
key={(cluster.mc_version) + (cluster.mc_loader)}
transition={{ ease: 'backInOut', duration: 0.35 }}
>
<h1 className="text-6xl font-bold text-fg-primary">{cluster?.mc_version} {prettifyLoader(cluster?.mc_loader ?? 'vanilla')}</h1>
<h1 className="text-6xl font-bold text-fg-primary">{cluster.mc_version} {prettifyLoader(cluster.mc_loader)}</h1>
<p className="text-lg font-medium text-fg-secondary">{versionInfo.shortDescription}</p>
<div className="flex flex-row justify-center items-center gap-2">
<LaunchButton clusterId={cluster?.id} size="large" />
<LaunchButton cluster={cluster} size="large" />
<Button color="ghost" onPress={viewCluster} size="iconLarge">
<Settings04Icon />
@ -174,7 +164,7 @@ function Card({
onPress={onPress}
style={blur
? {
// shitty hack because webkit breaks with css variables in its backdrop filter
// shitty hack because webkit breaks with css variables in its backdrop filter
backdropFilter: `blur(${BLUR})`,
WebkitBackdropFilter: `blur(${BLUR})`,
...style,

View file

@ -1,6 +1,5 @@
import type { PropsWithChildren } from 'react';
import { LoaderSuspense, Navbar } from '@/components';
import { GameBackground } from '@/components/GameBackground';
import { GameBackground, LoaderSuspense, Navbar } from '@/components';
import { useActiveCluster } from '@/hooks/useClusters';
import { getVersionInfoOrDefault } from '@/utils/versionMap';
import { createFileRoute, Outlet } from '@tanstack/react-router';
@ -53,7 +52,7 @@ function AppShell({
function BackgroundGradient() {
const cluster = useActiveCluster();
const versionInfo = getVersionInfoOrDefault(cluster?.mc_version);
const versionInfo = getVersionInfoOrDefault(cluster.mc_version);
return (
<div className="relative">
@ -93,7 +92,7 @@ function BackgroundGradient() {
opacity: 0,
left: '-10%',
}}
key={(cluster?.mc_version ?? Math.random()) + (cluster?.mc_loader ?? '')}
key={(cluster.mc_version) + (cluster.mc_loader)}
>
<GameBackground
className="absolute left-0 top-0 w-screen h-screen scale-110"

View file

@ -1,11 +1,9 @@
// import ScrollableContainer from '@/components/ScrollableContainer';
import SettingsRow from '@/components/SettingsRow';
import SettingsSwitch from '@/components/SettingSwitch';
import { SettingsRow, SettingsSwitch } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { Sidebar } from '@/routes/app/settings/route';
import { THEMES } from '@/utils/theming';
import { createFileRoute } from '@tanstack/react-router';
import { Monitor01Icon, PackageIcon, Speedometer04Icon } from '@untitled-theme/icons-react';
import Sidebar from './route';
export const Route = createFileRoute('/app/settings/appearance')({
component: RouteComponent,

View file

@ -1,12 +1,10 @@
import { DebugInfo, Overlay } from '@/components/overlay';
import SettingsRow from '@/components/SettingsRow';
import SettingsSwitch from '@/components/SettingSwitch';
import { DebugInfo, Overlay, SettingsRow, SettingsSwitch } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { Sidebar } from '@/routes/app/settings/route';
import { Button } from '@onelauncher/common/components';
import { createFileRoute, Link } from '@tanstack/react-router';
import { BatteryEmptyIcon, BatteryFullIcon, Code02Icon, FileHeart02Icon, Truck01Icon } from '@untitled-theme/icons-react';
import Sidebar from './route';
export const Route = createFileRoute('/app/settings/developer')({
component: RouteComponent,

View file

@ -1,15 +1,12 @@
// import DiscordIcon from '@/assets/logos/discord.svg';
import SettingsRow from '@/components/SettingsRow';
import SettingsSwitch from '@/components/SettingSwitch';
import { SettingsRow, SettingsSwitch } from '@/components';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
// import useSettings from '@/hooks/useSettings';
import { Sidebar } from '@/routes/app/settings/route';
import { Button } from '@onelauncher/common/components';
import { createFileRoute } from '@tanstack/react-router';
import { dataDir, join } from '@tauri-apps/api/path';
import { FolderIcon, Link03Icon, LinkExternal01Icon } from '@untitled-theme/icons-react';
import { useEffect, useState } from 'react';
import Sidebar from './route';
export const Route = createFileRoute('/app/settings/')({
component: RouteComponent,

View file

@ -1,15 +1,14 @@
import type { SettingProfileModel } from '@/bindings.gen';
// import ScrollableContainer from '@/components/ScrollableContainer';
import SettingsRow from '@/components/SettingsRow';
import { SettingsRow } from '@/components';
import usePopState from '@/hooks/usePopState';
import { useSettings } from '@/hooks/useSettings';
import { bindings } from '@/main';
import { Sidebar } from '@/routes/app/settings/route';
import { useCommandMut, useCommandSuspense } from '@onelauncher/common';
import { Switch, TextField } from '@onelauncher/common/components';
import { createFileRoute } from '@tanstack/react-router';
import { Database01Icon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, XIcon } from '@untitled-theme/icons-react';
import { useMemo } from 'react';
import Sidebar from './route';
export const Route = createFileRoute('/app/settings/minecraft')({
component: RouteComponent,

View file

@ -67,7 +67,7 @@ interface SidebarProps {
exact?: boolean;
}
function Sidebar(props: SidebarProps) {
export function Sidebar(props: SidebarProps) {
const { base, links, exact } = props;
const navigate = useNavigate();

View file

@ -2,11 +2,11 @@ import type { MinecraftCredentials } from '@/bindings.gen';
import { AccountAvatar, SkinViewer } from '@/components';
import { usePlayerProfile } from '@/hooks/usePlayerProfile';
import { bindings } from '@/main';
import { OnboardingNavigation } from '@/routes/onboarding/route';
import { useCommandMut, useCommandSuspense } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { useQueryClient } from '@tanstack/react-query';
import { createFileRoute } from '@tanstack/react-router';
import { OnboardingNavigation } from './route';
export const Route = createFileRoute('/onboarding/account')({
component: RouteComponent,

View file

@ -1,6 +1,7 @@
import type { GameLoader, OnlineCluster, OnlineClusterEntry } from '@/bindings.gen';
import type { VersionInfo } from '@/utils/versionMap';
import { bindings } from '@/main';
import { OnboardingNavigation } from '@/routes/onboarding/route';
import { getVersionInfoOrDefault } from '@/utils/versionMap';
import { useCommandSuspense } from '@onelauncher/common';
import { createFileRoute } from '@tanstack/react-router';
@ -8,7 +9,6 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { useState } from 'react';
import { Button as AriaButton } from 'react-aria-components';
import { twMerge } from 'tailwind-merge';
import { OnboardingNavigation } from '../route';
export interface StrippedCLuster {
mc_version: string;

View file

@ -1,11 +1,9 @@
import type { ClusterModel, ModpackArchive, ModpackFile } from '@/bindings.gen';
import type { DownloadModsRef } from '@/components';
import type { ModCardContextApi, onClickOnMod } from '@/components/Bundle';
import type { StrippedCLuster } from './version';
import { DownloadMods } from '@/components';
import { ModCardContext } from '@/components/Bundle';
import { BundleModListModal, Overlay } from '@/components/overlay';
import type { DownloadModsRef, ModCardContextApi, onClickOnMod } from '@/components';
import type { StrippedCLuster } from '@/routes/onboarding/preferences/version';
import { BundleModListModal, DownloadMods, ModCardContext, Overlay } from '@/components';
import { bindings } from '@/main';
import { OnboardingNavigation } from '@/routes/onboarding/route';
import { useCommandSuspense } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';
import { createFileRoute } from '@tanstack/react-router';
@ -14,7 +12,6 @@ import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import { useMemo, useRef, useState } from 'react';
import { Button as AriaButton } from 'react-aria-components';
import { twMerge } from 'tailwind-merge';
import { OnboardingNavigation } from '../route';
export const Route = createFileRoute('/onboarding/preferences/versionCategory')({
component: RouteComponent,

View file

@ -1,10 +1,7 @@
import type { DownloadModsRef } from '@/components';
import type { PropsWithChildren } from 'react';
import LauncherLogo from '@/assets/logos/oneclient.svg?react';
import { LoaderSuspense, NavbarButton } from '@/components';
import { GameBackground } from '@/components/GameBackground';
import { Overlay, SuperSecretDevOptions } from '@/components/overlay';
import { Stepper } from '@/components/Stepper';
import { GameBackground, LoaderSuspense, NavbarButton, Overlay, Stepper, SuperSecretDevOptions } from '@/components';
import { bindings } from '@/main';
import { useCommandSuspense } from '@onelauncher/common';
import { Button } from '@onelauncher/common/components';

View file

@ -21,6 +21,21 @@
}
}
@utility partial-leftline-* {
position: relative;
&::after {
content: "";
background: currentColor;
position: absolute;
left: 0;
top: 0;
width: 3px;
height: --spacing(--value(integer));
height: --value(percentage, [percentage]);
}
}
@theme {
/** border */
--color-component-border: #ffffff1A;
@ -34,7 +49,7 @@
--radius-3xl: 2rem;
}
.prose-invert{
.prose-invert {
--tw-prose-body: var(--color-fg-primary);
--tw-prose-headings: var(--color-fg-primary);
--tw-prose-bold: var(--color-fg-primary);

View file

@ -1,20 +1,3 @@
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
let output = Command::new("git")
.args(&["rev-parse", "HEAD"])
.output()
.unwrap();
let git_hash = String::from_utf8(output.stdout).unwrap();
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
let start = SystemTime::now();
let timestamp = start
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp);
tauri_build::build();
}

View file

@ -46,6 +46,13 @@
"opener:allow-open-url",
"opener:default",
"os:default"
"os:allow-arch",
"os:deny-exe-extension",
"os:allow-family",
"os:deny-hostname",
"os:allow-locale",
"os:allow-os-type",
"os:allow-platform",
"os:allow-version"
]
}

View file

@ -32,6 +32,9 @@ gui = []
[lints]
workspace = true
[build-dependencies]
shadow-rs = { workspace = true, features = ["default"] }
[dependencies]
# core
interpulse = { workspace = true }
@ -74,6 +77,7 @@ paste = { workspace = true }
byteorder = { workspace = true }
merge = { workspace = true }
opener = { workspace = true }
shadow-rs = { workspace = true }
# integration
discord-rich-presence = { workspace = true }

5
packages/core/build.rs Normal file
View file

@ -0,0 +1,5 @@
use shadow_rs::ShadowBuilder;
fn main() {
ShadowBuilder::builder().build().unwrap();
}

View file

@ -196,6 +196,9 @@ pub trait TauriLauncherApi {
provider: Provider,
author: PackageAuthor,
) -> LauncherResult<Vec<ManagedUser>>;
#[taurpc(alias = "getLinkedPackages")]
async fn get_linked_packages(cluster_id: ClusterId) -> LauncherResult<Vec<packages::Model>>;
// endregion: package
// MARK: API: modpack
@ -248,6 +251,12 @@ pub trait TauriLauncherApi {
) -> LauncherResult<crate::utils::minecraft::MowojangProfile>;
// endregion: minecraft
// MARK: API: discord RPC
// region: discord RPC
#[taurpc(alias = "setDiscordRPCMessage")]
async fn set_discord_rpc_message(message: String) -> LauncherResult<()>;
// endregion: discord RPC
// MARK: API: Other
async fn open(input: String) -> LauncherResult<()>;
}
@ -731,6 +740,19 @@ impl TauriLauncherApi for TauriLauncherApiImpl {
Ok(model)
}
async fn get_linked_packages(
self,
cluster_id: ClusterId,
) -> LauncherResult<Vec<packages::Model>> {
let cluster = api::cluster::dao::get_cluster_by_id(cluster_id)
.await?
.ok_or_else(|| anyhow::anyhow!("cluster with id {} not found", cluster_id))?;
let model = api::packages::dao::get_linked_packages(&cluster).await?;
Ok(model)
}
// endregion: package
// MARK: Impl: modpack
@ -815,6 +837,22 @@ impl TauriLauncherApi for TauriLauncherApiImpl {
}
// endregion: minecraft
// MARK: Impl: discord RPC
// region: discord RPC
async fn set_discord_rpc_message(self, message: String) -> LauncherResult<()> {
let state = State::get().await?;
if let Some(discord) = &state.rpc {
if !state.settings.read().await.discord_enabled {
discord.clear_activity().await;
return Ok(());
}
discord.set_message(&message, None).await;
}
Ok(())
}
// endregion: discord RPC
// MARK: Impl: Other
// region: other
async fn open(self, input: String) -> LauncherResult<()> {

View file

@ -1,8 +1,6 @@
use tauri::Runtime;
use tauri_plugin_os::{arch, family, locale, platform, type_, version};
use crate::error::LauncherResult;
#[taurpc::procedures(path = "debug")]
pub trait TauriLauncherDebugApi {
#[taurpc(alias = "openDevTools")]
@ -11,9 +9,6 @@ pub trait TauriLauncherDebugApi {
#[taurpc(alias = "isInDev")]
async fn is_in_dev() -> bool;
#[taurpc(alias = "getPlatform")]
async fn get_platform() -> String;
#[taurpc(alias = "getArch")]
async fn get_arch() -> String;
@ -26,14 +21,20 @@ pub trait TauriLauncherDebugApi {
#[taurpc(alias = "getType")]
async fn get_type() -> String;
#[taurpc(alias = "getVersion")]
#[taurpc(alias = "getPlatform")]
async fn get_platform() -> String;
#[taurpc(alias = "getOsVersion")]
async fn get_version() -> String;
#[taurpc(alias = "getCommitHash")]
async fn get_commit_hash() -> LauncherResult<String>;
#[taurpc(alias = "getGitCommitHash")]
async fn get_git_commit_hash() -> String;
#[taurpc(alias = "getBuildTimestamp")]
async fn get_build_timestamp() -> LauncherResult<String>;
async fn get_build_timestamp() -> String;
#[taurpc(alias = "getPackageVersion")]
async fn get_package_version() -> String;
}
#[taurpc::ipc_type]
@ -49,10 +50,6 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl {
tauri::is_dev()
}
async fn get_platform(self) -> String {
platform().to_string()
}
async fn get_arch(self) -> String {
arch().to_string()
}
@ -69,21 +66,23 @@ impl TauriLauncherDebugApi for TauriLauncherDebugApiImpl {
type_().to_string()
}
async fn get_platform(self) -> String {
platform().to_string()
}
async fn get_version(self) -> String {
version().to_string()
}
async fn get_commit_hash(self) -> LauncherResult<String> {
if tauri::is_dev() {
let hash = std::env::var("GIT_HASH").map_err(anyhow::Error::from)?;
Ok(hash)
} else {
Ok("null".to_string())
}
async fn get_git_commit_hash(self) -> String {
crate::build::COMMIT_HASH.to_string()
}
async fn get_build_timestamp(self) -> LauncherResult<String> {
let timestamp = std::env::var("BUILD_TIMESTAMP").map_err(anyhow::Error::from)?;
Ok(timestamp)
async fn get_build_timestamp(self) -> String {
crate::build::BUILD_TIME.to_string()
}
async fn get_package_version(self) -> String {
crate::build::PKG_VERSION.to_string()
}
}

View file

@ -1,6 +1,9 @@
#![feature(slice_as_array)]
#![allow(clippy::struct_excessive_bools)]
shadow_rs::shadow!(build);
pub use build::*;
use api::proxy::LauncherProxy;
use error::LauncherResult;
pub use logger::start_logger;

View file

@ -30,7 +30,7 @@ impl Default for Settings {
Self {
global_game_settings: setting_profiles::Model::default_global_profile(),
allow_parallel_running_clusters: false,
discord_enabled: false,
discord_enabled: true,
seen_onboarding: false,
mod_list_use_grid: true,
parallel_mod_downloading: true,