mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-14 08:04:52 +01:00
Compare commits
2 Commits
v2
...
v2-remove-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c086828dc | ||
|
|
70999c093b |
@@ -11,10 +11,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.5.1",
|
||||
"color.js": "^1.2.0",
|
||||
"preact": "^10.16.0",
|
||||
"preact-router": "^4.1.2",
|
||||
"react-icons": "^4.12.0",
|
||||
"zustand": "^4.4.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
2108
pnpm-lock.yaml
generated
Normal file
2108
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
src-tauri/.gitignore
vendored
1
src-tauri/.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
WixTools/**/*
|
||||
|
||||
1289
src-tauri/Cargo.lock
generated
1289
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,9 @@ edition = "2021"
|
||||
tauri-build = { version = "1.5", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "1.5", features = ["api-all"] }
|
||||
tauri = { version = "1.5", features = ["shell-open"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
reqwest = "0.11.22"
|
||||
window-shadows = "0.2.2"
|
||||
|
||||
[features]
|
||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
/// Downloads a file from the given URL.
|
||||
/// Saves it to the specified file.
|
||||
/// This will overwrite the file if it already exists.
|
||||
/// url: The URL to download from.
|
||||
/// to_file: The file to save to.
|
||||
#[tauri::command]
|
||||
pub async fn download_file(url: String, to_file: String) -> Result<String, String> {
|
||||
let mut response = reqwest::get(&url).await
|
||||
.expect("Failed to send request");
|
||||
let mut dest = {
|
||||
let fname = Path::new(&to_file);
|
||||
match File::create(&fname) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Err("Failed to create file".to_string()),
|
||||
}
|
||||
};
|
||||
while let Some(chunk) = response.chunk().await
|
||||
.expect("Unable to read chunk") {
|
||||
dest.write_all(&chunk)
|
||||
.expect("Failed to write chunk to file")
|
||||
}
|
||||
Ok("Downloaded".to_string())
|
||||
}
|
||||
@@ -1,37 +1,15 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::fs::create_dir;
|
||||
use tauri::Manager;
|
||||
use window_shadows::set_shadow;
|
||||
|
||||
mod http;
|
||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
http::download_file
|
||||
])
|
||||
.setup(|app| {
|
||||
// Create base directories.
|
||||
let app_data_dir = app.path_resolver()
|
||||
.app_data_dir()
|
||||
.expect("Failed to get app data directory");
|
||||
|
||||
let background_directory = app_data_dir.join("bg");
|
||||
if !background_directory.exists() {
|
||||
create_dir(background_directory.as_path())
|
||||
.expect("Failed to create app data directory");
|
||||
}
|
||||
|
||||
// Enable window shadows.
|
||||
let main_window = app.get_window("main")
|
||||
.expect("Unable to fetch Tauri window.");
|
||||
#[cfg(any(windows, target_os = "macos"))]
|
||||
set_shadow(&main_window, true).unwrap();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
@@ -7,27 +7,20 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "Cultivation",
|
||||
"version": "0.1.0"
|
||||
"version": "0.0.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"fs": {
|
||||
"scope": ["$DATA", "$DATA/io.grasscutter.cultivation", "$DATA/io.grasscutter.cultivation/**"]
|
||||
},
|
||||
"protocol": {
|
||||
"all": true,
|
||||
"asset": true,
|
||||
"assetScope": ["$DATA", "$DATA/io.grasscutter.cultivation", "$DATA/io.grasscutter.cultivation/**"]
|
||||
},
|
||||
"http": {
|
||||
"scope": ["https://*.*/**"]
|
||||
},
|
||||
"all": true
|
||||
"all": false,
|
||||
"shell": {
|
||||
"all": false,
|
||||
"open": true
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"identifier": "io.grasscutter.cultivation",
|
||||
"identifier": "com.tauri.dev",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
@@ -43,7 +36,6 @@
|
||||
{
|
||||
"fullscreen": false,
|
||||
"resizable": true,
|
||||
"decorations": false,
|
||||
"title": "Cultivation",
|
||||
"width": 1280,
|
||||
"height": 730
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import useSettings from "@backend/stores/settings.ts";
|
||||
|
||||
import { create } from "zustand";
|
||||
import { persist, createJSONStorage } from "zustand/middleware";
|
||||
|
||||
import { invoke } from "@tauri-apps/api";
|
||||
import { fetch } from "@tauri-apps/api/http";
|
||||
import { exists } from "@tauri-apps/api/fs";
|
||||
import { convertFileSrc } from "@tauri-apps/api/tauri";
|
||||
|
||||
import { prominent } from "color.js";
|
||||
|
||||
import { LauncherResponse, StoreWrite, SupportedGames } from "@backend/types.ts";
|
||||
import { AppDataPath, LauncherUrls } from "@app/constants.ts";
|
||||
|
||||
export type GameDataStore = {
|
||||
backgroundHash: string;
|
||||
|
||||
fetchLatestBackground: () => Promise<void>;
|
||||
};
|
||||
|
||||
export const useGenshinStore = create<GameDataStore>()(
|
||||
persist(
|
||||
(set): GameDataStore => ({
|
||||
backgroundHash: "",
|
||||
fetchLatestBackground: () => fetchLatestBackground(
|
||||
set as StoreWrite, LauncherUrls.GENSHIN_IMPACT)
|
||||
}),
|
||||
{
|
||||
name: "genshin_data",
|
||||
storage: createJSONStorage(() => localStorage)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const useStarRailStore = create<GameDataStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
backgroundHash: "",
|
||||
fetchLatestBackground: () => fetchLatestBackground(
|
||||
set as StoreWrite, LauncherUrls.STAR_RAIL)
|
||||
}),
|
||||
{
|
||||
name: "starrail_data",
|
||||
storage: createJSONStorage(() => localStorage)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Fetches the latest background image.
|
||||
*
|
||||
* @param set The store write function.
|
||||
* @param serviceUrl The URL used for fetching the background.
|
||||
*/
|
||||
export async function fetchLatestBackground(set: StoreWrite, serviceUrl: string): Promise<void> {
|
||||
// Fetch the launcher data.
|
||||
const launcherData = await fetch<LauncherResponse>(serviceUrl);
|
||||
const responseData = launcherData.data;
|
||||
|
||||
// Check if the background exists on the system.
|
||||
const backgroundUrl = responseData.data.adv.background;
|
||||
const backgroundHash = backgroundUrl.split("/").pop()?.substring(0, 32);
|
||||
if (backgroundHash == undefined) throw new Error("Unable to find the hash of the background.");
|
||||
|
||||
// Check if the file exists on the system.
|
||||
const filePath = `${AppDataPath}/bg/${backgroundHash}.png`;
|
||||
if (!await exists(filePath)) {
|
||||
// Download the image.
|
||||
await invoke("download_file", {
|
||||
url: backgroundUrl, toFile: filePath
|
||||
});
|
||||
|
||||
// Update the store.
|
||||
set({ backgroundHash });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the file of the background.
|
||||
*
|
||||
* @param hash The hash of the background.
|
||||
*/
|
||||
export async function getBackgroundFile(hash: string): Promise<string> {
|
||||
return convertFileSrc(`${AppDataPath}/bg/${hash}.png`);
|
||||
}
|
||||
|
||||
type BackgroundData = {
|
||||
url: string;
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook which returns the URL of the locally cached background image.
|
||||
*/
|
||||
export function useBackground(): BackgroundData {
|
||||
const { selectedGame } = useSettings();
|
||||
const { backgroundHash, fetchLatestBackground } =
|
||||
selectedGame == SupportedGames.GenshinImpact ? useGenshinStore() : useStarRailStore();
|
||||
const [background, setBackground] = useState<string | null>(null);
|
||||
const [colorPalette, setColorPalette] = useState<string[] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (backgroundHash != "") {
|
||||
const filePath = await getBackgroundFile(backgroundHash);
|
||||
setBackground(filePath);
|
||||
setColorPalette(await prominent(filePath,
|
||||
{ amount: 5, format: "hex" }) as string[]);
|
||||
} else {
|
||||
fetchLatestBackground();
|
||||
}
|
||||
})();
|
||||
}, [backgroundHash, fetchLatestBackground]);
|
||||
|
||||
return {
|
||||
url: background ?? "",
|
||||
colors: colorPalette ?? []
|
||||
};
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import { create } from "zustand";
|
||||
import { createJSONStorage, persist } from "zustand/middleware";
|
||||
|
||||
import { SupportedGames } from "@backend/types.ts";
|
||||
|
||||
export type SettingsStore = {
|
||||
selectedGame: SupportedGames | any;
|
||||
|
||||
setGame: (game: SupportedGames) => void;
|
||||
};
|
||||
|
||||
const useSettings = create<SettingsStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
selectedGame: SupportedGames.GenshinImpact,
|
||||
|
||||
setGame: (selectedGame) => set({ selectedGame })
|
||||
}),
|
||||
{
|
||||
name: "settings",
|
||||
storage: createJSONStorage(() => localStorage)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export default useSettings;
|
||||
@@ -1,74 +0,0 @@
|
||||
export type StoreWrite = (partial: unknown, replace?: boolean | undefined) => void;
|
||||
|
||||
export type SDKResponse = {
|
||||
retcode: number;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type LauncherResponse = SDKResponse & {
|
||||
data: {
|
||||
adv: BackgroundData; // This is the background shown to the user.
|
||||
banner: NewsFeedData[]; // These are shown in a slideshow-style card.
|
||||
icon: IconData[]; // These are shown in the right sidebar.
|
||||
post: PostData[]; // These are shown in a multi-tab card.
|
||||
}
|
||||
};
|
||||
|
||||
export type BackgroundData = {
|
||||
background: string; // This is the direct URL to the background image.
|
||||
icon: string;
|
||||
url: string;
|
||||
version: string; // This should be parsed into a number.
|
||||
bg_checksum: string; // Unknown hash algorithm.
|
||||
};
|
||||
|
||||
export type NewsFeedData = {
|
||||
banner_id: string;
|
||||
name: string; // This is almost always blank.
|
||||
img: string; // This is a banner-sized image for the card.
|
||||
url: string; // This is the associated URL with the card.
|
||||
// It should be opened in the user's browser when clicked on.
|
||||
order: string; // This should be parsed into a number.
|
||||
// Shown to the user in the order of lowest -> highest.
|
||||
};
|
||||
|
||||
export type IconData = {
|
||||
icon_id: string;
|
||||
img: string;
|
||||
tittle: string; // Intentionally misspelled.
|
||||
url: string;
|
||||
qr_img: string;
|
||||
qr_desc: string; // This is almost always blank.
|
||||
img_hover: string;
|
||||
other_links: [];
|
||||
title: string;
|
||||
icon_link: string;
|
||||
links: {
|
||||
title: string;
|
||||
url: string;
|
||||
};
|
||||
enable_red_dot: boolean;
|
||||
red_dot_content: string;
|
||||
};
|
||||
|
||||
export type PostData = {
|
||||
post_id: string;
|
||||
type: PostType;
|
||||
tittle: string; // Intentionally misspelled.
|
||||
url: string; // This is where the user should be directed to.
|
||||
show_time: string; // This is a date formatted as mm/dd.
|
||||
order: string; // This should be parsed into a number.
|
||||
// Shown to the user in the order of lowest -> highest.
|
||||
title: string; // Redundant; same content as 'tittle'.
|
||||
};
|
||||
|
||||
export enum PostType {
|
||||
Info = "POST_TYPE_INFO",
|
||||
Activity = "POST_TYPE_ACTIVITY",
|
||||
Announcement = "POST_TYPE_ANNOUNCE"
|
||||
}
|
||||
|
||||
export enum SupportedGames {
|
||||
GenshinImpact = "genshin_impact",
|
||||
StarRail = "starrail"
|
||||
}
|
||||
@@ -1,13 +1,4 @@
|
||||
import { appDataDir } from "@tauri-apps/api/path";
|
||||
|
||||
export const AppDataPath = (await appDataDir()).slice(0, -1);
|
||||
|
||||
export const PageRoutes = {
|
||||
HOME: "/",
|
||||
SETTINGS: "/settings"
|
||||
};
|
||||
|
||||
export const LauncherUrls = {
|
||||
GENSHIN_IMPACT: "https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/content?filter_adv=false&key=gcStgarh&language=en-us&launcher_id=10",
|
||||
STAR_RAIL: "https://hkrpg-launcher-static.hoyoverse.com/hkrpg_global/mdk/launcher/api/content?filter_adv=false&key=vplOVX8Vn7cwG8yb&language=en-us&launcher_id=35"
|
||||
};
|
||||
|
||||
@@ -1,25 +1,16 @@
|
||||
import Router, { Route } from "preact-router";
|
||||
|
||||
import { Route, Router } from "preact-router";
|
||||
|
||||
import TopBar from "@components/TopBar.tsx";
|
||||
import Launcher from "@ui/layout/Launcher.tsx";
|
||||
|
||||
import { useBackground } from "@backend/stores/mihoyo.ts";
|
||||
|
||||
import { PageRoutes } from "@app/constants.ts";
|
||||
|
||||
import "@css/App.scss";
|
||||
|
||||
function App() {
|
||||
const background = useBackground();
|
||||
|
||||
return (
|
||||
<div class={"App"}
|
||||
style={{
|
||||
backgroundImage: `url(${background.url})`
|
||||
}}
|
||||
>
|
||||
<TopBar color={background.colors[2]} />
|
||||
<div className={"App"}>
|
||||
<TopBar />
|
||||
|
||||
<Router>
|
||||
<Route path={PageRoutes.HOME} component={Launcher} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
function InfoBoard() {
|
||||
return (
|
||||
<div class={"InfoBoard"}>
|
||||
<div className={"InfoBoard"}>
|
||||
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
import "@css/components/TopBar.scss";
|
||||
|
||||
interface IProps {
|
||||
color: string | null;
|
||||
}
|
||||
|
||||
function TopBar(props: IProps) {
|
||||
function TopBar() {
|
||||
return (
|
||||
<div class={"TopBar"} data-tauri-drag-region
|
||||
style={{ backgroundColor: `${props.color}55` }}
|
||||
>
|
||||
<div class={"flex flex-row gap-1 text-white"}>
|
||||
<p>Cultivation</p>
|
||||
<p>2.0.0</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
@apply w-full h-screen;
|
||||
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
padding: 0;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.App {
|
||||
@apply w-full h-screen bg-no-repeat bg-contain;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.TopBar {
|
||||
@apply flex flex-row justify-between items-center w-full;
|
||||
@apply pl-4 pr-4 backdrop-blur-3xl;
|
||||
|
||||
height: 40px;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.Launcher {
|
||||
@apply flex w-full h-full;
|
||||
}
|
||||
@@ -1,24 +1,18 @@
|
||||
import NewsFeed from "@components/NewsFeed.tsx";
|
||||
import InfoBoard from "@components/InfoBoard.tsx";
|
||||
|
||||
import "@css/layout/Launcher.scss";
|
||||
interface Props {}
|
||||
|
||||
function Launcher() {
|
||||
function Launcher(_props: Props) {
|
||||
return (
|
||||
<div class={"Launcher"}>
|
||||
<div class={"Launcher_Announcements"}>
|
||||
<div className={"App_Body"}>
|
||||
<div>
|
||||
<NewsFeed />
|
||||
<InfoBoard />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={"Launcher_Links"}>
|
||||
|
||||
</div>
|
||||
|
||||
<div class={"Launcher_QuickSettings"}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@ui/*": ["src/ui/*"],
|
||||
"@css/*": ["src/ui/css/*"],
|
||||
"@components/*": ["src/ui/components/*"],
|
||||
"@backend/*": ["src/backend/*"]
|
||||
"@backend/*": ["src/backend/*"],
|
||||
},
|
||||
},
|
||||
"include": ["src"],
|
||||
|
||||
Reference in New Issue
Block a user