mirror of
https://github.com/open-goal/launcher.git
synced 2024-10-20 04:57:38 -04:00
backend: majority of version management implemented
This commit is contained in:
parent
a027e59e75
commit
fee811df72
|
@ -1,18 +1,19 @@
|
|||
use futures_util::StreamExt;
|
||||
use std::{collections::HashMap, error::Error, path::Path};
|
||||
use std::{io::Cursor, path::Path};
|
||||
use tokio::{fs::File, io::AsyncWriteExt};
|
||||
|
||||
use crate::config::LauncherConfig;
|
||||
use crate::{config::LauncherConfig, util};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_downloaded_official_versions(
|
||||
pub async fn list_downloaded_versions(
|
||||
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
|
||||
version_folder: String,
|
||||
) -> Result<Vec<String>, ()> {
|
||||
let config_lock = config.lock().await;
|
||||
match &config_lock.installation_dir {
|
||||
None => Ok(Vec::new()),
|
||||
Some(path) => {
|
||||
let expected_path = Path::new(&path).join("/versions/official");
|
||||
let expected_path = Path::new(path).join("versions").join(version_folder);
|
||||
if !expected_path.is_dir() {
|
||||
Ok(Vec::new())
|
||||
} else {
|
||||
|
@ -24,7 +25,11 @@ pub async fn list_downloaded_official_versions(
|
|||
e.ok().and_then(|d| {
|
||||
let p = d.path();
|
||||
if p.is_dir() {
|
||||
Some(p.to_string_lossy().into_owned())
|
||||
Some(
|
||||
p.file_name()
|
||||
.map(|name| name.to_string_lossy().into_owned())
|
||||
.unwrap_or("".into()),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -48,15 +53,12 @@ pub async fn download_official_version(
|
|||
match &config_lock.installation_dir {
|
||||
None => Ok(()),
|
||||
Some(path) => {
|
||||
println!("BLAH - {}", path);
|
||||
// TODO - make the dir
|
||||
let expected_path = Path::new(&path).join("versions/official/test");
|
||||
println!("{}", expected_path.display());
|
||||
// TODO - severe lack of safety here!
|
||||
// TODO - make the dir and the file name
|
||||
let expected_path = Path::new(&path).join("versions/official/test.zip");
|
||||
let client = reqwest::Client::new();
|
||||
println!("{}", url);
|
||||
let mut req = client.get(url);
|
||||
let res = req.send().await.expect("");
|
||||
println!("{:?}", res);
|
||||
let total = res.content_length().expect("");
|
||||
|
||||
let mut file = File::create(expected_path).await.expect("");
|
||||
|
@ -66,7 +68,62 @@ pub async fn download_official_version(
|
|||
let chunk = chunk.expect("");
|
||||
file.write_all(&chunk).await.expect("");
|
||||
}
|
||||
|
||||
let target_dir = Path::new(&path).join("versions/official/").join(version);
|
||||
|
||||
let zip_path = Path::new(&path).join("versions/official/test.zip");
|
||||
|
||||
let archive: Vec<u8> = std::fs::read(&zip_path.clone()).unwrap();
|
||||
zip_extract::extract(Cursor::new(archive), &target_dir, true).expect("");
|
||||
|
||||
std::fs::remove_file(zip_path).expect("TODO");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn go_to_version_folder(
|
||||
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
|
||||
version_folder: String,
|
||||
) -> Result<(), ()> {
|
||||
let config_lock = config.lock().await;
|
||||
match &config_lock.installation_dir {
|
||||
None => Err(()),
|
||||
Some(path) => {
|
||||
let expected_path = Path::new(path).join("versions").join(version_folder);
|
||||
util::open_dir_in_os(expected_path.to_string_lossy().into_owned());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_active_version_change(
|
||||
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
|
||||
version_folder: String,
|
||||
new_active_version: String,
|
||||
) -> Result<(), ()> {
|
||||
let mut config_lock = config.lock().await;
|
||||
// TODO - error checking
|
||||
config_lock.set_active_version_folder(version_folder);
|
||||
config_lock.set_active_version(new_active_version);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_active_version(
|
||||
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
|
||||
) -> Result<Option<String>, ()> {
|
||||
let config_lock = config.lock().await;
|
||||
Ok(config_lock.active_version.clone())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_active_version_folder(
|
||||
config: tauri::State<'_, tokio::sync::Mutex<LauncherConfig>>,
|
||||
) -> Result<Option<String>, ()> {
|
||||
let config_lock = config.lock().await;
|
||||
Ok(config_lock.active_version_folder.clone())
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ impl SupportedGame {
|
|||
pub struct GameConfig {
|
||||
is_installed: bool,
|
||||
version: Option<String>,
|
||||
version_folder: Option<String>,
|
||||
}
|
||||
|
||||
impl GameConfig {
|
||||
|
@ -45,6 +46,7 @@ impl GameConfig {
|
|||
Self {
|
||||
is_installed: false,
|
||||
version: None,
|
||||
version_folder: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +103,8 @@ pub struct LauncherConfig {
|
|||
pub games: SupportedGames,
|
||||
pub last_active_game: Option<SupportedGame>,
|
||||
pub installation_dir: Option<String>,
|
||||
pub active_version: Option<String>,
|
||||
pub active_version_folder: Option<String>,
|
||||
}
|
||||
|
||||
// TODO - what is _loaded?
|
||||
|
@ -118,6 +122,8 @@ impl LauncherConfig {
|
|||
games: SupportedGames::default(),
|
||||
last_active_game: None,
|
||||
installation_dir: None,
|
||||
active_version: None,
|
||||
active_version_folder: Some("official".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,4 +187,14 @@ impl LauncherConfig {
|
|||
self.installation_dir = Some(new_dir);
|
||||
self.save_config();
|
||||
}
|
||||
|
||||
pub fn set_active_version(&mut self, new_version: String) {
|
||||
self.active_version = Some(new_version);
|
||||
self.save_config();
|
||||
}
|
||||
|
||||
pub fn set_active_version_folder(&mut self, new_version_folder: String) {
|
||||
self.active_version_folder = Some(new_version_folder);
|
||||
self.save_config();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::env;
|
|||
mod commands;
|
||||
mod config;
|
||||
mod textures;
|
||||
mod util;
|
||||
use commands::{
|
||||
close_splashscreen, copy_dir, get_highest_simd, get_install_directory, open_dir, open_repl,
|
||||
set_install_directory,
|
||||
|
@ -19,6 +20,8 @@ use textures::{extract_textures, get_all_texture_packs};
|
|||
pub type FFIResult<T> = Result<T, String>;
|
||||
|
||||
fn main() {
|
||||
// TODO - switch to https://github.com/daboross/fern so we can setup easy logging
|
||||
// to a file as well
|
||||
if env::var_os("RUST_LOG").is_none() {
|
||||
env::set_var("RUST_LOG", "debug");
|
||||
}
|
||||
|
@ -44,8 +47,12 @@ fn main() {
|
|||
get_install_directory,
|
||||
set_install_directory,
|
||||
// Version Management,
|
||||
commands::versions::list_downloaded_official_versions,
|
||||
commands::versions::list_downloaded_versions,
|
||||
commands::versions::download_official_version,
|
||||
commands::versions::go_to_version_folder,
|
||||
commands::versions::save_active_version_change,
|
||||
commands::versions::get_active_version,
|
||||
commands::versions::get_active_version_folder,
|
||||
// Requirements Checking
|
||||
get_highest_simd,
|
||||
open_dir,
|
||||
|
|
|
@ -1,86 +1,87 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Cursor, Read},
|
||||
path::{Path, PathBuf},
|
||||
fs,
|
||||
io::{self, Cursor, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TexturePack {
|
||||
author: String,
|
||||
description: String,
|
||||
version: String,
|
||||
path: Option<PathBuf>,
|
||||
author: String,
|
||||
description: String,
|
||||
version: String,
|
||||
path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn extract_textures(app_handle: tauri::AppHandle, textures_array: Vec<String>) {
|
||||
let text_dir = app_handle
|
||||
.path_resolver()
|
||||
.app_dir()
|
||||
.unwrap()
|
||||
.join("data/texture_replacements");
|
||||
let text_dir = app_handle
|
||||
.path_resolver()
|
||||
.app_dir()
|
||||
.unwrap()
|
||||
.join("data/texture_replacements");
|
||||
|
||||
let target_dir = PathBuf::from(text_dir.clone()); // Doesn't need to exist
|
||||
let target_dir = PathBuf::from(text_dir.clone()); // Doesn't need to exist
|
||||
|
||||
for path in textures_array {
|
||||
println!("Extracting texture pack: {:?}", path.clone());
|
||||
// for path in textures_array {
|
||||
// println!("Extracting texture pack: {:?}", path.clone());
|
||||
|
||||
let archive: Vec<u8> = fs::read(&path.clone()).unwrap();
|
||||
// The third parameter allows you to strip away toplevel directories.
|
||||
// If `archive` contained a single directory, its contents would be extracted instead.
|
||||
match zip_extract::extract(Cursor::new(archive), &target_dir, true) {
|
||||
Ok(_) => continue,
|
||||
Err(err) => println!("{:?}", err),
|
||||
}
|
||||
}
|
||||
// let archive: Vec<u8> = fs::read(&path.clone()).unwrap();
|
||||
// // The third parameter allows you to strip away toplevel directories.
|
||||
// // If `archive` contained a single directory, its contents would be extracted instead.
|
||||
// match zip_extract::extract(Cursor::new(archive), &target_dir, true) {
|
||||
// Ok(_) => continue,
|
||||
// Err(err) => println!("{:?}", err),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn read_texture_json_file(file_path: PathBuf) -> Result<TexturePack, io::Error> {
|
||||
let zipfile = std::fs::File::open(&file_path)?;
|
||||
let mut zip = zip::ZipArchive::new(zipfile).unwrap();
|
||||
let zipfile = std::fs::File::open(&file_path)?;
|
||||
let mut zip = zip::ZipArchive::new(zipfile).unwrap();
|
||||
|
||||
// TODO: Figure out some top level schenanigans here similar to the zip extract ignoring toplevel
|
||||
let mut contents = String::new();
|
||||
zip.by_name("texture_replacements/about.json")?
|
||||
.read_to_string(&mut contents)?;
|
||||
// TODO: Figure out some top level schenanigans here similar to the zip extract ignoring toplevel
|
||||
let mut contents = String::new();
|
||||
zip
|
||||
.by_name("texture_replacements/about.json")?
|
||||
.read_to_string(&mut contents)?;
|
||||
|
||||
let pack: TexturePack = TexturePack {
|
||||
path: Some(file_path),
|
||||
..serde_json::from_str(&contents).unwrap()
|
||||
};
|
||||
Ok(pack)
|
||||
let pack: TexturePack = TexturePack {
|
||||
path: Some(file_path),
|
||||
..serde_json::from_str(&contents).unwrap()
|
||||
};
|
||||
Ok(pack)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_all_texture_packs(dir: String) -> Vec<TexturePack> {
|
||||
let dir_path = Path::new(&dir).exists();
|
||||
if !dir_path {
|
||||
println!("Textures directory doesn't exist, creating it now.");
|
||||
fs::create_dir(dir.clone()).unwrap();
|
||||
return Vec::new();
|
||||
}
|
||||
let dir_path = Path::new(&dir).exists();
|
||||
if !dir_path {
|
||||
println!("Textures directory doesn't exist, creating it now.");
|
||||
fs::create_dir(dir.clone()).unwrap();
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let entries = fs::read_dir(dir).unwrap();
|
||||
let entries = fs::read_dir(dir).unwrap();
|
||||
|
||||
let mut texture_pack_data: Vec<TexturePack> = Vec::new();
|
||||
for entry in entries {
|
||||
let path = entry.unwrap().path();
|
||||
match path.extension() {
|
||||
Some(ext) if ext == "zip" => {
|
||||
let files = match read_texture_json_file(path.clone()) {
|
||||
Ok(pack) => pack,
|
||||
Err(_e) => {
|
||||
// if the about.json file isn't inside of the expected directory this error happens
|
||||
// TODO: add this error to a logs file so players know when they install a bad texture pack
|
||||
println!("File doesn't have proper about.json: {:?}", path);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
texture_pack_data.push(files);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
let mut texture_pack_data: Vec<TexturePack> = Vec::new();
|
||||
for entry in entries {
|
||||
let path = entry.unwrap().path();
|
||||
match path.extension() {
|
||||
Some(ext) if ext == "zip" => {
|
||||
let files = match read_texture_json_file(path.clone()) {
|
||||
Ok(pack) => pack,
|
||||
Err(_e) => {
|
||||
// if the about.json file isn't inside of the expected directory this error happens
|
||||
// TODO: add this error to a logs file so players know when they install a bad texture pack
|
||||
println!("File doesn't have proper about.json: {:?}", path);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
texture_pack_data.push(files);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
return texture_pack_data;
|
||||
}
|
||||
return texture_pack_data;
|
||||
}
|
||||
|
|
25
src-tauri/src/util.rs
Normal file
25
src-tauri/src/util.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use std::process::Command;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn open_dir_in_os(dir: String) {
|
||||
Command::new("explorer")
|
||||
.arg(dir) // <- Specify the directory you'd like to open.
|
||||
.spawn()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn open_dir_in_os(dir: String) {
|
||||
Command::new("xdg-open")
|
||||
.arg(dir) // <- Specify the directory you'd like to open.
|
||||
.spawn()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn open_dir_in_os(dir: String) {
|
||||
Command::new("open")
|
||||
.arg(dir) // <- Specify the directory you'd like to open.
|
||||
.spawn()
|
||||
.unwrap();
|
||||
}
|
|
@ -28,9 +28,6 @@
|
|||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"resources": [
|
||||
"data/"
|
||||
],
|
||||
"externalBin": [
|
||||
"bin/extractor",
|
||||
"bin/gk",
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
|
||||
export async function listDownloadedOfficialVersions(): Promise<string[]> {
|
||||
export enum VersionFolders {
|
||||
OFFICIAL = "official",
|
||||
UNOFFICIAL = "unofficial",
|
||||
DEVEL = "devel",
|
||||
}
|
||||
|
||||
export async function listDownloadedVersions(
|
||||
folder: VersionFolders
|
||||
): Promise<string[]> {
|
||||
try {
|
||||
return await invoke("list_downloaded_official_versions", {});
|
||||
return await invoke("list_downloaded_versions", { versionFolder: folder });
|
||||
} catch (e) {
|
||||
console.log("TODO AH!");
|
||||
}
|
||||
|
@ -18,3 +26,41 @@ export async function downloadOfficialVersion(
|
|||
console.log("TODO AH!");
|
||||
}
|
||||
}
|
||||
|
||||
export async function openVersionFolder(folder: VersionFolders) {
|
||||
try {
|
||||
return await invoke("go_to_version_folder", { versionFolder: folder });
|
||||
} catch (e) {
|
||||
console.log("TODO AH!");
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveActiveVersionChange(
|
||||
folder: VersionFolders,
|
||||
newVersion: String
|
||||
) {
|
||||
try {
|
||||
return await invoke("save_active_version_change", {
|
||||
versionFolder: folder,
|
||||
newActiveVersion: newVersion,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("TODO AH!");
|
||||
}
|
||||
}
|
||||
|
||||
export async function getActiveVersion() {
|
||||
try {
|
||||
return await invoke("get_active_version", {});
|
||||
} catch (e) {
|
||||
console.log("TODO AH!");
|
||||
}
|
||||
}
|
||||
|
||||
export async function getActiveVersionFolder() {
|
||||
try {
|
||||
return await invoke("get_active_version_folder", {});
|
||||
} catch (e) {
|
||||
console.log("TODO AH!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,18 @@
|
|||
import Icon from "@iconify/svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { each } from "svelte/internal";
|
||||
import { downloadOfficialVersion, listDownloadedOfficialVersions } from "$lib/rpc/versions";
|
||||
import {
|
||||
downloadOfficialVersion,
|
||||
getActiveVersion,
|
||||
listDownloadedVersions,
|
||||
openVersionFolder,
|
||||
saveActiveVersionChange,
|
||||
VersionFolders,
|
||||
} from "$lib/rpc/versions";
|
||||
|
||||
let componentLoaded = false;
|
||||
let currentOfficialVersion = "v0.1.31";
|
||||
let selectedOfficialVersion = "v0.1.31";
|
||||
let currentOfficialVersion = undefined;
|
||||
let selectedOfficialVersion = undefined;
|
||||
|
||||
const tabItemActiveClasses =
|
||||
"inline-block text-sm font-bold text-center disabled:cursor-not-allowed p-4 text-orange-500 border-b-2 border-orange-500 dark:text-orange-500 dark:border-orange-500";
|
||||
|
@ -28,12 +35,14 @@
|
|||
githubLink: string | undefined;
|
||||
downloadUrl: string | undefined; // TODO - windows/mac/linux
|
||||
isDownloaded: boolean;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
let officialReleases: Release[] = [];
|
||||
|
||||
onMount(async () => {
|
||||
// TODO - check when this is null
|
||||
currentOfficialVersion = await getActiveVersion();
|
||||
selectedOfficialVersion = currentOfficialVersion;
|
||||
await refreshOfficialVersionList(undefined);
|
||||
// TODO - spinner
|
||||
componentLoaded = true;
|
||||
|
@ -41,7 +50,9 @@
|
|||
|
||||
async function refreshOfficialVersionList(evt) {
|
||||
// Check the backend to see if the folder has any versions
|
||||
const installedVersions = await listDownloadedOfficialVersions();
|
||||
const installedVersions = await listDownloadedVersions(
|
||||
VersionFolders.OFFICIAL
|
||||
);
|
||||
officialReleases = [];
|
||||
for (const version of installedVersions) {
|
||||
officialReleases = [
|
||||
|
@ -51,8 +62,7 @@
|
|||
date: undefined,
|
||||
githubLink: undefined,
|
||||
downloadUrl: undefined,
|
||||
isDownloaded: true,
|
||||
isActive: true, // TODO
|
||||
isDownloaded: true
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -75,7 +85,8 @@
|
|||
if (existingRelease.version == release.tag_name) {
|
||||
existingRelease.date = release.published_at;
|
||||
existingRelease.githubLink = release.html_url;
|
||||
existingRelease.downloadUrl = "https://github.com/open-goal/jak-project/releases/download/v0.1.32/opengoal-windows-v0.1.32.zip";
|
||||
existingRelease.downloadUrl =
|
||||
"https://github.com/open-goal/jak-project/releases/download/v0.1.32/opengoal-windows-v0.1.32.zip";
|
||||
foundExistingRelease = true;
|
||||
break;
|
||||
}
|
||||
|
@ -89,24 +100,28 @@
|
|||
version: release.tag_name,
|
||||
date: release.published_at,
|
||||
githubLink: release.html_url,
|
||||
downloadUrl: "https://github.com/open-goal/jak-project/releases/download/v0.1.32/opengoal-windows-v0.1.32.zip",
|
||||
isDownloaded: false,
|
||||
isActive: false, // TODO
|
||||
downloadUrl:
|
||||
"https://github.com/open-goal/jak-project/releases/download/v0.1.32/opengoal-windows-v0.1.32.zip",
|
||||
isDownloaded: false
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Sort releases by published date
|
||||
officialReleases.sort((a, b) => b.date.localeCompare(a.date));
|
||||
officialReleases = officialReleases.sort((a, b) => b.date.localeCompare(a.date));
|
||||
selectedOfficialVersion = "v0.1.32";
|
||||
}
|
||||
|
||||
async function saveOfficialVersionChange(evt) {
|
||||
// TODO - tauri side
|
||||
|
||||
await saveActiveVersionChange(
|
||||
VersionFolders.OFFICIAL,
|
||||
selectedOfficialVersion
|
||||
);
|
||||
currentOfficialVersion = selectedOfficialVersion;
|
||||
}
|
||||
|
||||
async function openOfficialVersionFolder(evt) {
|
||||
// TODO - tauri side
|
||||
openVersionFolder(VersionFolders.OFFICIAL);
|
||||
}
|
||||
|
||||
async function onDownloadOfficialVersion(version: String, url: String) {
|
||||
|
@ -135,16 +150,19 @@
|
|||
</div>
|
||||
<div class="flex">
|
||||
{#if currentOfficialVersion != selectedOfficialVersion}
|
||||
<Button class="!p-2 mr-2 dark:bg-green-500 hover:dark:bg-green-600" on:click={saveOfficialVersionChange}>
|
||||
<Icon
|
||||
icon="material-symbols:save"
|
||||
width="20"
|
||||
height="20"
|
||||
alt="save official version change"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
btnClass="!p-2 mr-2 rounded-md dark:bg-green-500 hover:dark:bg-green-600"
|
||||
on:click={saveOfficialVersionChange}
|
||||
>
|
||||
<Icon
|
||||
icon="material-symbols:save"
|
||||
width="20"
|
||||
height="20"
|
||||
alt="save official version change"
|
||||
/>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button class="!p-2 mr-2" on:click={refreshOfficialVersionList}>
|
||||
<Button btnClass="!p-2 mr-2 rounded-md dark:bg-orange-500 hover:dark:bg-orange-600" on:click={refreshOfficialVersionList}>
|
||||
<Icon
|
||||
icon="material-symbols:refresh"
|
||||
width="20"
|
||||
|
@ -152,7 +170,7 @@
|
|||
alt="refresh official version list"
|
||||
/>
|
||||
</Button>
|
||||
<Button class="!p-2">
|
||||
<Button btnClass="!p-2 rounded-md dark:bg-orange-500 hover:dark:bg-orange-600" on:click={openOfficialVersionFolder}>
|
||||
<Icon
|
||||
icon="material-symbols:folder-open-rounded"
|
||||
width="20"
|
||||
|
@ -174,23 +192,32 @@
|
|||
<TableHeadCell>Date</TableHeadCell>
|
||||
<TableHeadCell>Github Link</TableHeadCell>
|
||||
</TableHead>
|
||||
<TableBody class="divide-y">
|
||||
{#each officialReleases as release}
|
||||
<TableBody tableBodyClass="divide-y">
|
||||
{#each officialReleases as release (release.version)}
|
||||
<TableBodyRow>
|
||||
<TableBodyCell class="!p-4 py-0">
|
||||
<Radio
|
||||
class="disabled:cursor-not-allowed"
|
||||
bind:group={selectedOfficialVersion}
|
||||
value={release.version}
|
||||
disabled={!release.isDownloaded}
|
||||
name="official-release"
|
||||
/>
|
||||
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
|
||||
{#if release.isDownloaded}
|
||||
<Radio
|
||||
class="disabled:cursor-not-allowed p-0"
|
||||
bind:group={selectedOfficialVersion}
|
||||
value={release.version}
|
||||
disabled={!release.isDownloaded}
|
||||
name="official-release"
|
||||
/>
|
||||
{/if}
|
||||
</TableBodyCell>
|
||||
<TableBodyCell
|
||||
tdClass="px-6 py-0 whitespace-nowrap font-medium"
|
||||
tdClass="px-6 py-2 whitespace-nowrap font-medium"
|
||||
style="line-height: 0;"
|
||||
>
|
||||
<Button class="dark:bg-transparent hover:dark:bg-transparent focus:ring-0 focus:ring-offset-0" on:click={() => onDownloadOfficialVersion(release.version, release.downloadUrl)}>
|
||||
<Button
|
||||
btnClass="dark:bg-transparent hover:dark:bg-transparent focus:ring-0 focus:ring-offset-0"
|
||||
on:click={() =>
|
||||
onDownloadOfficialVersion(
|
||||
release.version,
|
||||
release.downloadUrl
|
||||
)}
|
||||
>
|
||||
{#if release.isDownloaded}
|
||||
<Icon
|
||||
icon="ic:baseline-delete-forever"
|
||||
|
@ -199,17 +226,22 @@
|
|||
color="red"
|
||||
/>
|
||||
{:else}
|
||||
<Icon icon="ic:baseline-download" color="#00d500" width="24" height="24" />
|
||||
<Icon
|
||||
icon="ic:baseline-download"
|
||||
color="#00d500"
|
||||
width="24"
|
||||
height="24"
|
||||
/>
|
||||
{/if}
|
||||
</Button>
|
||||
</TableBodyCell>
|
||||
<TableBodyCell tdClass="px-6 py-0 whitespace-nowrap font-medium"
|
||||
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium"
|
||||
>{release.version}</TableBodyCell
|
||||
>
|
||||
<TableBodyCell tdClass="px-6 py-0 whitespace-nowrap font-medium"
|
||||
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium"
|
||||
>{new Date(release.date).toLocaleDateString()}</TableBodyCell
|
||||
>
|
||||
<TableBodyCell tdClass="px-6 py-0 whitespace-nowrap font-medium"
|
||||
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium"
|
||||
><a href={release.githubLink} target="_blank" rel="noreferrer"
|
||||
><Icon icon="mdi:github" width="24" height="24" /></a
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue