mirror of
https://github.com/Drop-OSS/drop-app.git
synced 2025-11-13 16:22:43 +10:00
feat(install ui): ui to install games
This commit is contained in:
@ -1,29 +1,45 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
@click="() => buttonActions[props.status]()"
|
||||
:class="[
|
||||
styles[props.status],
|
||||
'inline-flex uppercase font-display items-center gap-x-2 rounded-md px-4 py-3 text-md font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
|
||||
]"
|
||||
>
|
||||
<component :is="buttonIcons[props.status]" class="-mr-0.5 size-5" aria-hidden="true" />
|
||||
<component
|
||||
:is="buttonIcons[props.status]"
|
||||
class="-mr-0.5 size-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ buttonNames[props.status] }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ArrowDownTrayIcon, PlayIcon, QueueListIcon, TrashIcon } from "@heroicons/vue/20/solid";
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
PlayIcon,
|
||||
QueueListIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/vue/20/solid";
|
||||
import type { Component } from "vue";
|
||||
import { GameStatus } from "~/types.js";
|
||||
|
||||
const props = defineProps<{ status: GameStatus }>();
|
||||
const emit = defineEmits<{
|
||||
(e: "install"): void;
|
||||
(e: "cancel"): void;
|
||||
(e: "play"): void;
|
||||
}>();
|
||||
|
||||
const styles: { [key in GameStatus]: string } = {
|
||||
[GameStatus.Remote]:
|
||||
"bg-blue-600 text-white hover:bg-blue-500 focus-visible:outline-blue-600",
|
||||
[GameStatus.Queued]:
|
||||
"bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
||||
[GameStatus.Downloading]: "bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
||||
[GameStatus.Downloading]:
|
||||
"bg-zinc-800 text-white hover:bg-zinc-700 focus-visible:outline-zinc-700",
|
||||
[GameStatus.Installed]:
|
||||
"bg-green-600 text-white hover:bg-green-500 focus-visible:outline-green-600",
|
||||
[GameStatus.Updating]: "",
|
||||
@ -45,6 +61,15 @@ const buttonIcons: {[key in GameStatus]: Component} = {
|
||||
[GameStatus.Downloading]: ArrowDownTrayIcon,
|
||||
[GameStatus.Installed]: PlayIcon,
|
||||
[GameStatus.Updating]: ArrowDownTrayIcon,
|
||||
[GameStatus.Uninstalling]: TrashIcon
|
||||
}
|
||||
[GameStatus.Uninstalling]: TrashIcon,
|
||||
};
|
||||
|
||||
const buttonActions: { [key in GameStatus]: () => void } = {
|
||||
[GameStatus.Remote]: () => emit("install"),
|
||||
[GameStatus.Queued]: () => emit("cancel"),
|
||||
[GameStatus.Downloading]: () => emit("cancel"),
|
||||
[GameStatus.Installed]: () => emit("play"),
|
||||
[GameStatus.Updating]: () => emit("cancel"),
|
||||
[GameStatus.Uninstalling]: () => {},
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex h-9 items-center justify-center rounded-md bg-blue-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||
class="inline-flex h-9 items-center justify-center rounded-md bg-blue-600 hover:bg-blue-500 disabled:text-zinc-500 disabled:bg-blue-900 disabled:hover:bg-blue-900 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
||||
>
|
||||
<div v-if="props.loading" role="status">
|
||||
<svg
|
||||
|
||||
@ -18,13 +18,303 @@
|
||||
<div class="w-full min-h-screen mx-auto bg-zinc-900 px-5 py-6">
|
||||
<!-- game toolbar -->
|
||||
<div>
|
||||
<GameStatusButton :status="status" />
|
||||
<GameStatusButton @install="() => installFlow()" :status="status" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TransitionRoot as="template" :show="installFlowOpen">
|
||||
<Dialog class="relative z-50" @close="installFlowOpen = false">
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-out duration-300"
|
||||
enter-from="opacity-0"
|
||||
enter-to="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leave-from="opacity-100"
|
||||
leave-to="opacity-0"
|
||||
>
|
||||
<div
|
||||
class="fixed inset-0 bg-zinc-950 bg-opacity-75 transition-opacity"
|
||||
/>
|
||||
</TransitionChild>
|
||||
|
||||
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||
<div
|
||||
class="flex min-h-full items-start justify-center p-4 text-center sm:items-center sm:p-0"
|
||||
>
|
||||
<TransitionChild
|
||||
as="template"
|
||||
enter="ease-out duration-300"
|
||||
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enter-to="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leave-from="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<form
|
||||
@submit.prevent="() => install()"
|
||||
class="relative transform rounded-lg bg-zinc-900 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg"
|
||||
>
|
||||
<div class="px-4 pb-4 pt-5 space-y-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div class="mt-3 text-center sm:mt-0 sm:text-left">
|
||||
<DialogTitle
|
||||
as="h3"
|
||||
class="text-base font-semibold text-zinc-100"
|
||||
>Install {{ game.mName }}?
|
||||
</DialogTitle>
|
||||
<div class="mt-2">
|
||||
<p class="text-sm text-zinc-400">
|
||||
Drop will add {{ game.mName }} to the queue to be
|
||||
downloaded. While downloading, Drop may use up a large
|
||||
amount of resources, particularly network bandwidth and
|
||||
CPU utilisation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div v-if="versionOptions && versionOptions.length > 0">
|
||||
<Listbox as="div" v-model="installVersionIndex">
|
||||
<ListboxLabel
|
||||
class="block text-sm/6 font-medium text-zinc-100"
|
||||
>Version</ListboxLabel
|
||||
>
|
||||
<div class="relative mt-2">
|
||||
<ListboxButton
|
||||
class="relative w-full cursor-default rounded-md bg-zinc-800 py-1.5 pl-3 pr-10 text-left text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-700 focus:outline-none focus:ring-2 focus:ring-blue-600 sm:text-sm/6"
|
||||
>
|
||||
<span class="block truncate"
|
||||
>{{
|
||||
versionOptions[installVersionIndex].versionName
|
||||
}}
|
||||
on
|
||||
{{
|
||||
versionOptions[installVersionIndex].platform
|
||||
}}</span
|
||||
>
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||
>
|
||||
<ChevronUpDownIcon
|
||||
class="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ListboxOption
|
||||
as="template"
|
||||
v-for="(version, versionIdx) in versionOptions"
|
||||
:key="version.versionName"
|
||||
:value="versionIdx"
|
||||
v-slot="{ active, selected }"
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
active
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'text-zinc-300',
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
selected
|
||||
? 'font-semibold text-zinc-100'
|
||||
: 'font-normal',
|
||||
'block truncate',
|
||||
]"
|
||||
>{{ version.versionName }} on
|
||||
{{ version.platform }}</span
|
||||
>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[
|
||||
active ? 'text-white' : 'text-blue-600',
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
]"
|
||||
>
|
||||
<CheckIcon
|
||||
class="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
<div v-else class="mt-1 rounded-md bg-red-600/10 p-4">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<XCircleIcon
|
||||
class="h-5 w-5 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-red-600">
|
||||
There are no versions to install. Please contact your
|
||||
server admin or try again later.
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="installDirs">
|
||||
<Listbox as="div" v-model="installDir">
|
||||
<ListboxLabel
|
||||
class="block text-sm/6 font-medium text-zinc-100"
|
||||
>Install to</ListboxLabel
|
||||
>
|
||||
<div class="relative mt-2">
|
||||
<ListboxButton
|
||||
class="relative w-full cursor-default rounded-md bg-zinc-800 py-1.5 pl-3 pr-10 text-left text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-700 focus:outline-none focus:ring-2 focus:ring-blue-600 sm:text-sm/6"
|
||||
>
|
||||
<span class="block truncate">{{
|
||||
installDirs[installDir]
|
||||
}}</span>
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"
|
||||
>
|
||||
<ChevronUpDownIcon
|
||||
class="h-5 w-5 text-gray-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ListboxOption
|
||||
as="template"
|
||||
v-for="(dir, dirIdx) in installDirs"
|
||||
:key="dir"
|
||||
:value="dirIdx"
|
||||
v-slot="{ active, selected }"
|
||||
>
|
||||
<li
|
||||
:class="[
|
||||
active
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'text-zinc-300',
|
||||
'relative cursor-default select-none py-2 pl-3 pr-9',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
:class="[
|
||||
selected
|
||||
? 'font-semibold text-zinc-100'
|
||||
: 'font-normal',
|
||||
'block truncate',
|
||||
]"
|
||||
>{{ dir }}}</span
|
||||
>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[
|
||||
active ? 'text-white' : 'text-blue-600',
|
||||
'absolute inset-y-0 right-0 flex items-center pr-4',
|
||||
]"
|
||||
>
|
||||
<CheckIcon
|
||||
class="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="installError"
|
||||
class="mt-1 rounded-md bg-red-600/10 p-4"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<XCircleIcon
|
||||
class="h-5 w-5 text-red-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-red-600">
|
||||
{{ installError }}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-b-lg bg-zinc-800 px-4 py-3 sm:flex sm:gap-x-2 sm:flex-row-reverse sm:px-6"
|
||||
>
|
||||
<LoadingButton
|
||||
:disabled="
|
||||
!(versionOptions && versionOptions.length > 0 && !installDir)
|
||||
"
|
||||
:loading="installLoading"
|
||||
type="submit"
|
||||
class="w-full sm:w-fit"
|
||||
>
|
||||
Install
|
||||
</LoadingButton>
|
||||
<button
|
||||
type="button"
|
||||
class="mt-3 inline-flex w-full justify-center rounded-md bg-zinc-800 px-3 py-2 text-sm font-semibold text-zinc-100 shadow-sm ring-1 ring-inset ring-zinc-700 hover:bg-zinc-900 sm:mt-0 sm:w-auto"
|
||||
@click="installFlowOpen = false"
|
||||
ref="cancelButtonRef"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</TransitionChild>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</TransitionRoot>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
TransitionChild,
|
||||
TransitionRoot,
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxLabel,
|
||||
ListboxOption,
|
||||
ListboxOptions,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import { XCircleIcon } from "@heroicons/vue/24/solid";
|
||||
|
||||
import type { Game } from "@prisma/client";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
@ -45,4 +335,44 @@ listen(`update_game/${game.value.id}`, (event) => {
|
||||
});
|
||||
|
||||
const bannerUrl = await useObject(game.value.mBannerId);
|
||||
|
||||
const installFlowOpen = ref(false);
|
||||
const versionOptions = ref<
|
||||
undefined | Array<{ versionName: string; platform: string }>
|
||||
>();
|
||||
const installDirs = ref<undefined | Array<string>>();
|
||||
async function installFlow() {
|
||||
installFlowOpen.value = true;
|
||||
|
||||
try {
|
||||
versionOptions.value = await invoke("fetch_game_verion_options", {
|
||||
gameId: game.value.id,
|
||||
});
|
||||
installDirs.value = await invoke("fetch_download_dir_stats");
|
||||
} catch (error) {
|
||||
installError.value = (error as string).toString();
|
||||
}
|
||||
}
|
||||
|
||||
const installLoading = ref(false);
|
||||
const installError = ref<string | undefined>();
|
||||
const installVersionIndex = ref(0);
|
||||
const installDir = ref(0);
|
||||
async function install() {
|
||||
try {
|
||||
if (!versionOptions.value)
|
||||
throw new Error("Versions have not been loaded.");
|
||||
installLoading.value = true;
|
||||
await invoke("download_game", {
|
||||
gameId: game.value.id,
|
||||
gameVersion: versionOptions.value[installVersionIndex.value].versionName,
|
||||
installDir: installDir.value,
|
||||
});
|
||||
installLoading.value = false;
|
||||
|
||||
installFlowOpen.value = false;
|
||||
} catch (error) {
|
||||
installError.value = (error as string).toString();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -39,11 +39,6 @@ const versionName = ref("");
|
||||
const progress = ref(0);
|
||||
|
||||
async function startGameDownload() {
|
||||
await invoke("download_game", {
|
||||
gameId: gameId.value,
|
||||
gameVersion: versionName.value,
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
(async () => {
|
||||
const currentProgress = await invoke<number>(
|
||||
|
||||
@ -172,7 +172,6 @@ impl GameDownloadAgent {
|
||||
|
||||
pub fn ensure_contexts(&self) -> Result<(), GameDownloadError> {
|
||||
let context_lock = self.contexts.lock().unwrap();
|
||||
info!("{:?} {}", context_lock, context_lock.is_empty());
|
||||
if !context_lock.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@ -209,7 +208,7 @@ impl GameDownloadAgent {
|
||||
for (i, length) in chunk.lengths.iter().enumerate() {
|
||||
contexts.push(DropDownloadContext {
|
||||
file_name: raw_path.to_string(),
|
||||
version: version.to_string(),
|
||||
version: chunk.versionName.to_string(),
|
||||
offset: running_offset,
|
||||
index: i,
|
||||
game_id: game_id.to_string(),
|
||||
|
||||
@ -8,13 +8,14 @@ use crate::AppState;
|
||||
pub fn download_game(
|
||||
game_id: String,
|
||||
game_version: String,
|
||||
install_dir: usize,
|
||||
state: tauri::State<'_, Mutex<AppState>>,
|
||||
) -> Result<(), String> {
|
||||
state
|
||||
.lock()
|
||||
.unwrap()
|
||||
.download_manager
|
||||
.queue_game(game_id, game_version, 0)
|
||||
.queue_game(game_id, game_version, install_dir)
|
||||
.map_err(|_| "An error occurred while communicating with the download manager.".to_string())
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ use crate::db::DatabaseImpls;
|
||||
use crate::downloads::manifest::DropDownloadContext;
|
||||
use crate::remote::RemoteAccessError;
|
||||
use crate::DB;
|
||||
use log::info;
|
||||
use log::{info, warn};
|
||||
use md5::{Context, Digest};
|
||||
use reqwest::blocking::Response;
|
||||
|
||||
@ -150,6 +150,13 @@ pub fn download_game_chunk(
|
||||
.send()
|
||||
.map_err(|e| GameDownloadError::Communication(e.into()))?;
|
||||
|
||||
if response.status() != 200 {
|
||||
warn!("{}", response.text().unwrap());
|
||||
return Err(GameDownloadError::Communication(
|
||||
RemoteAccessError::InvalidCodeError(400),
|
||||
));
|
||||
}
|
||||
|
||||
let mut destination = DropWriter::new(ctx.path);
|
||||
|
||||
if ctx.offset != 0 {
|
||||
@ -160,7 +167,9 @@ pub fn download_game_chunk(
|
||||
|
||||
let content_length = response.content_length();
|
||||
if content_length.is_none() {
|
||||
return Err(GameDownloadError::Communication(RemoteAccessError::InvalidResponse));
|
||||
return Err(GameDownloadError::Communication(
|
||||
RemoteAccessError::InvalidResponse,
|
||||
));
|
||||
}
|
||||
|
||||
let mut pipeline = DropDownloadPipeline::new(
|
||||
@ -176,7 +185,9 @@ pub fn download_game_chunk(
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
let checksum = pipeline.finish().map_err(|e| GameDownloadError::IoError(e))?;
|
||||
let checksum = pipeline
|
||||
.finish()
|
||||
.map_err(|e| GameDownloadError::IoError(e))?;
|
||||
|
||||
let res = hex::encode(checksum.0);
|
||||
if res != ctx.checksum {
|
||||
|
||||
@ -8,6 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use log::{error, info, warn};
|
||||
use rustbreak::Database;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
|
||||
use crate::{db::DatabaseGameStatus, library::GameUpdateEvent, DB};
|
||||
@ -247,6 +248,11 @@ impl DownloadManagerBuilder {
|
||||
let mut lock = current_status.status.lock().unwrap();
|
||||
*lock = GameDownloadStatus::Error;
|
||||
self.set_status(DownloadManagerStatus::Error(error));
|
||||
|
||||
self.set_game_status(
|
||||
self.current_game_interface.as_ref().unwrap().id.clone(),
|
||||
DatabaseGameStatus::Remote,
|
||||
);
|
||||
}
|
||||
fn manage_cancel_signal(&mut self, game_id: String) {
|
||||
if let Some(current_flag) = &self.active_control_flag {
|
||||
|
||||
@ -9,6 +9,7 @@ pub struct DropChunk {
|
||||
pub ids: Vec<String>,
|
||||
pub checksums: Vec<String>,
|
||||
pub lengths: Vec<usize>,
|
||||
pub versionName: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
|
||||
@ -19,7 +19,7 @@ use downloads::download_manager::DownloadManager;
|
||||
use downloads::download_manager_builder::DownloadManagerBuilder;
|
||||
use env_logger::Env;
|
||||
use http::{header::*, response::Builder as ResponseBuilder};
|
||||
use library::{fetch_game, fetch_game_status, fetch_library, Game};
|
||||
use library::{fetch_game, fetch_game_status, fetch_game_verion_options, fetch_library, Game};
|
||||
use log::{debug, info};
|
||||
use remote::{gen_drop_url, use_remote, RemoteAccessError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -131,6 +131,7 @@ pub fn run() {
|
||||
delete_download_dir,
|
||||
fetch_download_dir_stats,
|
||||
fetch_game_status,
|
||||
fetch_game_verion_options,
|
||||
// Downloads
|
||||
download_game,
|
||||
get_current_game_download_progress,
|
||||
|
||||
@ -39,6 +39,19 @@ pub struct GameUpdateEvent {
|
||||
pub status: DatabaseGameStatus,
|
||||
}
|
||||
|
||||
// Game version with some fields missing and size information
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GameVersionOption {
|
||||
version_index: usize,
|
||||
version_name: String,
|
||||
platform: String,
|
||||
setup_command: String,
|
||||
launch_command: String,
|
||||
delta: bool,
|
||||
// total_size: usize,
|
||||
}
|
||||
|
||||
fn fetch_library_logic(app: AppHandle) -> Result<String, RemoteAccessError> {
|
||||
let base_url = DB.fetch_base_url();
|
||||
let library_url = base_url.join("/api/v1/client/user/library")?;
|
||||
@ -172,3 +185,32 @@ pub fn fetch_game_status(id: String) -> Result<DatabaseGameStatus, String> {
|
||||
|
||||
return Ok(status);
|
||||
}
|
||||
|
||||
fn fetch_game_verion_options_logic(game_id: String) -> Result<Vec<GameVersionOption>, RemoteAccessError> {
|
||||
let base_url = DB.fetch_base_url();
|
||||
|
||||
let endpoint =
|
||||
base_url.join(format!("/api/v1/client/metadata/versions?id={}", game_id).as_str())?;
|
||||
let header = generate_authorization_header();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let response = client
|
||||
.get(endpoint.to_string())
|
||||
.header("Authorization", header)
|
||||
.send()?;
|
||||
|
||||
if response.status() != 200 {
|
||||
return Err(RemoteAccessError::InvalidCodeError(
|
||||
response.status().into(),
|
||||
));
|
||||
}
|
||||
|
||||
let data = response.json::<Vec<GameVersionOption>>()?;
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn fetch_game_verion_options(game_id: String) -> Result<Vec<GameVersionOption>, String> {
|
||||
fetch_game_verion_options_logic(game_id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user