mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-11-17 18:52:06 +00:00
feat: combine loading bar and zone list
This commit is contained in:
@@ -1,80 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import Button from "primevue/button";
|
||||
import { computed, ref } from "vue";
|
||||
import { webviewAddEventListener, webviewBinds } from "@/native";
|
||||
import ProgressBar from "primevue/progressbar";
|
||||
import { webviewBinds } from "@/native";
|
||||
import ZoneSelector from "./components/ZoneSelector.vue";
|
||||
import { useZoneStore } from "./stores/ZoneStore";
|
||||
|
||||
const loadingFastFile = ref(false);
|
||||
const unlinkingFastFile = ref(false);
|
||||
const lastPercentage = ref<number>(0);
|
||||
|
||||
const performingAction = computed<boolean>(() => loadingFastFile.value || unlinkingFastFile.value);
|
||||
const zoneStore = useZoneStore();
|
||||
|
||||
async function openFastFileSelect() {
|
||||
return await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] });
|
||||
}
|
||||
|
||||
async function onOpenFastFileClick() {
|
||||
if (performingAction.value) return;
|
||||
|
||||
const fastFilePath = await openFastFileSelect();
|
||||
if (!fastFilePath) return;
|
||||
|
||||
loadingFastFile.value = true;
|
||||
lastPercentage.value = 0;
|
||||
|
||||
webviewBinds
|
||||
.loadFastFile(fastFilePath)
|
||||
.catch((e: string) => {
|
||||
console.error("Failed to load fastfile:", e);
|
||||
})
|
||||
.finally(() => {
|
||||
loadingFastFile.value = false;
|
||||
lastPercentage.value = 100;
|
||||
});
|
||||
zoneStore.loadFastFile(fastFilePath);
|
||||
}
|
||||
|
||||
async function onUnlinkFastFileClick() {
|
||||
if (performingAction.value) return;
|
||||
|
||||
const fastFilePath = await openFastFileSelect();
|
||||
if (!fastFilePath) return;
|
||||
|
||||
try {
|
||||
unlinkingFastFile.value = true;
|
||||
|
||||
let loadedZoneName: string;
|
||||
try {
|
||||
lastPercentage.value = 0;
|
||||
loadedZoneName = (await webviewBinds.loadFastFile(fastFilePath)).zoneName;
|
||||
} catch (e: unknown) {
|
||||
console.error("Failed to load fastfile:", e as string);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
lastPercentage.value = 0;
|
||||
await webviewBinds.unlinkZone(loadedZoneName);
|
||||
} catch (e: unknown) {
|
||||
console.error("Failed to unlink fastfile:", e as string);
|
||||
return;
|
||||
} finally {
|
||||
webviewBinds.unloadZone(loadedZoneName);
|
||||
}
|
||||
} finally {
|
||||
unlinkingFastFile.value = false;
|
||||
lastPercentage.value = 100;
|
||||
}
|
||||
}
|
||||
|
||||
webviewAddEventListener("zoneLoadProgress", (dto) => {
|
||||
lastPercentage.value = Math.floor(dto.percentage * 1000) / 10;
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneUnlinkProgress", (dto) => {
|
||||
lastPercentage.value = Math.floor(dto.percentage * 1000) / 10;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -83,28 +24,10 @@ webviewAddEventListener("zoneUnlinkProgress", (dto) => {
|
||||
<small>Nothing to see here yet, this is mainly for testing</small>
|
||||
|
||||
<div class="actions">
|
||||
<Button
|
||||
label="Load fastfile"
|
||||
:disabled="performingAction"
|
||||
:loading="loadingFastFile"
|
||||
@click="onOpenFastFileClick"
|
||||
/>
|
||||
<Button
|
||||
label="Unlink fastfile"
|
||||
:disabled="performingAction"
|
||||
:loading="unlinkingFastFile"
|
||||
@click="onUnlinkFastFileClick"
|
||||
/>
|
||||
<Button label="Load fastfile" @click="onOpenFastFileClick" />
|
||||
</div>
|
||||
|
||||
<ZoneSelector />
|
||||
|
||||
<ProgressBar
|
||||
v-if="performingAction"
|
||||
class="progressbar"
|
||||
:show-value="false"
|
||||
:value="lastPercentage"
|
||||
/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -113,6 +36,7 @@ webviewAddEventListener("zoneUnlinkProgress", (dto) => {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
column-gap: 0.5em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.zone-list {
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
import Button from "primevue/button";
|
||||
import ProgressBar from "primevue/progressbar";
|
||||
import Listbox from "primevue/listbox";
|
||||
import { ref, watch } from "vue";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { useZoneStore } from "@/stores/ZoneStore";
|
||||
import { webviewBinds } from "@/native";
|
||||
|
||||
interface SelectableZone {
|
||||
isLoading: boolean;
|
||||
zoneName: string;
|
||||
}
|
||||
|
||||
const zoneStore = useZoneStore();
|
||||
const selectedZone = ref<string | null>(null);
|
||||
const selectedZone = ref<SelectableZone | null>(null);
|
||||
|
||||
const availableZones = computed<SelectableZone[]>(() => {
|
||||
const result = [
|
||||
...zoneStore.zonesCurrentlyBeingLoaded.map(
|
||||
(zoneBeingLoaded) =>
|
||||
({
|
||||
isLoading: true,
|
||||
zoneName: zoneBeingLoaded,
|
||||
}) satisfies SelectableZone,
|
||||
),
|
||||
...zoneStore.loadedZones.map(
|
||||
(loadedZone) =>
|
||||
({
|
||||
isLoading: false,
|
||||
zoneName: loadedZone.name,
|
||||
}) satisfies SelectableZone,
|
||||
),
|
||||
];
|
||||
|
||||
result.sort((a, b) => a.zoneName.localeCompare(b.zoneName));
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
function onUnloadClicked() {
|
||||
if (!selectedZone.value) return;
|
||||
|
||||
webviewBinds.unloadZone(selectedZone.value).catch((e: string) => {
|
||||
webviewBinds.unloadZone(selectedZone.value.zoneName).catch((e: string) => {
|
||||
console.error("Failed to unload zone:", e);
|
||||
});
|
||||
}
|
||||
@@ -21,7 +50,8 @@ watch(
|
||||
(newValue) => {
|
||||
// Reset selection if unloaded
|
||||
if (!selectedZone.value) return;
|
||||
if (newValue.indexOf(selectedZone.value) >= 0) return;
|
||||
if (newValue.findIndex((loadedZone) => loadedZone.name === selectedZone.value?.zoneName) >= 0)
|
||||
return;
|
||||
selectedZone.value = null;
|
||||
},
|
||||
{ deep: true },
|
||||
@@ -31,7 +61,25 @@ watch(
|
||||
<template>
|
||||
<div class="zone-selector">
|
||||
<div class="zone-list">
|
||||
<Listbox v-model="selectedZone" :options="zoneStore.loadedZones" class="zone" />
|
||||
<Listbox
|
||||
v-model="selectedZone"
|
||||
:options="availableZones"
|
||||
data-key="zoneName"
|
||||
emptyMessage="No zones loaded"
|
||||
class="zone"
|
||||
>
|
||||
<template #option="{ option }: { option: SelectableZone }">
|
||||
<div class="selectable-zone">
|
||||
<span>{{ option.zoneName }}</span>
|
||||
<ProgressBar
|
||||
v-if="option.isLoading"
|
||||
class="zone-progressbar"
|
||||
:value="zoneStore.getPercentageForZoneBeingLoaded(option.zoneName)"
|
||||
:show-value="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Listbox>
|
||||
</div>
|
||||
|
||||
<div class="zone-actions">
|
||||
@@ -51,4 +99,18 @@ watch(
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.selectable-zone {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.zone-progressbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 0.2em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
export interface ZoneUnlinkProgressDto {
|
||||
zoneName: string;
|
||||
/**
|
||||
* Between 0-100
|
||||
*/
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
export interface ZoneDto {
|
||||
name: string;
|
||||
filePath: string;
|
||||
}
|
||||
|
||||
export interface ZoneLoadProgressDto {
|
||||
zoneName: string;
|
||||
/**
|
||||
* Between 0-100
|
||||
*/
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export interface ZoneLoadedDto {
|
||||
zoneName: string;
|
||||
filePath: string;
|
||||
zone: ZoneDto;
|
||||
}
|
||||
|
||||
export interface ZoneUnloadedDto {
|
||||
@@ -13,6 +20,7 @@ export interface ZoneUnloadedDto {
|
||||
}
|
||||
|
||||
export interface ZoneBinds {
|
||||
getZones(): Promise<ZoneDto[]>;
|
||||
loadFastFile(path: string): Promise<ZoneLoadedDto>;
|
||||
unloadZone(zoneName: string): Promise<void>;
|
||||
}
|
||||
|
||||
31
src/ModManUi/src/stores/UnlinkingStore.ts
Normal file
31
src/ModManUi/src/stores/UnlinkingStore.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { webviewAddEventListener, webviewBinds } from "@/native";
|
||||
|
||||
export const useUnlinkingStore = defineStore("unlinking", () => {
|
||||
const isUnlinking = ref(false);
|
||||
const lastPercentage = ref<number>(0);
|
||||
const failureMessage = ref<string | null>(null);
|
||||
|
||||
function unlinkZone(zoneName: string) {
|
||||
isUnlinking.value = true;
|
||||
lastPercentage.value = 0;
|
||||
failureMessage.value = null;
|
||||
return webviewBinds
|
||||
.unlinkZone(zoneName)
|
||||
.catch((e: string) => {
|
||||
console.error("Failed to unlink fastfile:", e);
|
||||
failureMessage.value = e;
|
||||
})
|
||||
.finally(() => {
|
||||
isUnlinking.value = false;
|
||||
lastPercentage.value = 100;
|
||||
});
|
||||
}
|
||||
|
||||
webviewAddEventListener("zoneUnlinkProgress", (dto) => {
|
||||
lastPercentage.value = dto.percentage;
|
||||
});
|
||||
|
||||
return { isUnlinking, lastPercentage, unlinkZone };
|
||||
});
|
||||
@@ -1,20 +1,72 @@
|
||||
import { ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { webviewAddEventListener } from "@/native";
|
||||
import { webviewAddEventListener, webviewBinds } from "@/native";
|
||||
import type { ZoneDto, ZoneLoadedDto } from "@/native/ZoneBinds";
|
||||
|
||||
export const useZoneStore = defineStore("zone", () => {
|
||||
const loadedZones = ref<string[]>([]);
|
||||
const loadedZones = ref<ZoneDto[]>([]);
|
||||
const zonesCurrentlyBeingLoaded = ref<string[]>([]);
|
||||
const lastPercentageByZoneName = ref<Record<string, number>>({});
|
||||
|
||||
const isLoadingZone = computed(() => zonesCurrentlyBeingLoaded.value.length > 0);
|
||||
|
||||
function loadFastFile(fastFilePath: string): Promise<ZoneLoadedDto> {
|
||||
const lastDirectorySeparator = fastFilePath.replace(/\\/g, "/").lastIndexOf("/");
|
||||
const lastDot = fastFilePath.lastIndexOf(".");
|
||||
const expectedZoneName = fastFilePath.substring(
|
||||
lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0,
|
||||
lastDot > lastDirectorySeparator ? lastDot : fastFilePath.length,
|
||||
);
|
||||
|
||||
zonesCurrentlyBeingLoaded.value.push(expectedZoneName);
|
||||
lastPercentageByZoneName.value[expectedZoneName] = 0;
|
||||
|
||||
return webviewBinds
|
||||
.loadFastFile(fastFilePath)
|
||||
.catch((e: string) => {
|
||||
console.error("Failed to load fastfile:", e);
|
||||
})
|
||||
.finally(() => {
|
||||
zonesCurrentlyBeingLoaded.value.splice(
|
||||
zonesCurrentlyBeingLoaded.value.indexOf(expectedZoneName),
|
||||
1,
|
||||
);
|
||||
delete lastPercentageByZoneName.value[expectedZoneName];
|
||||
}) as Promise<ZoneLoadedDto>;
|
||||
}
|
||||
|
||||
function getPercentageForZoneBeingLoaded(zoneName: string) {
|
||||
return lastPercentageByZoneName.value[zoneName] ?? 100;
|
||||
}
|
||||
|
||||
// Initially get all loaded zones
|
||||
webviewBinds.getZones().then((allZones) => {
|
||||
loadedZones.value = allZones;
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneLoadProgress", (dto) => {
|
||||
if (lastPercentageByZoneName.value[dto.zoneName] !== undefined) {
|
||||
lastPercentageByZoneName.value[dto.zoneName] = dto.percentage;
|
||||
}
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneLoaded", (dto) => {
|
||||
loadedZones.value.push(dto.zoneName);
|
||||
loadedZones.value.push(dto.zone);
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneUnloaded", (dto) => {
|
||||
const index = loadedZones.value.indexOf(dto.zoneName);
|
||||
const index = loadedZones.value.findIndex((zone) => zone.name === dto.zoneName);
|
||||
if (index >= 0) {
|
||||
loadedZones.value.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
return { loadedZones };
|
||||
return {
|
||||
loadedZones,
|
||||
zonesCurrentlyBeingLoaded,
|
||||
isLoadingZone,
|
||||
lastPercentageByZoneName,
|
||||
loadFastFile,
|
||||
getPercentageForZoneBeingLoaded,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user