diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 823d595c..6e9319ee 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -1,12 +1,42 @@ #include "FastFileContext.h" +#include "Web/Binds/FastFileBinds.h" +#include "Web/UiCommunication.h" #include "ZoneLoading.h" +void FastFileContext::Destroy() +{ + // Unload all zones + m_loaded_zones.clear(); +} + result::Expected FastFileContext::LoadFastFile(const std::string& path) { auto zone = ZoneLoading::LoadZone(path); if (!zone) return result::Unexpected(std::move(zone.error())); - return m_loaded_zones.emplace_back(std::move(*zone)).get(); + auto* result = m_loaded_zones.emplace_back(std::move(*zone)).get(); + + ui::NotifyZoneLoaded(result->m_name, path); + + return result; +} + +result::Expected FastFileContext::UnloadZone(const std::string& zoneName) +{ + const auto existingZone = std::ranges::find_if(m_loaded_zones, + [&zoneName](const std::unique_ptr& zone) + { + return zone->m_name == zoneName; + }); + + if (existingZone != m_loaded_zones.end()) + { + m_loaded_zones.erase(existingZone); + ui::NotifyZoneUnloaded(zoneName); + return NoResult(); + } + + return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); } diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h index 74b0b3af..a3f87a42 100644 --- a/src/ModMan/Context/FastFileContext.h +++ b/src/ModMan/Context/FastFileContext.h @@ -9,7 +9,10 @@ class FastFileContext { public: + void Destroy(); + result::Expected LoadFastFile(const std::string& path); + result::Expected UnloadZone(const std::string& zoneName); std::vector> m_loaded_zones; }; diff --git a/src/ModMan/Context/ModManContext.cpp b/src/ModMan/Context/ModManContext.cpp index cf01802f..221ebbf6 100644 --- a/src/ModMan/Context/ModManContext.cpp +++ b/src/ModMan/Context/ModManContext.cpp @@ -13,5 +13,6 @@ void ModManContext::Startup() void ModManContext::Destroy() { + m_fast_file.Destroy(); m_db_thread.Terminate(); } diff --git a/src/ModMan/Web/Binds/Binds.cpp b/src/ModMan/Web/Binds/Binds.cpp index d75e5026..ab1668f5 100644 --- a/src/ModMan/Web/Binds/Binds.cpp +++ b/src/ModMan/Web/Binds/Binds.cpp @@ -8,6 +8,6 @@ namespace ui void RegisterAllBinds(webview::webview& wv) { RegisterDialogHandlerBinds(wv); - RegisterFastFileBinds(wv); + RegisterZoneBinds(wv); } } // namespace ui diff --git a/src/ModMan/Web/Binds/FastFileBinds.cpp b/src/ModMan/Web/Binds/FastFileBinds.cpp index fab40843..b2cf0d50 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.cpp +++ b/src/ModMan/Web/Binds/FastFileBinds.cpp @@ -3,8 +3,27 @@ #include "Context/ModManContext.h" #include "Web/UiCommunication.h" +#include "Json/JsonExtension.h" + namespace { + class ZoneLoadedDto + { + public: + std::string zoneName; + std::string filePath; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath); + + class ZoneUnloadedDto + { + public: + std::string zoneName; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety { ModManContext::Get().m_db_thread.Dispatch( @@ -24,11 +43,47 @@ namespace } }); } + + void UnloadZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety + { + ModManContext::Get().m_db_thread.Dispatch( + [&wv, id, zoneName] + { + auto result = ModManContext::Get().m_fast_file.UnloadZone(zoneName); + if (result) + { + con::debug("Unloaded zone \"{}\"", zoneName); + ui::PromiseResolve(wv, id, true); + } + else + { + con::warn("Failed unloading zone {}: {}", zoneName, result.error()); + ui::PromiseReject(wv, id, std::move(result).error()); + } + }); + } } // namespace namespace ui { - void RegisterFastFileBinds(webview::webview& wv) + void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath) + { + const ZoneLoadedDto dto{ + .zoneName = std::move(zoneName), + .filePath = std::move(fastFilePath), + }; + Notify(*ModManContext::Get().m_main_webview, "zoneLoaded", dto); + } + + void NotifyZoneUnloaded(std::string zoneName) + { + const ZoneUnloadedDto dto{ + .zoneName = std::move(zoneName), + }; + Notify(*ModManContext::Get().m_main_webview, "zoneUnloaded", dto); + } + + void RegisterZoneBinds(webview::webview& wv) { BindAsync(wv, "loadFastFile", @@ -36,5 +91,12 @@ namespace ui { LoadFastFile(wv, id, std::move(path)); }); + + BindAsync(wv, + "unloadZone", + [&wv](const std::string& id, std::string zoneName) + { + UnloadZone(wv, id, std::move(zoneName)); + }); } } // namespace ui diff --git a/src/ModMan/Web/Binds/FastFileBinds.h b/src/ModMan/Web/Binds/FastFileBinds.h index dc45ad9a..cf11f10d 100644 --- a/src/ModMan/Web/Binds/FastFileBinds.h +++ b/src/ModMan/Web/Binds/FastFileBinds.h @@ -4,5 +4,8 @@ namespace ui { - void RegisterFastFileBinds(webview::webview& wv); -} + void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath); + void NotifyZoneUnloaded(std::string zoneName); + + void RegisterZoneBinds(webview::webview& wv); +} // namespace ui diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index 85054db9..4a6a761c 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,7 +1,9 @@ @@ -28,9 +37,15 @@ async function onOpenFastfileClick() {

