diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index d088b93..ccfcd5d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -107,9 +107,9 @@ async fn parse_args(inp: &Vec) -> Result { if game_path.is_some() { if args.value_of("non-elevated-game")? { - system_helpers::run_un_elevated(game_path.unwrap().to_string(), Some(game_args)) + system_helpers::run_un_elevated(game_path.unwrap(), Some(game_args)) } else { - system_helpers::run_program(game_path.unwrap().to_string(), Some(game_args)) + system_helpers::run_program(game_path.unwrap(), Some(game_args)) } } } @@ -308,6 +308,7 @@ fn is_grasscutter_running() -> bool { !proc.is_empty() } +#[cfg(windows)] #[tauri::command] fn restart_grasscutter(window: tauri::Window) -> bool { let pid: usize = *GC_PID.lock().unwrap(); @@ -338,6 +339,11 @@ fn restart_grasscutter(window: tauri::Window) -> bool { } } +#[cfg(unix)] +#[tauri::command] +fn restart_grasscutter(window: tauri::Window) {} + +#[cfg(windows)] #[tauri::command] fn enable_grasscutter_watcher(window: tauri::Window, process: String) { let grasscutter_name = process.clone(); @@ -398,6 +404,12 @@ fn enable_grasscutter_watcher(window: tauri::Window, process: String) { }); } +#[cfg(unix)] +#[tauri::command] +fn enable_grasscutter_watcher(window: tauri::Window, process: String) { + let gc_pid = Pid::from(696969); +} + #[tauri::command] async fn connect(port: u16, certificate_path: String) { // Log message to console. diff --git a/src-tauri/src/patch.rs b/src-tauri/src/patch.rs index 789eaef..66c7bcc 100644 --- a/src-tauri/src/patch.rs +++ b/src-tauri/src/patch.rs @@ -53,9 +53,7 @@ pub async fn unpatch_game() -> bool { pub async fn get_game_rsa_path() -> Option { let config = config::get_config(); - if config.game_install_path.is_none() { - return None; - } + config.game_install_path.as_ref()?; let mut game_folder = PathBuf::from(config.game_install_path.unwrap()); game_folder.pop(); diff --git a/src-tauri/src/release.rs b/src-tauri/src/release.rs index dd36607..4341ba1 100644 --- a/src-tauri/src/release.rs +++ b/src-tauri/src/release.rs @@ -16,7 +16,8 @@ pub async fn get_latest_release() -> Release { .unwrap(); let text = response.text().await.unwrap(); - println!("Response: {}", text); + // This includes ip when github rate limits you, so avoid it for now to avoid leaks through screenshots + //println!("Response: {}", text); // Parse "tag_name" from JSON let json: serde_json::Value = serde_json::from_str(&text).unwrap(); diff --git a/src-tauri/src/system_helpers.rs b/src-tauri/src/system_helpers.rs index ff8bddf..c1a65a0 100644 --- a/src-tauri/src/system_helpers.rs +++ b/src-tauri/src/system_helpers.rs @@ -1,12 +1,14 @@ use duct::cmd; use ini::Ini; -use std::ffi::OsStr; use std::path::PathBuf; -use windows_service::service::{ServiceAccess, ServiceState::Stopped}; -use windows_service::service_manager::{ServiceManager, ServiceManagerAccess}; +use std::ffi::OsStr; #[cfg(windows)] -use registry::{Data, Hive, Security}; +use { + registry::{Data, Hive, Security}, + windows_service::service::{ServiceAccess, ServiceState::Stopped}, + windows_service::service_manager::{ServiceManager, ServiceManagerAccess}, +}; #[tauri::command] pub fn run_program(path: String, args: Option) { @@ -230,7 +232,7 @@ pub fn service_status(service: String) -> bool { } }; let status_result = my_service.query_status(); - if status_result.is_ok() { + if let Ok(..) = status_result { let status = status_result.unwrap(); println!("{} service status: {:?}", service, status.current_state); if status.current_state == Stopped { @@ -262,6 +264,12 @@ pub fn start_service(service: String) -> bool { true } +#[cfg(unix)] +#[tauri::command] +pub fn start_service(service: String) { + let started = OsStr::new("Started service!"); +} + #[cfg(windows)] #[tauri::command] pub fn stop_service(service: String) -> bool { @@ -281,6 +289,10 @@ pub fn stop_service(service: String) -> bool { true } +#[cfg(unix)] +#[tauri::command] +pub fn stop_service(service: String) {} + #[cfg(unix)] #[tauri::command] pub fn wipe_registry(_exec_name: String) {} diff --git a/src-tauri/src/web.rs b/src-tauri/src/web.rs index d8a70ad..14fbce8 100644 --- a/src-tauri/src/web.rs +++ b/src-tauri/src/web.rs @@ -1,21 +1,27 @@ +use http::header; +use once_cell::sync::Lazy; use reqwest::header::{CONTENT_TYPE, USER_AGENT}; +static CLIENT: Lazy = Lazy::new(|| { + let mut headers = header::HeaderMap::new(); + headers.insert(USER_AGENT, header::HeaderValue::from_static("cultivation")); + headers.insert( + CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ); + + let client = reqwest::Client::builder().default_headers(headers); + client.build().unwrap() +}); pub(crate) async fn query(site: &str) -> String { - let client = reqwest::Client::new(); - - let response = client + CLIENT .get(site) - .header(USER_AGENT, "cultivation") - .header(CONTENT_TYPE, "application/json") .send() .await - .ok(); - - if response.is_some() { - response.unwrap().text().await.unwrap() - } else { - false.to_string() - } + .expect("Failed to get web response") + .text() + .await + .unwrap() } #[tauri::command] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6bc39a5..9dc97f7 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,7 +7,7 @@ }, "package": { "productName": "Cultivation", - "version": "1.0.26" + "version": "1.0.27" }, "tauri": { "allowlist": { diff --git a/src/ui/Mods.css b/src/ui/Mods.css index 709ca26..520a3a8 100644 --- a/src/ui/Mods.css +++ b/src/ui/Mods.css @@ -1,6 +1,6 @@ .Mods { backdrop-filter: blur(10px); - height: 90%; + height: 80%; width: 100%; } diff --git a/src/ui/Mods.tsx b/src/ui/Mods.tsx index 3129ebc..ab43cc7 100644 --- a/src/ui/Mods.tsx +++ b/src/ui/Mods.tsx @@ -14,6 +14,8 @@ import Back from '../resources/icons/back.svg' import Menu from './components/menu/Menu' import BigButton from './components/common/BigButton' import Tr from '../utils/language' +import { ModPages } from './components/mods/ModPages' +import TextInput from './components/common/TextInput' interface IProps { downloadHandler: DownloadHandler @@ -23,8 +25,21 @@ interface IState { isDownloading: boolean category: string downloadList: { name: string; url: string; mod: ModData }[] | null + page: number + search: string } +const pages = [ + { + name: -1, + title: '<', + }, + { + name: 1, + title: '>', + }, +] + const headers = [ { name: 'ripe', @@ -46,17 +61,22 @@ const headers = [ * @TODO Categorizaiton/sorting (by likes, views, etc) */ export class Mods extends React.Component { + timeout: number constructor(props: IProps) { super(props) + this.timeout = 0 this.state = { isDownloading: false, category: '', downloadList: null, + page: 1, + search: '', } this.setCategory = this.setCategory.bind(this) this.addDownload = this.addDownload.bind(this) + this.setPage = this.setPage.bind(this) } async addDownload(mod: ModData) { @@ -111,6 +131,29 @@ export class Mods extends React.Component { ) } + async setPage(value: number) { + const current = this.state.page + if (current + value == 0) return + this.setState( + { + page: current + value, + }, + this.forceUpdate + ) + } + + async setSearch(text: string) { + if (this.timeout) clearTimeout(this.timeout) + this.timeout = window.setTimeout(() => { + this.setState( + { + search: text, + }, + this.forceUpdate + ) + }, 500) + } + render() { return (
@@ -162,7 +205,30 @@ export class Mods extends React.Component { - + {this.state.category != 'installed' && ( + <> +
+ { + this.setSearch(text) + }} + initalValue={''} + /> +
+ + + )} + +
) } diff --git a/src/ui/components/mods/ModList.tsx b/src/ui/components/mods/ModList.tsx index 76ecda1..91552f7 100644 --- a/src/ui/components/mods/ModList.tsx +++ b/src/ui/components/mods/ModList.tsx @@ -1,6 +1,6 @@ import React from 'react' import { getConfigOption } from '../../../utils/configuration' -import { getInstalledMods, getMods, ModData, PartialModData } from '../../../utils/gamebanana' +import { getAllMods, getInstalledMods, getMods, ModData, PartialModData } from '../../../utils/gamebanana' import { LoadingCircle } from './LoadingCircle' import './ModList.css' @@ -8,6 +8,8 @@ import { ModTile } from './ModTile' interface IProps { mode: string + page: number + search: string addDownload: (mod: ModData) => void } @@ -62,7 +64,17 @@ export class ModList extends React.Component { return } - const mods = await getMods(this.props.mode) + let mods: ModData[] + + if (!(this.props.search == '')) { + // idk the api so just filter all mods to search + mods = (await getAllMods(this.props.mode)).filter((mod) => + mod.name.toLowerCase().includes(this.props.search.toLowerCase()) + ) + } else { + mods = await getMods(this.props.mode, this.props.page) + } + const horny = await getConfigOption('horny_mode') this.setState({ diff --git a/src/ui/components/mods/ModPages.css b/src/ui/components/mods/ModPages.css new file mode 100644 index 0000000..a40a734 --- /dev/null +++ b/src/ui/components/mods/ModPages.css @@ -0,0 +1,66 @@ +.ModPages { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-around; + width: 100%; + z-index: 2; + position: relative; + + padding: 5px; + + font-size: 20px; + font-weight: bold; + color: #fff; + background: rgba(77, 77, 77, 0.6); +} + +.ModPagesTitle { + display: flex; + justify-content: center; + z-index: 3; + + width: 100%; + max-width: 20%; +} + +.ModPagesTitle:hover { + cursor: pointer; +} + +.ModPagesTitle.selected { + border-bottom: 0px solid #fff; +} + +.ModPagesPage { + position: absolute; + justify-self: center; + left: 50%; + margin-top: 10px; + transform: translateX(-50%); + text-align: center; + + padding: -5px; + z-index: 3; + + font-size: 15px; + font-weight: bold; + color: #fff; +} + +.ModPagesPage input { + text-align: center; + padding: 3px; + border-radius: 3px; + height: 18px; + font-size: 20px; + font-weight: bold; + color: #fff; + background: rgba(77, 77, 77, 0.6); +} + +.ModPagesPage .TextInputWrapper { + background: rgba(77, 77, 77, 0.6); + z-index: -1; + display: inline-block; +} diff --git a/src/ui/components/mods/ModPages.tsx b/src/ui/components/mods/ModPages.tsx new file mode 100644 index 0000000..79720d5 --- /dev/null +++ b/src/ui/components/mods/ModPages.tsx @@ -0,0 +1,54 @@ +import React from 'react' + +import './ModPages.css' + +interface IProps { + headers: { + title: string + name: number + }[] + onClick: (value: number) => void + defaultHeader: number +} + +interface IState { + selected: number +} + +export class ModPages extends React.Component { + constructor(props: IProps) { + super(props) + + this.state = { + selected: this.props.defaultHeader, + } + } + + setSelected(value: number) { + const current = this.state.selected + if (current + value == 0) return + this.setState({ + selected: current + value, + }) + + this.props.onClick(value) + } + + render() { + return ( +
+ {this.props.headers.map((header, index) => { + return ( +
this.setSelected(header.name)} + > + {header.title} +
+ ) + })} +
+ ) + } +} diff --git a/src/ui/components/news/NewsSection.tsx b/src/ui/components/news/NewsSection.tsx index 761b93c..237266b 100644 --- a/src/ui/components/news/NewsSection.tsx +++ b/src/ui/components/news/NewsSection.tsx @@ -124,7 +124,7 @@ export default class NewsSection extends React.Component { case 'latest_version': news = ( - Latest version: Grasscutter 1.4.6 - Cultivation 1.0.10 + Latest version: Grasscutter 1.4.8 - Cultivation 1.0.27 ) break diff --git a/src/utils/gamebanana.ts b/src/utils/gamebanana.ts index 61c332f..642dc07 100644 --- a/src/utils/gamebanana.ts +++ b/src/utils/gamebanana.ts @@ -117,7 +117,22 @@ interface ModDownload { containsExe: boolean } -export async function getMods(mode: string) { +export async function getMods(mode: string, page: number) { + let modList: GamebananaResponse[] = [] + + const resp = JSON.parse( + await invoke('list_submissions', { + mode, + page: '' + page, + }) + ) + + modList = [...modList, ...resp] + + return formatGamebananaData(modList) +} + +export async function getAllMods(mode: string) { let modList: GamebananaResponse[] = [] let hadMods = true let page = 1 @@ -134,13 +149,8 @@ export async function getMods(mode: string) { modList = [...modList, ...resp] page++ - - console.log(page) - console.log(resp) } - console.log(modList) - return formatGamebananaData(modList) }