feat: Program info
This commit is contained in:
parent
402d6d6041
commit
82830d02ee
17 changed files with 194 additions and 60 deletions
|
|
@ -26,6 +26,7 @@
|
|||
"@solidjs/router": "^0.13.5",
|
||||
"@tanstack/solid-query": "^5.45.0",
|
||||
"@tauri-apps/api": "^2.0.0-beta.13",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.1.0-beta.3",
|
||||
"@tauri-apps/plugin-shell": "github:tauri-apps/tauri-plugin-shell#v2",
|
||||
"@thisbeyond/solid-dnd": "^0.7.5",
|
||||
"@unocss/reset": "^0.61.0",
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ uuid = { workspace = true }
|
|||
|
||||
tracing = { workspace = true }
|
||||
tracing-error = { workspace = true }
|
||||
tauri-plugin-clipboard-manager = "2.1.0-beta.4"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.25.0"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,9 @@ fn main() {
|
|||
"get_clusters",
|
||||
"get_cluster",
|
||||
"get_manifest",
|
||||
"get_settings",
|
||||
"set_settings"
|
||||
"get_settings",
|
||||
"set_settings",
|
||||
"get_program_info"
|
||||
]),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"window:allow-toggle-maximize",
|
||||
"window:allow-internal-toggle-maximize",
|
||||
"event:default",
|
||||
"shell:allow-open"
|
||||
"shell:allow-open",
|
||||
"clipboard-manager:allow-write-text"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ permissions = [
|
|||
"allow-get-manifest",
|
||||
"allow-login-msa",
|
||||
"allow-get-settings",
|
||||
"allow-set-settings"
|
||||
"allow-set-settings",
|
||||
"allow-get-program-info"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
|||
use std::str::FromStr;
|
||||
|
||||
use chrono::DateTime;
|
||||
use onelauncher::constants::{NATIVE_ARCH, TARGET_OS, VERSION};
|
||||
use onelauncher::data::{ClusterMeta, Loader, PackageData, Settings};
|
||||
use onelauncher::store::{Cluster, ClusterPath};
|
||||
use onelauncher::{cluster, settings};
|
||||
|
|
@ -12,27 +13,22 @@ use uuid::Uuid;
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! collect_commands {
|
||||
() => {
|
||||
{
|
||||
use $crate::api::commands::*;
|
||||
tauri_specta::ts::builder()
|
||||
.config(specta::ts::ExportConfig::default().bigint(specta::ts::BigIntExportBehavior::BigInt))
|
||||
.commands(tauri_specta::collect_commands![
|
||||
is_dev,
|
||||
create_cluster,
|
||||
get_cluster,
|
||||
get_clusters,
|
||||
get_settings,
|
||||
set_settings,
|
||||
])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub fn is_dev() -> bool {
|
||||
cfg!(debug_assertions)
|
||||
() => {{
|
||||
use $crate::api::commands::*;
|
||||
tauri_specta::ts::builder()
|
||||
.config(
|
||||
specta::ts::ExportConfig::default()
|
||||
.bigint(specta::ts::BigIntExportBehavior::BigInt),
|
||||
)
|
||||
.commands(tauri_specta::collect_commands![
|
||||
create_cluster,
|
||||
get_cluster,
|
||||
get_clusters,
|
||||
get_settings,
|
||||
set_settings,
|
||||
get_program_info
|
||||
])
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
|
|
@ -128,11 +124,39 @@ pub async fn get_clusters() -> Result<HashMap<ClusterPath, Cluster>, String> {
|
|||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn get_settings() -> Result<Settings, String> {
|
||||
settings::get().await.map_err(|err| err.into())
|
||||
settings::get().await.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub async fn set_settings(settings: Settings) -> Result<(), String> {
|
||||
settings::set(settings).await.map_err(|err| err.into())
|
||||
settings::set(settings).await.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Type)]
|
||||
pub struct ProgramInfo {
|
||||
launcher_version: String,
|
||||
webview_version: String,
|
||||
tauri_version: String,
|
||||
dev_build: bool,
|
||||
platform: String,
|
||||
arch: String
|
||||
}
|
||||
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub fn get_program_info() -> ProgramInfo {
|
||||
let webview_version = tauri::webview_version().unwrap_or("UNKNOWN".into());
|
||||
let tauri_version = tauri::VERSION;
|
||||
let dev_build = tauri::is_dev();
|
||||
|
||||
ProgramInfo {
|
||||
launcher_version: VERSION.into(),
|
||||
webview_version,
|
||||
tauri_version: tauri_version.into(),
|
||||
dev_build,
|
||||
platform: TARGET_OS.into(),
|
||||
arch: NATIVE_ARCH.into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,13 +37,14 @@ pub async fn run() {
|
|||
|
||||
pub async fn run_app<F: FnOnce(&mut tauri::App) + Send + 'static>(setup: F) {
|
||||
let builder = tauri::Builder::default()
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
|
||||
println!("{}, {argv:?}, {cwd}", app.package_info().name);
|
||||
app.emit("single-instance", SingleInstancePayload { args: argv, cwd })
|
||||
.unwrap();
|
||||
}))
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {
|
||||
println!("{}, {argv:?}, {cwd}", app.package_info().name);
|
||||
app.emit("single-instance", SingleInstancePayload { args: argv, cwd })
|
||||
.unwrap();
|
||||
}))
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(ext::updater::plugin())
|
||||
.manage(ext::updater::State::default())
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
|
|
@ -66,9 +67,9 @@ pub async fn run_app<F: FnOnce(&mut tauri::App) + Send + 'static>(setup: F) {
|
|||
.build(tauri::tauri_build_context!())
|
||||
.expect("failed to build tauri application");
|
||||
|
||||
if let Err(err) = initialize_state(app.app_handle().clone()).await {
|
||||
tracing::error!("{err}");
|
||||
};
|
||||
if let Err(err) = initialize_state(app.app_handle().clone()).await {
|
||||
tracing::error!("{err}");
|
||||
};
|
||||
|
||||
app.run(|_app_handle, _event| {})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,19 @@ import UpdatesPage from './ui/pages/Updates';
|
|||
import ClusterRoot from '~ui/pages/cluster/ClusterRoot';
|
||||
import SettingsRoot from '~ui/pages/settings/SettingsRoot';
|
||||
import BrowserRoot from '~ui/pages/browser/BrowserRoot';
|
||||
import { initAppInfo } from '~utils/program-info';
|
||||
|
||||
export * as bridge from './bindings';
|
||||
|
||||
render(() => (
|
||||
<Router root={App}>
|
||||
<Route path="/" component={HomePage} />
|
||||
<Route path="/updates" component={UpdatesPage} />
|
||||
<Route path="/clusters" component={ClusterRoot} children={<ClusterRoot.Routes />} />
|
||||
<Route path="/settings" component={SettingsRoot} children={<SettingsRoot.Routes />} />
|
||||
<Route path="/browser" component={BrowserRoot} children={<BrowserRoot.Routes />} />
|
||||
</Router>
|
||||
), document.getElementById('root') as HTMLElement);
|
||||
initAppInfo()
|
||||
.then(() => {
|
||||
render(() => (
|
||||
<Router root={App}>
|
||||
<Route path="/" component={HomePage} />
|
||||
<Route path="/updates" component={UpdatesPage} />
|
||||
<Route path="/clusters" component={ClusterRoot} children={<ClusterRoot.Routes />} />
|
||||
<Route path="/settings" component={SettingsRoot} children={<SettingsRoot.Routes />} />
|
||||
<Route path="/browser" component={BrowserRoot} children={<BrowserRoot.Routes />} />
|
||||
</Router>
|
||||
), document.getElementById('root') as HTMLElement);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@ import ErrorBoundary from './components/ErrorBoundary';
|
|||
import AnimatedRoutes from './components/AnimatedRoutes';
|
||||
import NotificationOverlay from './components/overlay/notifications/NotificationOverlay';
|
||||
import { SettingsProvider } from './hooks/useSettings';
|
||||
import { bridge } from '~index';
|
||||
import { AppInfo } from '~utils/program-info';
|
||||
|
||||
function App(props: ParentProps) {
|
||||
if (!bridge.commands.isDev()) {
|
||||
if (AppInfo.dev_build !== true)
|
||||
document.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsProvider>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
content: attr(data-text);
|
||||
@apply pointer-events-none;
|
||||
@apply opacity-0 transition-opacity;
|
||||
@apply absolute left-1/2 -translate-x-1/2 bottom-6;
|
||||
@apply absolute left-1/2 -translate-x-1/2 top-0 -translate-y-6/5;
|
||||
@apply min-w-44 w-max max-w-52;
|
||||
@apply backdrop-blur-lg rounded-md bg-black/60 p-2 text-center text-sm text-wrap text-white shadow shadow-black/30;
|
||||
@apply backdrop-blur-lg rounded-md bg-black/50 p-2 text-center text-sm text-wrap text-white shadow shadow-black/30;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ function Tooltip(props: TooltipProps) {
|
|||
const [split, rest] = splitProps(props, ['class']);
|
||||
|
||||
return (
|
||||
<div data-text={props.text} class={`${styles.tooltip} ${split.class}`} {...rest} />
|
||||
<div data-text={props.text} class={`${styles.tooltip} ${split.class || ''}`} {...rest} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
import type { ParentProps } from 'solid-js';
|
||||
import { Brush01Icon, CodeSnippet02Icon, Globe01Icon, Key01Icon, MessageTextSquare01Icon, RefreshCcw02Icon, Rocket02Icon, Sliders04Icon, Users01Icon } from '@untitled-theme/icons-solid';
|
||||
import { type ParentProps, createSignal } from 'solid-js';
|
||||
import { Brush01Icon, CodeSnippet02Icon, MessageTextSquare01Icon, RefreshCcw02Icon, Rocket02Icon, Sliders04Icon, Users01Icon } from '@untitled-theme/icons-solid';
|
||||
import { Route } from '@solidjs/router';
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
|
||||
import Sidebar from '../../components/Sidebar';
|
||||
import SettingsGeneral from './launcher/SettingsGeneral';
|
||||
import SettingsAppearance from './launcher/SettingsAppearance';
|
||||
import SettingsAccounts from './game/SettingsAccounts';
|
||||
import SettingsMinecraft from './game/SettingsMinecraft';
|
||||
import SettingsDeveloper from './about/SettingsDeveloper';
|
||||
import SettingsFeedback from './about/SettingsFeedback';
|
||||
import SettingsChangelog from './about/SettingsChangelog';
|
||||
import AnimatedRoutes from '~ui/components/AnimatedRoutes';
|
||||
import ErrorBoundary from '~ui/components/ErrorBoundary';
|
||||
import Tooltip from '~ui/components/base/Tooltip';
|
||||
import { bridge } from '~index';
|
||||
import useCommand from '~ui/hooks/useCommand';
|
||||
import { AppInfo } from '~utils/program-info';
|
||||
|
||||
function SettingsRoutes() {
|
||||
return (
|
||||
|
|
@ -22,6 +29,8 @@ function SettingsRoutes() {
|
|||
<Route path="/accounts" component={SettingsAccounts} />
|
||||
|
||||
{/* About */}
|
||||
<Route path="/feedback" component={SettingsFeedback} />
|
||||
<Route path="/changelog" component={SettingsChangelog} />
|
||||
<Route path="/developer" component={SettingsDeveloper} />
|
||||
</>
|
||||
);
|
||||
|
|
@ -30,7 +39,7 @@ function SettingsRoutes() {
|
|||
function SettingsRoot(props: ParentProps) {
|
||||
return (
|
||||
<div class="flex flex-row flex-1 h-full gap-x-7">
|
||||
<div class="mt-8">
|
||||
<div class="mt-8 flex flex-col justify-between">
|
||||
<Sidebar
|
||||
base="/settings"
|
||||
state={{}}
|
||||
|
|
@ -38,8 +47,8 @@ function SettingsRoot(props: ParentProps) {
|
|||
'Launcher Settings': [
|
||||
[<Rocket02Icon />, 'General', '/'],
|
||||
[<Brush01Icon />, 'Appearance', '/appearance'],
|
||||
[<Key01Icon />, 'APIs', '/apis'],
|
||||
[<Globe01Icon />, 'Language', '/language'],
|
||||
// [<Key01Icon />, 'APIs', '/apis'],
|
||||
// [<Globe01Icon />, 'Language', '/language'],
|
||||
],
|
||||
'Game Settings': [
|
||||
[<Sliders04Icon />, 'Minecraft settings', '/minecraft'],
|
||||
|
|
@ -52,6 +61,7 @@ function SettingsRoot(props: ParentProps) {
|
|||
],
|
||||
}}
|
||||
/>
|
||||
<Info />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full h-full">
|
||||
|
|
@ -65,6 +75,36 @@ function SettingsRoot(props: ParentProps) {
|
|||
);
|
||||
}
|
||||
|
||||
function Info() {
|
||||
const [copied, setCopied] = createSignal(false);
|
||||
|
||||
return (
|
||||
<div class="mx-1 my-2 flex flex-col justify-center items-center relative">
|
||||
<Tooltip text={copied() ? 'Copied to clipboard!' : 'Copy to clipboard'}>
|
||||
<div
|
||||
class="whitespace-pre-line line-height-normal text-sm text-fg-secondary"
|
||||
onClick={(e) => {
|
||||
const info = e.target.innerHTML;
|
||||
writeText(info)
|
||||
.finally(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 3000);
|
||||
});
|
||||
}}
|
||||
>
|
||||
{`
|
||||
Launcher Version: ${AppInfo.launcher_version}
|
||||
Tauri Version: ${AppInfo.tauri_version}
|
||||
Webview Version: ${AppInfo.webview_version}
|
||||
Platform: ${AppInfo.platform} ${AppInfo.arch} bit
|
||||
Build: ${AppInfo.dev_build ? 'dev' : 'release'}
|
||||
`.trim()}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SettingsRoot.Routes = SettingsRoutes;
|
||||
|
||||
export default SettingsRoot;
|
||||
|
|
|
|||
33
desktop/src/ui/pages/settings/about/SettingsChangelog.tsx
Normal file
33
desktop/src/ui/pages/settings/about/SettingsChangelog.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
|
||||
function SettingsChangelog() {
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Changelog</h1>
|
||||
<ScrollableContainer>
|
||||
<UpdatePost />
|
||||
<UpdatePost />
|
||||
<UpdatePost />
|
||||
</ScrollableContainer>
|
||||
</Sidebar.Page>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Implement markdown support?
|
||||
function UpdatePost() {
|
||||
return (
|
||||
<div class="bg-component-bg p-4 rounded-xl flex-1 flex flex-col gap-2 line-height-relaxed whitespace-pre-line">
|
||||
<h2>New Feature</h2>
|
||||
<p class="">
|
||||
{`
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus fringilla, enim nec lobortis ornare, dui dolor convallis ante, eget blandit dolor purus eget nisi. Nullam malesuada sem purus, nec placerat velit luctus nec. Nullam quis consequat mi. Pellentesque in mi nec ligula rhoncus ullamcorper. Morbi vehicula, magna in lobortis imperdiet, diam orci sodales massa, sed convallis sapien diam id lorem. Cras condimentum tincidunt felis non suscipit. Morbi rhoncus enim nec felis sagittis, sed semper tortor interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut sed lacinia est. Etiam a est vitae diam laoreet dapibus. Cras eu dignissim felis. Nam quis magna tempor, convallis nibh eu, sagittis quam.
|
||||
|
||||
Aliquam ullamcorper libero sem, ac pellentesque nulla gravida eget. Phasellus volutpat lectus sit amet dictum gravida. Vivamus ligula magna, venenatis vitae elementum ut, elementum quis dolor. Pellentesque congue urna nibh, quis mollis nunc sagittis eu. Maecenas lacinia, velit quis euismod pulvinar, eros diam eleifend metus, quis porta nisi ligula ut ipsum. Proin eget diam mi. Cras sed rutrum est, in pellentesque lacus. Donec vitae egestas nibh. Vivamus vulputate convallis dui, vitae tempus velit sagittis eget. Ut sit amet magna a risus ultrices sagittis. Duis imperdiet erat felis, a pretium velit egestas in. Nulla facilisi. Praesent fringilla sem orci, ac elementum magna sollicitudin at. Phasellus convallis gravida est, at venenatis quam maximus id. Cras euismod purus vel massa consequat, sit amet porttitor odio accumsan.
|
||||
`.trim()}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsChangelog;
|
||||
11
desktop/src/ui/pages/settings/about/SettingsFeedback.tsx
Normal file
11
desktop/src/ui/pages/settings/about/SettingsFeedback.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import Sidebar from '~ui/components/Sidebar';
|
||||
|
||||
function SettingsFeedback() {
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Feedback</h1>
|
||||
</Sidebar.Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsFeedback;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export const isDev = () => window.location.host.startsWith('localhost:');
|
||||
|
||||
export default { isDev };
|
||||
10
desktop/src/utils/program-info.ts
Normal file
10
desktop/src/utils/program-info.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import type { ProgramInfo } from '~bindings';
|
||||
import { bridge } from '~index';
|
||||
|
||||
// eslint-disable-next-line import/no-mutable-exports -- because yes
|
||||
export let AppInfo!: ProgramInfo;
|
||||
export default AppInfo;
|
||||
|
||||
export async function initAppInfo() {
|
||||
AppInfo = await bridge.commands.getProgramInfo();
|
||||
}
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
|
|
@ -107,6 +107,9 @@ importers:
|
|||
'@tauri-apps/api':
|
||||
specifier: ^2.0.0-beta.13
|
||||
version: 2.0.0-beta.13
|
||||
'@tauri-apps/plugin-clipboard-manager':
|
||||
specifier: 2.1.0-beta.3
|
||||
version: 2.1.0-beta.3
|
||||
'@tauri-apps/plugin-shell':
|
||||
specifier: github:tauri-apps/tauri-plugin-shell#v2
|
||||
version: https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5b496abe090f771847887edc8dd2e5eeee0af270
|
||||
|
|
@ -1075,6 +1078,9 @@ packages:
|
|||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@tauri-apps/plugin-clipboard-manager@2.1.0-beta.3':
|
||||
resolution: {integrity: sha512-j48jTdZWeX5BylIGLYbXtg6ynZAcFKDKK6dguHAbG3zviqNE/XMf2vHOPJeFDZsDE5kVOZZS7nKOj7T2WiCpvw==}
|
||||
|
||||
'@tauri-apps/plugin-shell@https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5b496abe090f771847887edc8dd2e5eeee0af270':
|
||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5b496abe090f771847887edc8dd2e5eeee0af270}
|
||||
version: 2.0.0-beta.2
|
||||
|
|
@ -4234,6 +4240,10 @@ snapshots:
|
|||
'@tauri-apps/cli-win32-ia32-msvc': 2.0.0-beta.20
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.0.0-beta.20
|
||||
|
||||
'@tauri-apps/plugin-clipboard-manager@2.1.0-beta.3':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.0-beta.13
|
||||
|
||||
'@tauri-apps/plugin-shell@https://codeload.github.com/tauri-apps/tauri-plugin-shell/tar.gz/5b496abe090f771847887edc8dd2e5eeee0af270':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.0.0-beta.8
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue