+ {/* */}
>
);
};
\ No newline at end of file
diff --git a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx
index e0fde914..088d9cae 100644
--- a/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx
+++ b/src-ui/app/_app_controllers/plugins_controllers/FetchLatestPluginsDataController.jsx
@@ -1,115 +1,25 @@
import { useEffect } from "react";
import { usePlugins } from "@logics_configs";
-export const FetchLatestPluginsDataController = () => {
+export const FetchLatestPluginsDataController = ({ pluginsControllerHasRunRef }) => {
const {
asyncFetchPluginsInfo,
- updatePluginsData,
- downloadAndExtractPlugin,
- currentIsInitializedLoadPlugin,
- currentIsFetchedPluginsInfo,
- updateIsFetchedPluginsInfo,
} = usePlugins();
- const asyncUpdateLatestPluginsData = async () => {
+ const asyncInitFetchPluginsInfo = async () => {
try {
- const info_array = await asyncFetchPluginsInfo();
- updatePluginsData(prev => {
- // Map を利用してそれぞれの配列を plugin_id で参照できるようにする
- const info_map = new Map(info_array.map(info => [info.plugin_id, info]));
- const prev_map = new Map(prev.data.map(item => [item.plugin_id, item]));
-
- console.log(prev_map);
-
- const new_data = [];
- for (const info of info_array) {
- let new_plugin_info = {};
- if (prev_map.has(info.plugin_id)) { // plugin_id 登録済み
- const target_downloaded_plugin = prev_map.get(info.plugin_id);
-
- if (target_downloaded_plugin.is_downloaded) { // 既にダウンロード済み
- const is_latest_version_available = !(target_downloaded_plugin.plugin_version === info.plugin_version);
-
- new_plugin_info = {
- ...target_downloaded_plugin,
- is_downloaded: true,
- latest_plugin_info: { ...info },
- is_latest_version_available: is_latest_version_available,
- is_latest_version_already: (target_downloaded_plugin.downloaded_plugin_info?.plugin_version === info.plugin_version),
- };
- } else { // infoにもあり登録済みだがダウンロードされていない
- new_plugin_info = {
- ...target_downloaded_plugin,
- is_downloaded: false,
- is_latest_version_already: false,
- is_latest_version_available: info.is_latest_version_available,
- latest_plugin_info: { ...info },
- }
- }
- } else { // 未ダウンロード
- new_plugin_info = {
- plugin_id: info.plugin_id,
- is_downloaded: false,
- is_latest_version_already: false,
- latest_plugin_info: { ...info },
- };
- }
-
- new_data.push(new_plugin_info);
- }
-
- // prev.data にのみ存在するアイテム = latest plugin infoには存在しない
- // を追加し、is_outdated: true を付与
- prev.data.forEach(item => {
- if (!info_map.has(item.plugin_id)) {
- new_data.push({ ...item, is_outdated: true });
- }
- });
-
- new_data.forEach(plugin => {
- if (!plugin.is_outdated) {
- plugin.is_latest_version_available = (plugin.latest_plugin_info.is_plugin_supported);
- }
- });
-
- // ダウンロード済みで最新版じゃない場合、自動的にアップデート
- // is_latest_version_supported: true のみ。
- // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。
- new_data.forEach(async plugin => {
- if (plugin.is_enabled) {
- console.log(plugin);
-
- if (!plugin.is_latest_version_already && plugin.is_latest_version_available) {
- await downloadAndExtractPlugin(plugin);
- }
- }
- });
-
- new_data.forEach(async plugin => {
- if (plugin.is_downloaded && plugin.is_enabled) {
- if (!plugin.downloaded_plugin_info?.is_plugin_supported && !plugin.latest_plugin_info?.is_plugin_supported) {
- plugin.is_enabled = false
- }
- }
- });
-
- return new_data;
- });
-
+ await asyncFetchPluginsInfo();
} catch (error) {
console.error(error);
}
};
useEffect(() => {
- console.log(currentIsInitializedLoadPlugin.data);
- if (currentIsInitializedLoadPlugin.data && !currentIsFetchedPluginsInfo.data) {
- asyncUpdateLatestPluginsData().then(() => {
- updateIsFetchedPluginsInfo(true);
- });
+ if (!pluginsControllerHasRunRef.current.is_init_fetched_plugins_info) {
+ asyncInitFetchPluginsInfo();
+ pluginsControllerHasRunRef.current.is_init_fetched_plugins_info = true;
}
-
- }, [currentIsInitializedLoadPlugin.data]);
+ }, []);
return null;
};
\ No newline at end of file
diff --git a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx
index 23422cc5..0945df96 100644
--- a/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx
+++ b/src-ui/app/_app_controllers/plugins_controllers/LoadPluginsController.jsx
@@ -1,17 +1,9 @@
import React, { useEffect } from "react";
import { usePlugins } from "@logics_configs";
-import clsx from "clsx";
-
-if (typeof window !== "undefined") {
- window.React = React;
- window.clsx = clsx;
-}
-
-export const LoadPluginsController = () => {
+export const LoadPluginsController = ({ pluginsControllerHasRunRef }) => {
const {
asyncLoadAllPlugins,
- currentIsInitializedLoadPlugin,
updateIsInitializedLoadPlugin,
} = usePlugins();
@@ -24,11 +16,11 @@ export const LoadPluginsController = () => {
};
useEffect(() => {
-
- if (!currentIsInitializedLoadPlugin.data) {
+ if (!pluginsControllerHasRunRef.current.is_initialized_load_plugin) {
asyncInitLoadPlugins().then(() => {
updateIsInitializedLoadPlugin(true);
});
+ pluginsControllerHasRunRef.current.is_initialized_load_plugin = true;
}
}, []);
diff --git a/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx
new file mode 100644
index 00000000..2fdb51c9
--- /dev/null
+++ b/src-ui/app/_app_controllers/plugins_controllers/MergePluginsController.jsx
@@ -0,0 +1,207 @@
+import { useEffect, useRef } from "react";
+import { usePlugins } from "@logics_configs";
+import { useSoftwareVersion } from "@logics_common";
+
+export const MergePluginsController = () => {
+ const {
+ currentLoadedPlugins,
+ updatePluginsData,
+ currentPluginsData,
+ currentFetchedPluginsInfo,
+ currentSavedPluginsStatus,
+ downloadAndExtractPlugin,
+ } = usePlugins();
+ const { checkVrctVerCompatibility } = useSoftwareVersion();
+
+ // downloaded, fetched, saved の各情報をまとめてマージ
+ useEffect(() => {
+ const mergePluginData = () => {
+ updatePluginsData(prev => {
+ // downloaded, fetched, 保存済み状態のMapをそれぞれ作成(plugin_id をキー)
+ const downloaded_map = new Map(
+ currentLoadedPlugins.data.map(info => [info.plugin_id, info])
+ );
+ const fetched_map = new Map(
+ currentFetchedPluginsInfo.data.map(info => [info.plugin_id, info])
+ );
+ const saved_map = new Map(
+ currentSavedPluginsStatus.data.map(saved => [saved.plugin_id, saved])
+ );
+ const prev_map = new Map(
+ prev.data.map(item => [item.plugin_id, item])
+ );
+
+ // union_keys: Saved以外の情報に対して重複なくキーを取得する
+ const union_keys = new Set([
+ ...downloaded_map.keys(),
+ ...fetched_map.keys(),
+ ...prev_map.keys(),
+ ]);
+
+ const new_data = [];
+ for (const id of union_keys) {
+ const downloaded = downloaded_map.get(id);
+ const fetched = fetched_map.get(id);
+ const prev_plugin = prev_map.get(id);
+ let plugin = {};
+
+ if (downloaded) {
+ // ダウンロード済み情報に対してサポート確認
+ const { is_plugin_supported, is_plugin_supported_latest_vrct } =
+ checkVrctVerCompatibility(
+ downloaded.min_supported_vrct_version,
+ downloaded.max_supported_vrct_version
+ );
+ plugin = {
+ // prevの情報があれば引き継ぎつつ上書き
+ ...(prev_plugin || {}),
+ plugin_id: downloaded.plugin_id,
+ component: downloaded.component,
+ is_downloaded: true,
+ downloaded_plugin_info: {
+ ...downloaded,
+ is_plugin_supported,
+ is_plugin_supported_latest_vrct,
+ },
+ };
+
+ if (fetched) {
+ const is_latest_version_available =
+ (downloaded.plugin_version !== fetched.plugin_version && fetched.is_plugin_supported);
+ plugin = {
+ ...plugin,
+ is_outdated: false,
+ latest_plugin_info: { ...fetched },
+ is_latest_version_available:
+ plugin.is_downloaded && is_latest_version_available,
+ is_latest_version_already:
+ downloaded.plugin_version === fetched.plugin_version,
+ };
+ } else {
+ // フェッチ情報がない場合の初期状態
+ plugin = {
+ ...plugin,
+ is_latest_version_available: false,
+ is_latest_version_already: true,
+ };
+ }
+ } else if (fetched) {
+ // フェッチ情報のみの場合は、ダウンロードしていない初期状態
+ plugin = {
+ ...(prev_plugin || {}),
+ plugin_id: fetched.plugin_id,
+ is_downloaded: false,
+ is_latest_version_available: fetched.is_plugin_supported,
+ is_latest_version_already: false,
+ is_outdated: false,
+ latest_plugin_info: { ...fetched },
+ };
+ } else if (prev_plugin) {
+ // 既存情報のみ存在する場合は outdated フラグを付与
+ plugin = { ...prev_plugin, is_outdated: true };
+ }
+ // いずれかの情報がある場合のみ new_data に追加
+ if (plugin.plugin_id) {
+ new_data.push(plugin);
+ }
+ }
+
+ // 保存済み状態(currentSavedPluginsStatus)のマージ
+ // ・new_dataに存在する各プラグインに対して、保存済みの is_enabled を上書き
+ new_data.forEach(plugin => {
+ if (saved_map.has(plugin.plugin_id)) {
+ plugin.is_enabled = saved_map.get(plugin.plugin_id).is_enabled;
+ }
+ });
+ // ・prev.data には存在せず、保存済み情報にのみある場合は追加
+ for (const [id, saved] of saved_map.entries()) {
+ if (!new_data.some(item => item.plugin_id === id)) {
+ new_data.push({ plugin_id: saved.plugin_id, is_enabled: saved.is_enabled });
+ }
+ }
+
+
+ // 追加処理: ダウンロード済みかつ有効なプラグインで、サポート対象でない場合は無効化
+ new_data.forEach(plugin => {
+ if (plugin.is_downloaded && plugin.is_enabled) {
+ if (
+ !plugin.downloaded_plugin_info?.is_plugin_supported &&
+ !plugin.latest_plugin_info?.is_plugin_supported
+ ) {
+ plugin.is_enabled = false;
+ }
+ }
+ });
+
+ console.log("merged plugin data", new_data);
+ return new_data;
+ });
+ };
+
+ mergePluginData();
+ }, [currentFetchedPluginsInfo.data, currentLoadedPlugins.data, currentSavedPluginsStatus]);
+
+
+
+ // --- 自動アップデート(ダウンロード処理)のuseEffect ---
+ // ※downloadAndExtractPlugin の重複実行を防ぐため、実行中の plugin_id を useRef で管理
+ const downloadingRef = useRef(new Set());
+
+ useEffect(() => {
+ if (!currentPluginsData.data.length) return;
+ // マージ結果の currentPluginsData.data を元にダウンロード処理をチェック
+ currentPluginsData.data.forEach(plugin => {
+ if (plugin.is_downloaded &&
+ plugin.is_enabled &&
+ !plugin.is_latest_version_already &&
+ plugin.is_latest_version_available
+ ) {
+ console.log(!downloadingRef.current.has(plugin.plugin_id));
+
+ if (!downloadingRef.current.has(plugin.plugin_id)) {
+ downloadingRef.current.add(plugin.plugin_id);
+ // ※ downloadAndExtractPlugin は外部通信を伴い currentLoadedPlugins.data 更新を引き起こすので注意
+ downloadAndExtractPlugin(plugin)
+ .then(() => {
+ console.log(`Plugin ${plugin.plugin_id} updated successfully`);
+ downloadingRef.current.delete(plugin.plugin_id);
+ })
+ .catch((error) => {
+ console.error(`Plugin ${plugin.plugin_id} update failed`, error);
+ downloadingRef.current.delete(plugin.plugin_id);
+ });
+ }
+ }
+ });
+ }, [currentPluginsData.data]);
+
+
+
+
+ return null;
+};
+
+
+
+
+
+// ダウンロード済みで最新版じゃない場合、自動的にアップデート
+// // is_latest_version_supported: true のみ。
+// // 失敗した場合、現在のバージョンが非対応の場合はdisabledにする。
+// new_data.forEach(async plugin => {
+// if (plugin.is_enabled) {
+// console.log(plugin);
+
+// if (!plugin.is_latest_version_already && plugin.is_latest_version_available) {
+// await downloadAndExtractPlugin(plugin);
+// }
+// }
+// });
+
+// new_data.forEach(plugin => {
+// if (plugin.is_downloaded && plugin.is_enabled) {
+// if (!plugin.downloaded_plugin_info?.is_plugin_supported && !plugin.latest_plugin_info?.is_plugin_supported) {
+// plugin.is_enabled = false
+// }
+// }
+// });
\ No newline at end of file
diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx
index 9baed8fb..9f7f490e 100644
--- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx
+++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.jsx
@@ -8,7 +8,7 @@ export const Plugins = () => {
currentIsPluginsInitialized,
} = usePlugins();
- if (!currentIsPluginsInitialized.data) return null;
+ // if (!currentIsPluginsInitialized.data) return null;
return (
diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js
index aa8c5ad7..80e7e717 100644
--- a/src-ui/logics/configs/plugins/usePlugins.js
+++ b/src-ui/logics/configs/plugins/usePlugins.js
@@ -7,6 +7,9 @@ import {
useStore_IsPluginsInitialized,
useStore_IsInitializedLoadPlugin,
useStore_IsFetchedPluginsInfo,
+
+ useStore_FetchedPluginsInfo,
+ useStore_LoadedPlugins,
} from "@store";
import { useStdoutToPython } from "@logics/useStdoutToPython";
@@ -39,6 +42,8 @@ export const usePlugins = () => {
const { currentIsInitializedLoadPlugin, updateIsInitializedLoadPlugin, pendingIsInitializedLoadPlugin } = useStore_IsInitializedLoadPlugin();
const { currentIsFetchedPluginsInfo, updateIsFetchedPluginsInfo, pendingIsFetchedPluginsInfo } = useStore_IsFetchedPluginsInfo();
+ const { currentFetchedPluginsInfo, updateFetchedPluginsInfo, pendingFetchedPluginsInfo } = useStore_FetchedPluginsInfo();
+ const { currentLoadedPlugins, updateLoadedPlugins, pendingLoadedPlugins } = useStore_LoadedPlugins();
const { currentSavedPluginsStatus, updateSavedPluginsStatus, pendingSavedPluginsStatus } = useStore_SavedPluginsStatus();
const { currentPluginsData, updatePluginsData, pendingPluginsData } = useStore_PluginsData();
@@ -47,65 +52,24 @@ export const usePlugins = () => {
const { asyncTauriFetchGithub } = useFetch();
+
+
const generatePluginContext = (downloaded_plugin_info) => {
const plugin_context = {
registerComponent: (component) => {
if (!downloaded_plugin_info.plugin_id || !downloaded_plugin_info.location || !component) {
return console.error("An invalid plugin was detected.", downloaded_plugin_info.plugin_id, downloaded_plugin_info.location, component);
}
- updatePluginsData(prev => {
- console.log("-----updated downloaded plugin info----");
+ updateLoadedPlugins(prev => {
const prev_map = new Map(prev.data.map(item => [item.plugin_id, item]));
- const new_data = [];
- let new_value = {};
- const { is_plugin_supported, is_plugin_supported_latest_vrct } = checkVrctVerCompatibility(downloaded_plugin_info.min_supported_vrct_version, downloaded_plugin_info.max_supported_vrct_version);
-
- const target_plugin = prev_map.get(downloaded_plugin_info.plugin_id);
- if (!target_plugin) { // 未ダウンロード 新規登録
- new_value = {
- plugin_id: downloaded_plugin_info.plugin_id,
- component: component,
- is_downloaded: true,
- is_latest_version_available: false,
- is_latest_version_already: true,
- downloaded_plugin_info: {
- ...downloaded_plugin_info,
- component: component,
- is_plugin_supported: is_plugin_supported,
- is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct,
- },
- };
- return [...prev.data, new_value];
- }
-
- for (const old_plugin_data of prev.data) {
-
- if (old_plugin_data.plugin_id === downloaded_plugin_info.plugin_id) { // ダウンロード済み or 登録済 アップデート
- const target_prev_plugin = prev_map.get(downloaded_plugin_info.plugin_id);
- const is_latest_version_available = (target_prev_plugin.is_downloaded) && (target_prev_plugin.latest_plugin_info?.plugin_version) && !(downloaded_plugin_info.plugin_version === target_prev_plugin.latest_plugin_info?.plugin_version);
-
- new_value = {
- ...target_prev_plugin,
- plugin_id: downloaded_plugin_info.plugin_id,
- component: component,
- is_downloaded: true,
- is_latest_version_available: is_latest_version_available,
- is_latest_version_already: (target_plugin.plugin_version === old_plugin_data.latest_plugin_info?.plugin_info),
- downloaded_plugin_info: {
- ...downloaded_plugin_info,
- component: component,
- is_plugin_supported: is_plugin_supported,
- is_plugin_supported_latest_vrct: is_plugin_supported_latest_vrct,
- },
- };
- } else {
- new_value = old_plugin_data;
- }
- new_data.push(new_value);
- }
- return new_data;
+ prev_map.set(downloaded_plugin_info.plugin_id, {
+ ...downloaded_plugin_info,
+ component: component,
+ });
+ return Array.from(prev_map.values());
});
+
},
createAtomWithHook: (...args) => createAtomWithHook(...args),
logics: { ...logics_common, ...logics_configs, ...logics_main }
@@ -259,10 +223,11 @@ export const usePlugins = () => {
const asyncFetchPluginsInfo = async () => {
+ if (currentIsFetchedPluginsInfo.data) return;
try {
const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL);
if (response.status !== 200) {
- throw new Error("Failed to fetch plugin list, status: " + response.status);
+ throw new Error("Failed to fetch plugins list, status: " + response.status);
}
const plugins_data = response.data;
const updated_list = await Promise.all(
@@ -271,7 +236,7 @@ export const usePlugins = () => {
const plugin_info = await asyncFetchPluginInfo(plugin_data.url);
return plugin_info;
} catch (error) {
- console.error("Error fetching plugin info for URL:", plugin_data.url, error);
+ console.error("Error fetching plugin info for URL: ", plugin_data.url, error);
return {
title: plugin_data.title,
plugin_id: plugin_data.plugin_id || plugin_data.title,
@@ -282,13 +247,15 @@ export const usePlugins = () => {
}
})
);
- return updated_list;
+ updateFetchedPluginsInfo(updated_list);
+ updateIsFetchedPluginsInfo(true);
} catch (error) {
- console.error("Error fetching plugin info list:", error);
+ console.error("Error fetching plugin info list: ", error);
}
}
const asyncFetchPluginInfo = async (plugin_info_asset_url) => {
+
const release_response = await asyncTauriFetchGithub(plugin_info_asset_url);
if (release_response.status !== 200) {
throw new Error(`Failed to fetch release info from ${plugin_info_asset_url}`);
@@ -371,6 +338,12 @@ export const usePlugins = () => {
currentIsFetchedPluginsInfo,
updateIsFetchedPluginsInfo,
+ currentFetchedPluginsInfo,
+ updateFetchedPluginsInfo,
+
+ currentLoadedPlugins,
+ updateLoadedPlugins,
+
setSavedPluginsStatus,
handlePendingPlugin,
diff --git a/src-ui/store.js b/src-ui/store.js
index 58012777..3d4bac13 100644
--- a/src-ui/store.js
+++ b/src-ui/store.js
@@ -281,7 +281,9 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA
export const { atomInstance: Atom_IsPluginsInitialized, useHook: useStore_IsPluginsInitialized } = createAtomWithHook(false, "IsPluginsInitialized");
export const { atomInstance: Atom_IsInitializedLoadPlugin, useHook: useStore_IsInitializedLoadPlugin } = createAtomWithHook(false, "IsInitializedLoadPlugin");
export const { atomInstance: Atom_IsFetchedPluginsInfo, useHook: useStore_IsFetchedPluginsInfo } = createAtomWithHook(false, "IsFetchedPluginsInfo");
-export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook(false, "FetchedPluginsInfo");
+
+export const { atomInstance: Atom_FetchedPluginsInfo, useHook: useStore_FetchedPluginsInfo } = createAtomWithHook([], "FetchedPluginsInfo");
+export const { atomInstance: Atom_LoadedPlugins, useHook: useStore_LoadedPlugins } = createAtomWithHook([], "LoadedPlugins");
export const { atomInstance: Atom_SavedPluginsStatus, useHook: useStore_SavedPluginsStatus } = createAtomWithHook([], "SavedPluginsStatus");
export const { atomInstance: Atom_PluginsData, useHook: useStore_PluginsData } = createAtomWithHook([], "PluginsData");