lint: deleting more dead code

This commit is contained in:
Tyler Wilding 2023-03-04 00:38:42 -05:00
parent 4456f114d3
commit 4f9b0dfd62
No known key found for this signature in database
GPG key ID: 77CB07796494137E
17 changed files with 271 additions and 634 deletions

View file

@ -7,7 +7,6 @@
import Sidebar from "./components/sidebar/Sidebar.svelte";
import Background from "./components/background/Background.svelte";
import { appWindow } from "@tauri-apps/api/window";
import { log } from "$lib/utils/log";
import Header from "./components/header/Header.svelte";
import Textures from "./routes/Textures.svelte";
import Update from "./routes/Update.svelte";
@ -53,7 +52,6 @@
// Shift+Ctrl F12
if (e.code == "F12" && e.ctrlKey && e.shiftKey) {
revokeSpecificActions = false;
log.info("Hello World - Dev Tools Enabled!");
}
});
}

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { getGameTitle, getInternalName, SupportedGame } from "$lib/constants";
import { openDir } from "$lib/rpc/commands";
import { openDir } from "$lib/rpc/window";
import Icon from "@iconify/svelte";
import { configDir, join } from "@tauri-apps/api/path";
import { createEventDispatcher, onMount } from "svelte";

View file

@ -8,7 +8,11 @@
import { progressTracker } from "$lib/stores/ProgressStore";
import type { Job } from "$lib/jobs/jobs";
import { getInternalName, type SupportedGame } from "$lib/constants";
import { runCompiler, runDecompiler, updateDataDirectory } from "$lib/rpc/extractor";
import {
runCompiler,
runDecompiler,
updateDataDirectory,
} from "$lib/rpc/extractor";
import { finalizeInstallation } from "$lib/rpc/config";
import { generateSupportPackage } from "$lib/rpc/support";
@ -57,7 +61,7 @@
progressTracker.init([
{
status: "queued",
label: "Copy Files"
label: "Copy Files",
},
{
status: "queued",

View file

@ -1,68 +1,21 @@
import { log } from "$lib/utils/log";
import { invoke } from "@tauri-apps/api/tauri";
import { appDir, join } from "@tauri-apps/api/path";
export async function getHighestSimd(): Promise<string> {
try {
return await invoke("get_highest_simd");
} catch (e) {
if (e === "AVXNotSupported") {
return "noavx";
} else {
return undefined;
}
}
}
export async function openDir(dir: string): Promise<void> {
try {
return await invoke("open_dir", { dir });
} catch (e) {
log.error(e);
}
}
export async function closeSplashScreen() {
try {
invoke("close_splashscreen");
} catch (e) {
log.error(e);
}
}
export async function copyDirectory(source: string, destination: string) {
try {
return await invoke("copy_dir", { dirSrc: source, dirDest: destination });
} catch (e) {
log.error(e);
}
} catch (e) {}
}
export async function extractTextures(texturesArray: Array<String>) {
try {
return await invoke("extract_textures", { texturesArray });
} catch (e) {
log.error(e);
}
} catch (e) {}
}
export async function getAllTexturePacks() {
const textureZipDir = await join(await appDir(), "data/texture_zips/");
try {
return await invoke("get_all_texture_packs", { dir: textureZipDir });
} catch (e) {
log.error(e);
}
}
export async function openREPL() {
const appDirPath = await appDir();
try {
return await invoke("open_repl", {
projPath: `${appDirPath}data`,
currDir: appDirPath,
});
} catch (e) {
log.error(e);
}
} catch (e) {}
}

9
src/lib/rpc/window.ts Normal file
View file

@ -0,0 +1,9 @@
import { invoke } from "@tauri-apps/api/tauri";
export async function openDir(dir: string): Promise<void> {
try {
return await invoke("open_dir", { dir });
} catch (e) {
console.log(`[OG] Error encountered when trying to open dir - ${dir}`, e);
}
}

View file