- The last path: {{ lastPath }} Loading: {{ loadingFastFile }}

+
+

Loaded zones:

+
+ {{ zone }} + +
+
diff --git a/src/ModManUi/src/native/FastFileBinds.ts b/src/ModManUi/src/native/FastFileBinds.ts index 4f417902..556d3210 100644 --- a/src/ModManUi/src/native/FastFileBinds.ts +++ b/src/ModManUi/src/native/FastFileBinds.ts @@ -1,3 +1,18 @@ +export interface ZoneLoadedDto { + zoneName: string; + filePath: string; +} + +export interface ZoneUnloadedDto { + zoneName: string; +} + export interface FastFileBinds { loadFastFile(path: string): Promise; + unloadZone(zoneName: string): Promise; +} + +export interface FastFileEventMap { + zoneLoaded: ZoneLoadedDto; + zoneUnloaded: ZoneUnloadedDto; } diff --git a/src/ModManUi/src/native/index.ts b/src/ModManUi/src/native/index.ts index 9ab6257a..d57dd38d 100644 --- a/src/ModManUi/src/native/index.ts +++ b/src/ModManUi/src/native/index.ts @@ -1,12 +1,9 @@ import type { DialogBinds } from "./DialogBinds"; -import type { FastFileBinds } from "./FastFileBinds"; - +import type { FastFileBinds, FastFileEventMap } from "./FastFileBinds"; export type NativeMethods = DialogBinds & FastFileBinds; -interface NativeEventMap { - -} +type NativeEventMap = FastFileEventMap; type WebViewExtensions = { webviewBinds: NativeMethods; diff --git a/src/ModManUi/src/stores/ZoneStore.ts b/src/ModManUi/src/stores/ZoneStore.ts new file mode 100644 index 00000000..3877ab87 --- /dev/null +++ b/src/ModManUi/src/stores/ZoneStore.ts @@ -0,0 +1,20 @@ +import { readonly, ref } from "vue"; +import { defineStore } from "pinia"; +import { webviewAddEventListener } from "@/native"; + +export const useZoneStore = defineStore("zone", () => { + const loadedZones = ref([]); + + webviewAddEventListener("zoneLoaded", (dto) => { + loadedZones.value.push(dto.zoneName); + }); + + webviewAddEventListener("zoneUnloaded", (dto) => { + const index = loadedZones.value.indexOf(dto.zoneName); + if (index >= 0) { + loadedZones.value.splice(index, 1); + } + }); + + return { loadedZones: readonly(loadedZones) }; +}); diff --git a/src/ModManUi/src/stores/counter.ts b/src/ModManUi/src/stores/counter.ts deleted file mode 100644 index 374b4d03..00000000 --- a/src/ModManUi/src/stores/counter.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ref, computed } from "vue"; -import { defineStore } from "pinia"; - -export const useCounterStore = defineStore("counter", () => { - const count = ref(0); - const doubleCount = computed(() => count.value * 2); - function increment() { - count.value++; - } - - return { count, doubleCount, increment }; -}); diff --git a/src/ZoneCommon/Zone/Zone.cpp b/src/ZoneCommon/Zone/Zone.cpp index deae50d3..8c160d89 100644 --- a/src/ZoneCommon/Zone/Zone.cpp +++ b/src/ZoneCommon/Zone/Zone.cpp @@ -8,7 +8,8 @@ Zone::Zone(std::string name, const zone_priority_t priority, GameId gameId) m_language(GameLanguage::LANGUAGE_NONE), m_game_id(gameId), m_pools(ZoneAssetPools::CreateForGame(gameId, this, priority)), - m_memory(std::make_unique()) + m_memory(std::make_unique()), + m_registered(false) { }