mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-10 04:22:13 +10:00
refactor(game status): transient vs synced state now defined
This commit is contained in:
@ -30,8 +30,8 @@ import { GameStatusEnum, type GameStatus } from "~/types.js";
|
||||
const props = defineProps<{ status: GameStatus }>();
|
||||
const emit = defineEmits<{
|
||||
(e: "install"): void;
|
||||
(e: "cancel"): void;
|
||||
(e: "play"): void;
|
||||
(e: "queue"): void;
|
||||
}>();
|
||||
|
||||
const styles: { [key in GameStatusEnum]: string } = {
|
||||
@ -71,11 +71,11 @@ const buttonIcons: { [key in GameStatusEnum]: Component } = {
|
||||
|
||||
const buttonActions: { [key in GameStatusEnum]: () => void } = {
|
||||
[GameStatusEnum.Remote]: () => emit("install"),
|
||||
[GameStatusEnum.Queued]: () => emit("cancel"),
|
||||
[GameStatusEnum.Downloading]: () => emit("cancel"),
|
||||
[GameStatusEnum.Queued]: () => emit("queue"),
|
||||
[GameStatusEnum.Downloading]: () => emit("queue"),
|
||||
[GameStatusEnum.SetupRequired]: () => {},
|
||||
[GameStatusEnum.Installed]: () => emit("play"),
|
||||
[GameStatusEnum.Updating]: () => emit("cancel"),
|
||||
[GameStatusEnum.Updating]: () => emit("queue"),
|
||||
[GameStatusEnum.Uninstalling]: () => {},
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,14 +1,36 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import type { Game, GameStatus } from "~/types";
|
||||
import type { Game, GameStatus, GameStatusEnum } from "~/types";
|
||||
|
||||
const gameRegistry: { [key: string]: Game } = {};
|
||||
|
||||
const gameStatusRegistry: { [key: string]: Ref<GameStatus> } = {};
|
||||
|
||||
type OptionGameStatus = { [key in GameStatusEnum]: { version_name?: string } };
|
||||
export type SerializedGameStatus = [
|
||||
{ type: GameStatusEnum },
|
||||
OptionGameStatus | null
|
||||
];
|
||||
|
||||
const parseStatus = (status: SerializedGameStatus): GameStatus => {
|
||||
if (status[0]) {
|
||||
return {
|
||||
type: status[0].type,
|
||||
};
|
||||
} else if (status[1]) {
|
||||
const [[gameStatus, options]] = Object.entries(status[1]);
|
||||
return {
|
||||
type: gameStatus as GameStatusEnum,
|
||||
...options,
|
||||
};
|
||||
} else {
|
||||
throw new Error("No game status");
|
||||
}
|
||||
};
|
||||
|
||||
export const useGame = async (id: string) => {
|
||||
if (!gameRegistry[id]) {
|
||||
const data: { game: Game; status: GameStatus } = await invoke(
|
||||
const data: { game: Game; status: SerializedGameStatus } = await invoke(
|
||||
"fetch_game",
|
||||
{
|
||||
id,
|
||||
@ -16,11 +38,13 @@ export const useGame = async (id: string) => {
|
||||
);
|
||||
gameRegistry[id] = data.game;
|
||||
if (!gameStatusRegistry[id]) {
|
||||
gameStatusRegistry[id] = ref(data.status);
|
||||
gameStatusRegistry[id] = ref(parseStatus(data.status));
|
||||
|
||||
listen(`update_game/${id}`, (event) => {
|
||||
const payload: { status: GameStatus } = event.payload as any;
|
||||
gameStatusRegistry[id].value = payload.status;
|
||||
const payload: {
|
||||
status: SerializedGameStatus;
|
||||
} = event.payload as any;
|
||||
gameStatusRegistry[id].value = parseStatus(payload.status);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,10 +18,14 @@ export function setupHooks() {
|
||||
router.push("/store");
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
document.addEventListener("contextmenu", (event) => {
|
||||
event.target?.dispatchEvent(new Event("contextmenu"));
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
export function initialNavigation(state: Ref<AppState>) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<div class="absolute flex top-0 h-fit inset-x-0 z-[-20]">
|
||||
<img :src="bannerUrl" class="w-full h-auto object-cover" />
|
||||
<h1
|
||||
class="absolute inset-x-0 w-fit mx-auto text-center top-32 -translate-y-[50%] text-4xl text-zinc-100 font-bold font-display z-50 p-4 shadow-xl bg-zinc-900/80 rounded"
|
||||
class="absolute inset-x-0 w-fit mx-auto text-center top-32 -translate-y-[50%] text-4xl text-zinc-100 font-bold font-display z-50 p-4 shadow-xl bg-zinc-900/80 rounded-xl"
|
||||
>
|
||||
{{ game.mName }}
|
||||
</h1>
|
||||
@ -17,40 +17,23 @@
|
||||
<!-- main page -->
|
||||
<div class="w-full min-h-screen mx-auto bg-zinc-900 px-5 py-6">
|
||||
<!-- game toolbar -->
|
||||
<div>
|
||||
<div class="h-full flex flex-row gap-x-4 items-stretch">
|
||||
<GameStatusButton
|
||||
@install="() => installFlow()"
|
||||
@play="() => play()"
|
||||
@queue="() => queue()"
|
||||
:status="status"
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
:href="remoteUrl"
|
||||
target="_blank"
|
||||
type="button"
|
||||
class="inline-flex items-center rounded-md bg-zinc-800/50 px-4 font-semibold text-white shadow-sm hover:bg-zinc-800/80 uppercase font-display"
|
||||
>
|
||||
<BuildingStorefrontIcon class="mr-2 size-5" aria-hidden="true" />
|
||||
|
||||
<div class="flex flex-row">
|
||||
<div>
|
||||
<div
|
||||
v-if="showPreview"
|
||||
v-html="previewHTML"
|
||||
class="mt-12 prose prose-invert prose-blue max-w-none"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
v-html="descriptionHTML"
|
||||
class="mt-12 prose prose-invert prose-blue max-w-none"
|
||||
/>
|
||||
|
||||
<button
|
||||
v-if="showReadMore"
|
||||
class="mt-8 w-full inline-flex items-center gap-x-6"
|
||||
@click="() => (showPreview = !showPreview)"
|
||||
>
|
||||
<div class="grow h-[1px] bg-zinc-700 rounded-full" />
|
||||
<span
|
||||
class="uppercase text-sm font-semibold font-display text-zinc-600"
|
||||
>Click to read {{ showPreview ? "more" : "less" }}</span
|
||||
>
|
||||
<div class="grow h-[1px] bg-zinc-700 rounded-full" />
|
||||
</button>
|
||||
</div>
|
||||
Store
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -349,45 +332,23 @@ import {
|
||||
ListboxOptions,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import { BuildingStorefrontIcon } from "@heroicons/vue/24/outline";
|
||||
import { XCircleIcon } from "@heroicons/vue/24/solid";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import moment from "moment";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const id = route.params.id.toString();
|
||||
|
||||
const { game: rawGame, status } = await useGame(id);
|
||||
const game = ref(rawGame);
|
||||
|
||||
const remoteUrl: string = await invoke("gen_drop_url", {
|
||||
path: `/store/${game.value.id}`,
|
||||
});
|
||||
|
||||
const bannerUrl = await useObject(game.value.mBannerId);
|
||||
|
||||
const md = MarkdownIt();
|
||||
|
||||
const showPreview = ref(true);
|
||||
const gameDescriptionCharacters = game.value.mDescription.split("");
|
||||
|
||||
// First new line after x characters
|
||||
const descriptionSplitIndex = gameDescriptionCharacters.findIndex(
|
||||
(v, i, arr) => {
|
||||
// If we're at the last element, we return true.
|
||||
// So we don't have to handle a -1 from this findIndex
|
||||
if (i + 1 == arr.length) return true;
|
||||
if (i < 500) return false;
|
||||
if (v != "\n") return false;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
const previewDescription = gameDescriptionCharacters
|
||||
.slice(0, descriptionSplitIndex + 1) // Slice a character after
|
||||
.join("");
|
||||
const previewHTML = md.render(previewDescription);
|
||||
|
||||
const descriptionHTML = md.render(game.value.mDescription);
|
||||
|
||||
const showReadMore = previewHTML != descriptionHTML;
|
||||
|
||||
const installFlowOpen = ref(false);
|
||||
const versionOptions = ref<
|
||||
undefined | Array<{ versionName: string; platform: string }>
|
||||
@ -432,8 +393,11 @@ async function play() {
|
||||
try {
|
||||
await invoke("launch_game", { gameId: game.value.id });
|
||||
} catch (e) {
|
||||
game.value.mName = e as string;
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function queue() {
|
||||
router.push("/queue");
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
<li
|
||||
v-if="games[element.id]"
|
||||
:key="element.id"
|
||||
class="mb-4 bg-zinc-900 rounded-lg relative flex justify-between gap-x-6 py-5 px-4"
|
||||
class="mb-4 bg-zinc-900 rounded-lg flex flex-row justify-between gap-x-6 py-5 px-4"
|
||||
>
|
||||
<div class="flex items-center max-w-md gap-x-4">
|
||||
<div class="w-full flex items-center max-w-md gap-x-4 relative">
|
||||
<img
|
||||
class="size-24 flex-none bg-zinc-800 object-cover rounded"
|
||||
:src="games[element.id].cover"
|
||||
@ -15,7 +15,7 @@
|
||||
/>
|
||||
<div class="min-w-0 flex-auto">
|
||||
<p class="text-xl font-semibold text-zinc-100">
|
||||
<NuxtLink :href="`/library/${element.id}`">
|
||||
<NuxtLink :href="`/library/${element.id}`" class="">
|
||||
<span class="absolute inset-x-0 -top-px bottom-0" />
|
||||
{{ games[element.id].game.mName }}
|
||||
</NuxtLink>
|
||||
@ -40,10 +40,12 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRightIcon
|
||||
class="size-5 flex-none text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<button @click="() => cancelGame(element.id)" class="group">
|
||||
<XMarkIcon
|
||||
class="transition size-8 flex-none text-zinc-600 group-hover:text-zinc-300"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<p v-else>Loading...</p>
|
||||
@ -59,6 +61,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { XMarkIcon } from "@heroicons/vue/20/solid";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import type { Game, GameStatus } from "~/types";
|
||||
|
||||
@ -94,4 +97,8 @@ async function onEnd(event: { oldIndex: number; newIndex: number }) {
|
||||
newIndex: event.newIndex,
|
||||
});
|
||||
}
|
||||
|
||||
async function cancelGame(id: string) {
|
||||
await invoke("cancel_game", { gameId: id });
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -24,11 +24,8 @@ pub struct DatabaseAuth {
|
||||
// Strings are version names for a particular game
|
||||
#[derive(Serialize, Clone, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DatabaseGameStatus {
|
||||
pub enum GameStatus {
|
||||
Remote {},
|
||||
Downloading {
|
||||
version_name: String,
|
||||
},
|
||||
SetupRequired {
|
||||
version_name: String,
|
||||
install_dir: String,
|
||||
@ -37,10 +34,14 @@ pub enum DatabaseGameStatus {
|
||||
version_name: String,
|
||||
install_dir: String,
|
||||
},
|
||||
Updating {
|
||||
version_name: String,
|
||||
},
|
||||
}
|
||||
|
||||
// Stuff that shouldn't be synced to disk
|
||||
#[derive(Clone, Serialize)]
|
||||
pub enum GameTransientStatus {
|
||||
Downloading { version_name: String },
|
||||
Uninstalling {},
|
||||
Updating { version_name: String },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -58,8 +59,11 @@ pub struct GameVersion {
|
||||
pub struct DatabaseGames {
|
||||
pub install_dirs: Vec<String>,
|
||||
// Guaranteed to exist if the game also exists in the app state map
|
||||
pub games_statuses: HashMap<String, DatabaseGameStatus>,
|
||||
pub game_versions: HashMap<String, HashMap<String, GameVersion>>,
|
||||
pub statuses: HashMap<String, GameStatus>,
|
||||
pub versions: HashMap<String, HashMap<String, GameVersion>>,
|
||||
|
||||
#[serde(skip)]
|
||||
pub transient_statuses: HashMap<String, GameTransientStatus>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone, Deserialize)]
|
||||
@ -119,8 +123,9 @@ impl DatabaseImpls for DatabaseInterface {
|
||||
base_url: "".to_string(),
|
||||
games: DatabaseGames {
|
||||
install_dirs: vec![games_base_dir.to_str().unwrap().to_string()],
|
||||
games_statuses: HashMap::new(),
|
||||
game_versions: HashMap::new(),
|
||||
statuses: HashMap::new(),
|
||||
transient_statuses: HashMap::new(),
|
||||
versions: HashMap::new(),
|
||||
},
|
||||
};
|
||||
debug!(
|
||||
|
||||
@ -40,6 +40,11 @@ pub fn move_game_in_queue(
|
||||
.rearrange(old_index, new_index)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn cancel_game(state: tauri::State<'_, Mutex<AppState>>, game_id: String) {
|
||||
state.lock().unwrap().download_manager.cancel(game_id)
|
||||
}
|
||||
|
||||
/*
|
||||
#[tauri::command]
|
||||
pub fn get_current_write_speed(state: tauri::State<'_, Mutex<AppState>>) {}
|
||||
|
||||
@ -33,7 +33,10 @@ pub enum DownloadManagerSignal {
|
||||
/// download, sync everything to disk, and
|
||||
/// then exit
|
||||
Finish,
|
||||
/// Stops (but doesn't remove) current download
|
||||
Cancel,
|
||||
/// Removes a given game
|
||||
Remove(String),
|
||||
/// Any error which occurs in the agent
|
||||
Error(GameDownloadError),
|
||||
/// Pushes UI update
|
||||
@ -142,6 +145,11 @@ impl DownloadManager {
|
||||
.send(DownloadManagerSignal::Update)
|
||||
.unwrap();
|
||||
}
|
||||
pub fn cancel(&self, game_id: String) {
|
||||
self.command_sender
|
||||
.send(DownloadManagerSignal::Remove(game_id))
|
||||
.unwrap();
|
||||
}
|
||||
pub fn rearrange(&self, current_index: usize, new_index: usize) {
|
||||
if current_index == new_index {
|
||||
return;
|
||||
@ -159,8 +167,8 @@ impl DownloadManager {
|
||||
let mut queue = self.edit();
|
||||
let to_move = queue.remove(current_index).unwrap();
|
||||
queue.insert(new_index, to_move);
|
||||
|
||||
info!("new queue: {:?}", queue);
|
||||
drop(queue);
|
||||
|
||||
if needs_pause {
|
||||
self.command_sender.send(DownloadManagerSignal::Go).unwrap();
|
||||
|
||||
@ -2,7 +2,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc, Mutex,
|
||||
Arc, Mutex, RwLockWriteGuard,
|
||||
},
|
||||
thread::{spawn, JoinHandle},
|
||||
};
|
||||
@ -11,8 +11,9 @@ use log::{error, info};
|
||||
use tauri::{AppHandle, Emitter};
|
||||
|
||||
use crate::{
|
||||
db::DatabaseGameStatus,
|
||||
db::{Database, GameStatus, GameTransientStatus},
|
||||
library::{on_game_complete, GameUpdateEvent, QueueUpdateEvent, QueueUpdateEventQueueData},
|
||||
state::GameStatusManager,
|
||||
DB,
|
||||
};
|
||||
|
||||
@ -107,14 +108,18 @@ impl DownloadManagerBuilder {
|
||||
DownloadManager::new(terminator, queue, active_progress, command_sender)
|
||||
}
|
||||
|
||||
fn set_game_status(&self, id: String, status: DatabaseGameStatus) {
|
||||
fn set_game_status<F: FnOnce(&mut RwLockWriteGuard<'_, Database>, &String) -> ()>(
|
||||
&self,
|
||||
id: String,
|
||||
setter: F,
|
||||
) {
|
||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.insert(id.clone(), status.clone());
|
||||
setter(&mut db_handle, &id);
|
||||
drop(db_handle);
|
||||
DB.save().unwrap();
|
||||
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
self.app_handle
|
||||
.emit(
|
||||
&format!("update_game/{}", id),
|
||||
@ -208,10 +213,35 @@ impl DownloadManagerBuilder {
|
||||
self.stop_and_wait_current_download();
|
||||
return Ok(());
|
||||
}
|
||||
DownloadManagerSignal::Remove(game_id) => {
|
||||
self.manage_remove_game(game_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn manage_remove_game(&mut self, game_id: String) {
|
||||
if let Some(current_download) = &self.current_download_agent {
|
||||
if current_download.id == game_id {
|
||||
self.manage_cancel_signal();
|
||||
}
|
||||
}
|
||||
|
||||
let index = self.download_queue.get_by_id(game_id.clone()).unwrap();
|
||||
let mut queue_handle = self.download_queue.edit();
|
||||
queue_handle.remove(index);
|
||||
self.set_game_status(game_id, |db_handle, id| {
|
||||
db_handle.games.transient_statuses.remove(id);
|
||||
});
|
||||
drop(queue_handle);
|
||||
|
||||
if self.current_download_agent.is_none() {
|
||||
self.manage_go_signal();
|
||||
}
|
||||
|
||||
self.push_manager_update();
|
||||
}
|
||||
|
||||
fn manage_stop_signal(&mut self) {
|
||||
info!("Got signal 'Stop'");
|
||||
self.set_status(DownloadManagerStatus::Paused);
|
||||
@ -273,7 +303,12 @@ impl DownloadManagerBuilder {
|
||||
.insert(interface_data.id.clone(), download_agent);
|
||||
self.download_queue.append(interface_data);
|
||||
|
||||
self.set_game_status(id, DatabaseGameStatus::Downloading { version_name });
|
||||
self.set_game_status(id, |db, id| {
|
||||
db.games.transient_statuses.insert(
|
||||
id.to_string(),
|
||||
GameTransientStatus::Downloading { version_name },
|
||||
);
|
||||
});
|
||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||
}
|
||||
|
||||
@ -344,10 +379,12 @@ impl DownloadManagerBuilder {
|
||||
// Set flags for download manager
|
||||
active_control_flag.set(DownloadThreadControlFlag::Go);
|
||||
self.set_status(DownloadManagerStatus::Downloading);
|
||||
self.set_game_status(
|
||||
self.current_download_agent.as_ref().unwrap().id.clone(),
|
||||
DatabaseGameStatus::Downloading { version_name },
|
||||
);
|
||||
self.set_game_status(agent_data.id.clone(), |db, id| {
|
||||
db.games.transient_statuses.insert(
|
||||
id.to_string(),
|
||||
GameTransientStatus::Downloading { version_name },
|
||||
);
|
||||
});
|
||||
|
||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||
}
|
||||
@ -361,7 +398,9 @@ impl DownloadManagerBuilder {
|
||||
self.set_status(DownloadManagerStatus::Error(error));
|
||||
|
||||
let game_id = current_status.id.clone();
|
||||
self.set_game_status(game_id, DatabaseGameStatus::Remote {});
|
||||
self.set_game_status(game_id, |db_handle, id| {
|
||||
db_handle.games.transient_statuses.remove(id);
|
||||
});
|
||||
|
||||
self.sender.send(DownloadManagerSignal::Update).unwrap();
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ mod library;
|
||||
|
||||
mod process;
|
||||
mod remote;
|
||||
mod settings;
|
||||
mod state;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@ -178,6 +178,7 @@ pub fn run() {
|
||||
move_game_in_queue,
|
||||
pause_game_downloads,
|
||||
resume_game_downloads,
|
||||
cancel_game,
|
||||
// Processes
|
||||
launch_game,
|
||||
])
|
||||
|
||||
@ -5,18 +5,19 @@ use tauri::Emitter;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use urlencoding::encode;
|
||||
|
||||
use crate::db::DatabaseGameStatus;
|
||||
use crate::db::DatabaseImpls;
|
||||
use crate::db::GameVersion;
|
||||
use crate::db::{GameStatus, GameTransientStatus};
|
||||
use crate::downloads::download_manager::GameDownloadStatus;
|
||||
use crate::process::process_manager::Platform;
|
||||
use crate::remote::RemoteAccessError;
|
||||
use crate::state::{GameStatusManager, GameStatusWithTransient};
|
||||
use crate::{auth::generate_authorization_header, AppState, DB};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct FetchGameStruct {
|
||||
game: Game,
|
||||
status: DatabaseGameStatus,
|
||||
status: GameStatusWithTransient,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -36,7 +37,7 @@ pub struct Game {
|
||||
#[derive(serde::Serialize, Clone)]
|
||||
pub struct GameUpdateEvent {
|
||||
pub game_id: String,
|
||||
pub status: DatabaseGameStatus,
|
||||
pub status: (Option<GameStatus>, Option<GameTransientStatus>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
@ -61,6 +62,7 @@ pub struct GameVersionOption {
|
||||
setup_command: String,
|
||||
launch_command: String,
|
||||
delta: bool,
|
||||
umu_id_override: Option<String>,
|
||||
// total_size: usize,
|
||||
}
|
||||
|
||||
@ -89,11 +91,11 @@ fn fetch_library_logic(app: AppHandle) -> Result<Vec<Game>, RemoteAccessError> {
|
||||
|
||||
for game in games.iter() {
|
||||
handle.games.insert(game.id.clone(), game.clone());
|
||||
if !db_handle.games.games_statuses.contains_key(&game.id) {
|
||||
if !db_handle.games.statuses.contains_key(&game.id) {
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.insert(game.id.clone(), DatabaseGameStatus::Remote {});
|
||||
.statuses
|
||||
.insert(game.id.clone(), GameStatus::Remote {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,16 +118,11 @@ fn fetch_game_logic(
|
||||
|
||||
let game = state_handle.games.get(&id);
|
||||
if let Some(game) = game {
|
||||
let db_handle = DB.borrow_data().unwrap();
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
let data = FetchGameStruct {
|
||||
game: game.clone(),
|
||||
status: db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.get(&game.id)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
status,
|
||||
};
|
||||
|
||||
return Ok(data);
|
||||
@ -158,28 +155,23 @@ fn fetch_game_logic(
|
||||
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.entry(id)
|
||||
.or_insert(DatabaseGameStatus::Remote {});
|
||||
.statuses
|
||||
.entry(id.clone())
|
||||
.or_insert(GameStatus::Remote {});
|
||||
drop(db_handle);
|
||||
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
let data = FetchGameStruct {
|
||||
game: game.clone(),
|
||||
status: db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.get(&game.id)
|
||||
.unwrap()
|
||||
.clone(),
|
||||
status,
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fetch_game(
|
||||
id: String,
|
||||
app: tauri::AppHandle,
|
||||
) -> Result<FetchGameStruct, String> {
|
||||
pub fn fetch_game(id: String, app: tauri::AppHandle) -> Result<FetchGameStruct, String> {
|
||||
let result = fetch_game_logic(id, app);
|
||||
|
||||
if result.is_err() {
|
||||
@ -190,15 +182,8 @@ pub fn fetch_game(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fetch_game_status(id: String) -> Result<DatabaseGameStatus, String> {
|
||||
let db_handle = DB.borrow_data().unwrap();
|
||||
let status = db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.get(&id)
|
||||
.unwrap_or(&DatabaseGameStatus::Remote {})
|
||||
.clone();
|
||||
drop(db_handle);
|
||||
pub fn fetch_game_status(id: String) -> Result<GameStatusWithTransient, String> {
|
||||
let status = GameStatusManager::fetch_state(&id);
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
@ -277,7 +262,7 @@ pub fn on_game_complete(
|
||||
let mut handle = DB.borrow_data_mut().unwrap();
|
||||
handle
|
||||
.games
|
||||
.game_versions
|
||||
.versions
|
||||
.entry(game_id.clone())
|
||||
.or_default()
|
||||
.insert(version_name.clone(), data.clone());
|
||||
@ -285,12 +270,12 @@ pub fn on_game_complete(
|
||||
DB.save().unwrap();
|
||||
|
||||
let status = if data.setup_command.is_empty() {
|
||||
DatabaseGameStatus::Installed {
|
||||
GameStatus::Installed {
|
||||
version_name,
|
||||
install_dir,
|
||||
}
|
||||
} else {
|
||||
DatabaseGameStatus::SetupRequired {
|
||||
GameStatus::SetupRequired {
|
||||
version_name,
|
||||
install_dir,
|
||||
}
|
||||
@ -299,14 +284,17 @@ pub fn on_game_complete(
|
||||
let mut db_handle = DB.borrow_data_mut().unwrap();
|
||||
db_handle
|
||||
.games
|
||||
.games_statuses
|
||||
.statuses
|
||||
.insert(game_id.clone(), status.clone());
|
||||
drop(db_handle);
|
||||
DB.save().unwrap();
|
||||
app_handle
|
||||
.emit(
|
||||
&format!("update_game/{}", game_id),
|
||||
GameUpdateEvent { game_id, status },
|
||||
GameUpdateEvent {
|
||||
game_id,
|
||||
status: (Some(status), None),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
db::{DatabaseGameStatus, DATA_ROOT_DIR},
|
||||
db::{GameStatus, DATA_ROOT_DIR},
|
||||
DB,
|
||||
};
|
||||
|
||||
@ -74,11 +74,11 @@ impl ProcessManager {
|
||||
let db_lock = DB.borrow_data().unwrap();
|
||||
let game_status = db_lock
|
||||
.games
|
||||
.games_statuses
|
||||
.statuses
|
||||
.get(&game_id)
|
||||
.ok_or("Game not installed")?;
|
||||
|
||||
let DatabaseGameStatus::Installed {
|
||||
let GameStatus::Installed {
|
||||
version_name,
|
||||
install_dir,
|
||||
} = game_status
|
||||
@ -88,7 +88,7 @@ impl ProcessManager {
|
||||
|
||||
let game_version = db_lock
|
||||
.games
|
||||
.game_versions
|
||||
.versions
|
||||
.get(&game_id)
|
||||
.ok_or("Invalid game ID".to_owned())?
|
||||
.get(version_name)
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
31
src-tauri/src/state.rs
Normal file
31
src-tauri/src/state.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
db::{GameStatus, GameTransientStatus},
|
||||
DB,
|
||||
};
|
||||
|
||||
pub type GameStatusWithTransient = (
|
||||
Option<GameStatus>,
|
||||
Option<GameTransientStatus>,
|
||||
);
|
||||
pub struct GameStatusManager {}
|
||||
|
||||
impl GameStatusManager {
|
||||
pub fn fetch_state(game_id: &String) -> GameStatusWithTransient {
|
||||
let db_lock = DB.borrow_data().unwrap();
|
||||
let offline_state = db_lock.games.statuses.get(game_id).cloned();
|
||||
let online_state = db_lock.games.transient_statuses.get(game_id).cloned();
|
||||
drop(db_lock);
|
||||
|
||||
if online_state.is_some() {
|
||||
return (None, online_state);
|
||||
}
|
||||
|
||||
if offline_state.is_some() {
|
||||
return (offline_state, None);
|
||||
}
|
||||
|
||||
return (None, None);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user