@ -1,39 +0,0 @@
import { Command } from "@tauri-apps/api/shell";
import { os } from "@tauri-apps/api";
import { resolveErrorCode } from "./setup_errors";
import { installLog, log } from "$lib/utils/log";
// We'll leave this check here since it's the only thing still using a sidecar
//
// TODO - longterm, we should build this validation checking into the `extractor`
// because other things like Vulkan don't have a glewinfo equivalent
export async function isOpenGLVersionSupported(
version: string
): Promise<boolean> {
if ((await os.platform()) === "darwin") {
console.log("[OG]: MacOS isn't supported, OpenGL won't work here!");
return false;
}
// Otherwise, query for the version
let command = Command.sidecar("bin/glewinfo", ["-version", version]);
const output = await command.execute();
if (output.code === 0) {
return true;
}
log.error("opengl requirement check failed", {
version: version,
statusCode: output.code,
stdout: output.stdout,
stderr: output.stderr,
});
return false;
}
async function handleErrorCode(code: number, stepName: string) {
// isInstalling.update(() => false);
// const explaination = await resolveErrorCode(code);
// if (explaination === undefined) {
// throw new Error(`${stepName} exited with unexpected code: ${code}`);
// }
// throw new Error(explaination);
}

View file

@ -1,40 +0,0 @@
import { fileExists } from "../utils/file";
import { appDir, join } from "@tauri-apps/api/path";
import { readTextFile } from "@tauri-apps/api/fs";
import { log } from "$lib/utils/log";
interface ErrorCodeMetadataEntry {
msg: string;
}
let errorMetadata = new Map<string, ErrorCodeMetadataEntry>();
export async function resolveErrorCode(
code: number
): Promise<string | undefined> {
if (errorMetadata === undefined || errorMetadata.size == 0) {
// first time, load the metadata
const errorMetadataPath = await join(
await appDir(),
"data",
"launcher",
"error-code-metadata.json"
);
if (!(await fileExists(errorMetadataPath))) {
log.warn("could not locate error metadata file at path", {
path: errorMetadataPath,
});
return undefined;
}
const jsonData = JSON.parse(await readTextFile(errorMetadataPath));
for (var value in jsonData) {
errorMetadata.set(value, jsonData[value]);
}
}
if (errorMetadata.has(code.toString())) {
return errorMetadata.get(code.toString()).msg;
}
return undefined;
}

View file

@ -1,24 +1,24 @@
import type { VersionFolders } from "$lib/rpc/versions";
import { writable } from "svelte/store";
export interface VersionStoreSelectedInfo {
official: string | undefined;
unofficial: string | undefined;
devel: string | undefined;
}
export interface VersionStoreIFace {
activeVersionType: VersionFolders;
activeVersionName: string | undefined;
selectedVersions: VersionStoreSelectedInfo;
}
export const VersionStore = writable<VersionStoreIFace>({
activeVersionType: undefined,
activeVersionName: undefined,
selectedVersions: {
official: undefined,
unofficial: undefined,
devel: undefined,
},
});
import type { VersionFolders } from "$lib/rpc/versions";
import { writable } from "svelte/store";
export interface VersionStoreSelectedInfo {
official: string | undefined;
unofficial: string | undefined;
devel: string | undefined;
}
export interface VersionStoreIFace {
activeVersionType: VersionFolders;
activeVersionName: string | undefined;
selectedVersions: VersionStoreSelectedInfo;
}
export const VersionStore = writable<VersionStoreIFace>({
activeVersionType: undefined,
activeVersionName: undefined,
selectedVersions: {
official: undefined,
unofficial: undefined,
devel: undefined,
},
});

View file

@ -1,7 +1,6 @@
import { Convert } from "./translation_schema";
import type { TranslationSchema } from "./translation_schema";
import english from "$assets/translations/english.json";
import { log } from "$lib/utils/log";
let supportedTranslations = ["english"];
@ -9,9 +8,6 @@ export let TranslatedStrings: TranslationSchema;
export function loadTranslations(language: string) {
if (!supportedTranslations.includes(language)) {
log.error("Language not supported!", {
language: language,
});
}
// TODO - would prefer to import this by a raw path but have to import
// for vite reasons -- maybe there is a different way to ensure they are bundled?

View file

@ -1,52 +0,0 @@
import { copyDirectory } from "$lib/rpc/commands";
import { readTextFile } from "@tauri-apps/api/fs";
import { join, appDir, resourceDir } from "@tauri-apps/api/path";
import { dirExists, fileExists } from "./file";
import { log } from "./log";
export async function dataDirectoryExists(): Promise<boolean> {
return await dirExists(await join(await appDir(), "data"));
}
export async function isDataDirectoryUpToDate(): Promise<boolean> {
const resourceDirPath = await resourceDir();
const appDirPath = await appDir();
// There should be a `metadata.json` which will help us know if the directory is out of date
// aka, does the app have updated files compared to what the user has in their appDir.
const userMetaPath = await join(appDirPath, "data", "metadata.json");
const appMetaPath = await join(resourceDirPath, "data", "metadata.json");
if (!(await fileExists(userMetaPath))) {
log.warn("couldn't locate user's metadata file at path", {
path: userMetaPath,
});
return false;
}
// If it's there, read it in and check the version, compare with the app's
const userMetaVersion = JSON.parse(await readTextFile(userMetaPath)).version;
const appMetaVersion = JSON.parse(await readTextFile(appMetaPath)).version;
if (userMetaVersion != appMetaVersion) {
log.warn("user version does not match app version", {
userVersion: userMetaVersion,
appVersion: appMetaVersion,
});
return false;
}
// NOTE - the user can of course mess up their directory more, but we can only hold their hands so much
// TODO - better to add some sort of "verify local data" feature in the app imo
return true;
}
export async function copyDataDirectory(): Promise<boolean> {
const resourceDirPath = await resourceDir();
const appDirPath = await appDir();
let src = `${resourceDirPath.replaceAll("\\\\?\\", "")}data`;
let dst = `${appDirPath}data`;
try {
await copyDirectory(src, dst);
return true;
} catch (e) {
return false;
}
}

View file

@ -46,6 +46,15 @@ export async function saveFilePrompt(
});
}
export async function saveFolderPrompt(
fileType: string,
fileExtensions: string[]
): Promise<string | null> {
return await save({
filters: [{ name: fileType, extensions: fileExtensions }],
});
}
export async function isoPrompt(): Promise<string | undefined> {
const path = await filePrompt(["ISO", "iso"], "Jak ISO File");
if (path === null) {

View file

@ -1,203 +0,0 @@
import {
writeFile,
createDir,
readTextFile,
readDir,
} from "@tauri-apps/api/fs";
import { logDir, join, dirname, basename } from "@tauri-apps/api/path";
import { dirExists, fileExists } from "./file";
enum LogLevel {
Debug = "debug",
Info = "info",
Warn = "warn",
Error = "error",
}
export class Logger {
metadata: Object = {};
level: LogLevel = LogLevel.Debug;
fileNamePrefix: string;
logFileName: string = undefined;
maxFileRotate: number = 10;
buffer: string[] = [];
flushMillisecondInterval: number = 500;
flushImmediately: boolean = false;
flushInterval: any;
constructor(
metadata: Object,
level: LogLevel,
fileNamePrefix: string,
flushImmediately: boolean
) {
this.metadata = metadata;
this.level = level;
this.fileNamePrefix = fileNamePrefix;
this.flushImmediately = flushImmediately;
if (!this.flushImmediately) {
this.flushInterval = setInterval(
this.flushBuffer.bind(this),
this.flushMillisecondInterval
);
}
}
private logMessage(msg: string, level: LogLevel, meta?: Object): string {
let val = this.metadata;
// Apply provided metadata
if (meta !== undefined) {
for (const [key, value] of Object.entries(meta)) {
val[key] = `${value}`;
}
}
val["level"] = level;
val["message"] = msg;
val["timestamp"] = new Date().toISOString();
return JSON.stringify(val);
}
private async flushBuffer() {
if (this.buffer.length > 0) {
await this.writeToFile();
}
this.buffer = [];
}
private async appendToBuffer(logData: string) {
if (logData === undefined) {
return;
}
this.buffer.push(logData);
// Flush immediately if configured to do so
if (this.flushImmediately) {
this.flushBuffer();
}
}
private async writeToFile() {
// If we havn't figured out our log file name yet, figure it out now
// This is so we don't have to clear our logs manually, just rotate files
if (this.logFileName === undefined) {
this.logFileName = await this.rotateLogFile();
}
// Check if the file exists and read it's contents if so
const dir = await logDir();
const fullPath = await join(dir, this.logFileName);
const logExists = await fileExists(fullPath);
let contents = "";
if (logExists) {
contents = await readTextFile(fullPath);
if (contents === null || contents === undefined) {
contents = "";
}
} else {
await createDir(await dirname(fullPath), { recursive: true });
}
// Build up the string to append and write it
this.buffer.forEach((data) => {
if (data !== undefined && data !== null) {
contents += data + "\n";
}
});
await writeFile({ contents: contents, path: fullPath });
}
private async rotateLogFile(): Promise<string> {
const dir = await logDir();
const logDirExists = await dirExists(dir);
if (!logDirExists) {
await createDir(await dirname(dir), { recursive: true });
// The directory didn't exist, so it has no files!
return `${this.fileNamePrefix}_0.log`;
}
const logFiles = await readDir(dir);
let numLogs = 0;
let oldestLogIndex = 0;
for (let i = 0; i < logFiles.length; i++) {
const logFile = logFiles[i];
const logFileName = await basename(logFile.path);
// prefix_number.log
const [prefix, number] = logFileName.split("_");
if (prefix === this.fileNamePrefix) {
numLogs++;
oldestLogIndex = Math.min(oldestLogIndex, parseInt(number));
}
}
if (numLogs > this.maxFileRotate) {
return `${this.fileNamePrefix}_${oldestLogIndex}.log`;
} else {
return `${this.fileNamePrefix}_${numLogs}.log`;
}
}
child(meta: Object): Logger {
let newLogger = new Logger(
this.metadata,
this.level,
this.fileNamePrefix,
this.flushImmediately
);
let newMeta = this.metadata;
for (const [key, value] of Object.entries(meta)) {
newMeta[key] = `${value}`;
}
newLogger.metadata = newMeta;
newLogger.logFileName = this.logFileName;
return newLogger;
}
debug(msg: string, meta?: Object) {
if (this.level <= LogLevel.Debug) {
const logData = this.logMessage(msg, LogLevel.Debug, meta);
console.log(logData);
this.appendToBuffer(logData);
}
}
info(msg: string, meta?: Object) {
if (this.level <= LogLevel.Info) {
const logData = this.logMessage(msg, LogLevel.Info, meta);
console.log(logData);
this.appendToBuffer(logData);
}
}
warn(msg: string, meta?: Object) {
if (this.level <= LogLevel.Warn) {
const logData = this.logMessage(msg, LogLevel.Warn, meta);
console.warn(logData);
this.appendToBuffer(logData);
}
}
error(msg: string, meta?: Object) {
if (this.level <= LogLevel.Error) {
const logData = this.logMessage(msg, LogLevel.Error, meta);
console.error(logData);
this.appendToBuffer(logData);
}
}
}
export const log = new Logger(
{
name: "launcher",
},
LogLevel.Debug,
"launcher",
false
);
export const installLog = new Logger(
{
name: "launcher-install",
},
LogLevel.Debug,
"launcher-install",
true
);

View file

@ -1,11 +1,6 @@
<script>
import { openDir } from "$lib/rpc/commands";
import { appDir, join } from "@tauri-apps/api/path";
import { Alert, Button, Tabs, TabItem } from "flowbite-svelte";
import { onMount } from "svelte";
import { handleCheckUpdate } from "$lib/utils/updates";
import { UpdateStore } from "$lib/stores/AppStore";
import { Route, Router, useLocation, useParams } from "svelte-navigator";
import { Tabs, TabItem } from "flowbite-svelte";
import { useParams } from "svelte-navigator";
import General from "./settings/General.svelte";
import Folders from "./settings/Folders.svelte";
import Versions from "./settings/Versions.svelte";
@ -13,14 +8,6 @@
const params = useParams();
$: activeTab = $params["tab"];
let directory = undefined;
let logDir = undefined;
onMount(async () => {
directory = await appDir();
logDir = await join(directory, "/logs");
});
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";
const tabItemInactiveClasses =

View file

@ -1,8 +1,8 @@
<script lang="ts">
import { onMount } from "svelte";
import {
getActiveVersion,
getActiveVersionFolder,
getActiveVersion,
getActiveVersionFolder,
listDownloadedVersions,
openVersionFolder,
removeVersion,
@ -49,7 +49,10 @@
}
async function onSaveVersionChange(evt: any) {
await saveActiveVersionChange("devel", $VersionStore.selectedVersions.devel);
await saveActiveVersionChange(
"devel",
$VersionStore.selectedVersions.devel
);
// TODO if save was successful
$VersionStore.activeVersionType = "devel";
$VersionStore.activeVersionName = $VersionStore.selectedVersions.devel;

View file

@ -79,14 +79,15 @@
}
// Sort releases by published date
releases = releases.sort((a, b) =>
b.date.localeCompare(a.date)
);
releases = releases.sort((a, b) => b.date.localeCompare(a.date));
versionsLoaded = true;
}
async function saveOfficialVersionChange(evt) {
await saveActiveVersionChange("official", $VersionStore.selectedVersions.official);
await saveActiveVersionChange(
"official",
$VersionStore.selectedVersions.official
);
// TODO if save was successful
$VersionStore.activeVersionType = "official";
$VersionStore.activeVersionName = $VersionStore.selectedVersions.official;
@ -106,7 +107,10 @@
}
}
releases = releases;
await downloadOfficialVersion(event.detail.version, event.detail.downloadUrl);
await downloadOfficialVersion(
event.detail.version,
event.detail.downloadUrl
);
// TODO - indicate success or failure (toast)
// Then mark it as downloaded
for (const release of releases) {

View file

@ -1,8 +1,8 @@
<script lang="ts">
import { onMount } from "svelte";
import {
getActiveVersion,
getActiveVersionFolder,
getActiveVersion,
getActiveVersionFolder,
listDownloadedVersions,
openVersionFolder,
removeVersion,
@ -26,7 +26,8 @@
$VersionStore.activeVersionType = await getActiveVersionFolder();
$VersionStore.activeVersionName = await getActiveVersion();
if ($VersionStore.activeVersionType === "unofficial") {
$VersionStore.selectedVersions.unofficial = $VersionStore.activeVersionName;
$VersionStore.selectedVersions.unofficial =
$VersionStore.activeVersionName;
}
// Check the backend to see if the folder has any versions
const installedVersions = await listDownloadedVersions("unofficial");
@ -50,7 +51,10 @@
}
async function onSaveVersionChange(evt: any) {
await saveActiveVersionChange("unofficial", $VersionStore.selectedVersions.unofficial);
await saveActiveVersionChange(
"unofficial",
$VersionStore.selectedVersions.unofficial
);
// TODO if save was successful
$VersionStore.activeVersionType = "unofficial";
$VersionStore.activeVersionName = $VersionStore.selectedVersions.unofficial;

View file

@ -1,190 +1,194 @@
<script lang="ts">
import type { VersionFolders } from "$lib/rpc/versions";
import { VersionStore, type VersionStoreIFace } from "$lib/stores/VersionStore";
import type { ReleaseInfo } from "$lib/utils/github";
import Icon from "@iconify/svelte";
import {
Button,
Radio,
Spinner,
TabItem,
Table,
TableBody,
TableBodyCell,
TableBodyRow,
TableHead,
TableHeadCell,
} from "flowbite-svelte";
import { createEventDispatcher } from "svelte";
export let name: string;
export let description: string;
export let releaseList: ReleaseInfo[];
export let loaded: boolean;
export let releaseType: VersionFolders;
export let initiallyOpen: boolean;
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";
const tabItemInactiveClasses =
"inline-block text-sm font-normal text-center disabled:cursor-not-allowed p-4 border-b-2 border-transparent text-gray-400 hover:text-orange-300 hover:border-orange-500 dark:hover:text-orange-300 dark:text-orange-400";
const dispatch = createEventDispatcher();
function changesPending(versionStore: VersionStoreIFace): boolean {
return (
versionStore.selectedVersions[releaseType] !== undefined &&
versionStore.selectedVersions[releaseType] !== "" &&
versionStore.selectedVersions[releaseType] !== versionStore.activeVersionName
);
}
</script>
<TabItem
open={initiallyOpen}
activeClasses={tabItemActiveClasses}
inactiveClasses={tabItemInactiveClasses}
>
<span slot="title">{name}</span>
{#if !loaded}
<div class="flex flex-col justify-center items-center">
<Spinner color="yellow" size={"12"} />
</div>
{:else}
<div class="flex items-center mb-2">
<div class="grow">
<p class="text-sm text-gray-400 dark:text-gray-300">
{description}
</p>
</div>
<div class="flex">
{#if changesPending($VersionStore)}
<Button
btnClass="!p-2 mr-2 rounded-md dark:bg-green-500 hover:dark:bg-green-600 text-slate-900"
on:click={() => dispatch("versionChange")}
>
<Icon
icon="material-symbols:save"
width="20"
height="20"
alt="save version change"
/>
</Button>
{/if}
<Button
btnClass="!p-2 mr-2 rounded-md dark:bg-orange-500 hover:dark:bg-orange-600 text-slate-900"
on:click={() => dispatch("refreshVersions")}
>
<Icon
icon="material-symbols:refresh"
width="20"
height="20"
alt="refresh version list"
/>
</Button>
<Button
btnClass="!p-2 rounded-md dark:bg-orange-500 hover:dark:bg-orange-600 text-slate-900"
on:click={() => dispatch("openVersionFolder")}
>
<Icon
icon="material-symbols:folder-open-rounded"
width="20"
height="20"
alt="open version folder"
/>
</Button>
</div>
</div>
<Table>
<TableHead>
<TableHeadCell>
<span class="sr-only">Select</span>
</TableHeadCell>
<TableHeadCell>
<span class="sr-only">Controls</span>
</TableHeadCell>
<TableHeadCell>Version</TableHeadCell>
<TableHeadCell>Date</TableHeadCell>
<TableHeadCell>Changes</TableHeadCell>
</TableHead>
<TableBody tableBodyClass="divide-y">
{#each releaseList as release (release.version)}
<TableBodyRow>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
{#if release.isDownloaded}
<Radio
class="disabled:cursor-not-allowed p-0"
bind:group={$VersionStore.selectedVersions[releaseType]}
value={release.version}
disabled={!release.isDownloaded}
name={`${releaseType}-release`}
/>
{/if}
</TableBodyCell>
<TableBodyCell
tdClass="px-6 py-2 whitespace-nowrap font-medium"
style="line-height: 0;"
>
<Button
btnClass="dark:bg-transparent hover:dark:bg-transparent focus:ring-0 focus:ring-offset-0"
disabled={release.pendingAction}
on:click={async () => {
if (release.isDownloaded) {
dispatch("removeVersion", { version: release.version });
} else {
dispatch("downloadVersion", {
version: release.version,
downloadUrl: release.downloadUrl,
});
}
}}
>
{#if release.isDownloaded}
<Icon
icon="ic:baseline-delete-forever"
width="24"
height="24"
color="red"
/>
{:else if release.pendingAction}
<Spinner color="yellow" size={"6"} />
{:else if release.releaseType === "official"}
<Icon
icon="ic:baseline-download"
color="#00d500"
width="24"
height="24"
/>
{/if}
</Button>
</TableBodyCell>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium"
>{release.version}</TableBodyCell
>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
{#if release.date}
{new Date(release.date).toLocaleDateString()}
{/if}
</TableBodyCell>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
{#if release.githubLink}
<a
class="inline-block"
href={release.githubLink}
target="_blank"
rel="noreferrer"
><Icon
class="inline"
icon="mdi:github"
width="24"
height="24"
/></a
>
{/if}
</TableBodyCell>
</TableBodyRow>
{/each}
</TableBody>
</Table>
{/if}
</TabItem>
<script lang="ts">
import type { VersionFolders } from "$lib/rpc/versions";
import {
VersionStore,
type VersionStoreIFace,
} from "$lib/stores/VersionStore";
import type { ReleaseInfo } from "$lib/utils/github";
import Icon from "@iconify/svelte";
import {
Button,
Radio,
Spinner,
TabItem,
Table,
TableBody,
TableBodyCell,
TableBodyRow,
TableHead,
TableHeadCell,
} from "flowbite-svelte";
import { createEventDispatcher } from "svelte";
export let name: string;
export let description: string;
export let releaseList: ReleaseInfo[];
export let loaded: boolean;
export let releaseType: VersionFolders;
export let initiallyOpen: boolean;
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";
const tabItemInactiveClasses =
"inline-block text-sm font-normal text-center disabled:cursor-not-allowed p-4 border-b-2 border-transparent text-gray-400 hover:text-orange-300 hover:border-orange-500 dark:hover:text-orange-300 dark:text-orange-400";
const dispatch = createEventDispatcher();
function changesPending(versionStore: VersionStoreIFace): boolean {
return (
versionStore.selectedVersions[releaseType] !== undefined &&
versionStore.selectedVersions[releaseType] !== "" &&
versionStore.selectedVersions[releaseType] !==
versionStore.activeVersionName
);
}
</script>
<TabItem
open={initiallyOpen}
activeClasses={tabItemActiveClasses}
inactiveClasses={tabItemInactiveClasses}
>
<span slot="title">{name}</span>
{#if !loaded}
<div class="flex flex-col justify-center items-center">
<Spinner color="yellow" size={"12"} />
</div>
{:else}
<div class="flex items-center mb-2">
<div class="grow">
<p class="text-sm text-gray-400 dark:text-gray-300">
{description}
</p>
</div>
<div class="flex">
{#if changesPending($VersionStore)}
<Button
btnClass="!p-2 mr-2 rounded-md dark:bg-green-500 hover:dark:bg-green-600 text-slate-900"
on:click={() => dispatch("versionChange")}
>
<Icon
icon="material-symbols:save"
width="20"
height="20"
alt="save version change"
/>
</Button>
{/if}
<Button
btnClass="!p-2 mr-2 rounded-md dark:bg-orange-500 hover:dark:bg-orange-600 text-slate-900"
on:click={() => dispatch("refreshVersions")}
>
<Icon
icon="material-symbols:refresh"
width="20"
height="20"
alt="refresh version list"
/>
</Button>
<Button
btnClass="!p-2 rounded-md dark:bg-orange-500 hover:dark:bg-orange-600 text-slate-900"
on:click={() => dispatch("openVersionFolder")}
>
<Icon
icon="material-symbols:folder-open-rounded"
width="20"
height="20"
alt="open version folder"
/>
</Button>
</div>
</div>
<Table>
<TableHead>
<TableHeadCell>
<span class="sr-only">Select</span>
</TableHeadCell>
<TableHeadCell>
<span class="sr-only">Controls</span>
</TableHeadCell>
<TableHeadCell>Version</TableHeadCell>
<TableHeadCell>Date</TableHeadCell>
<TableHeadCell>Changes</TableHeadCell>
</TableHead>
<TableBody tableBodyClass="divide-y">
{#each releaseList as release (release.version)}
<TableBodyRow>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
{#if release.isDownloaded}
<Radio
class="disabled:cursor-not-allowed p-0"
bind:group={$VersionStore.selectedVersions[releaseType]}
value={release.version}
disabled={!release.isDownloaded}
name={`${releaseType}-release`}
/>
{/if}
</TableBodyCell>
<TableBodyCell
tdClass="px-6 py-2 whitespace-nowrap font-medium"
style="line-height: 0;"
>
<Button
btnClass="dark:bg-transparent hover:dark:bg-transparent focus:ring-0 focus:ring-offset-0"
disabled={release.pendingAction}
on:click={async () => {
if (release.isDownloaded) {
dispatch("removeVersion", { version: release.version });
} else {
dispatch("downloadVersion", {
version: release.version,
downloadUrl: release.downloadUrl,
});
}
}}
>
{#if release.isDownloaded}
<Icon
icon="ic:baseline-delete-forever"
width="24"
height="24"
color="red"
/>
{:else if release.pendingAction}
<Spinner color="yellow" size={"6"} />
{:else if release.releaseType === "official"}
<Icon
icon="ic:baseline-download"
color="#00d500"
width="24"
height="24"
/>
{/if}
</Button>
</TableBodyCell>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium"
>{release.version}</TableBodyCell
>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
{#if release.date}
{new Date(release.date).toLocaleDateString()}
{/if}
</TableBodyCell>
<TableBodyCell tdClass="px-6 py-2 whitespace-nowrap font-medium">
{#if release.githubLink}
<a
class="inline-block"
href={release.githubLink}
target="_blank"
rel="noreferrer"
><Icon
class="inline"
icon="mdi:github"
width="24"
height="24"
/></a
>
{/if}
</TableBodyCell>
</TableBodyRow>
{/each}
</TableBody>
</Table>
{/if}
</TabItem>