From b0f5751e1129f23260e6e6353e9770847b4ba9fc Mon Sep 17 00:00:00 2001 From: Sakamoto Shiina <68018796+ShiinaSakamoto@users.noreply.github.com> Date: Thu, 13 Mar 2025 12:17:36 +0900 Subject: [PATCH] [Update/Refactor] Fetch and show the plugins info list. Refactor some functions. Try to fetch functions from github api just once when vrct started.(It used to every time plugin tab has opened so easy to reach to the api limit) --- package-lock.json | 8 +- src-ui/app/App.jsx | 2 +- .../_app_controllers/PluginsController.jsx | 17 +- .../_download_button/_DownloadButton.jsx | 41 ++++ .../_DownloadButton.module.scss | 30 +++ .../download_models/DownloadModels.jsx | 46 +--- .../DownloadModels.module.scss | 30 --- .../download_plugins/DownloadPlugins.jsx | 33 +++ .../DownloadPlugins.module.scss | 0 .../setting_box/_components/index.js | 3 +- .../setting_box/plugins/Plugins.jsx | 202 ++++++------------ .../setting_box/plugins/Plugins.module.scss | 30 +++ .../app/main_page/main_section/PluginHost.jsx | 6 +- src-ui/logics/common/index.js | 3 +- src-ui/logics/common/useFetch.js | 21 ++ src-ui/logics/configs/plugins/usePlugins.js | 113 ++++++++-- src-ui/store.js | 2 +- 17 files changed, 350 insertions(+), 237 deletions(-) create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx create mode 100644 src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss create mode 100644 src-ui/logics/common/useFetch.js diff --git a/package-lock.json b/package-lock.json index 6cb00da1..62bd3e7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "tauri-app", "version": "0.0.0", "dependencies": { - "@babel/standalone": "^7.26.9", + "@babel/standalone": "7.26.9", "@emotion/react": "11.14.0", "@emotion/styled": "11.14.0", "@mui/material": "6.2.0", @@ -21,18 +21,18 @@ "jotai": "2.10.3", "js-base64": "3.7.7", "js-yaml": "4.1.0", - "jszip": "^3.10.1", + "jszip": "3.10.1", "react": "18.2.0", "react-dom": "18.2.0", "react-i18next": "15.2.0", "react-resizable-layout": "0.7.2", - "semver": "^7.7.1" + "semver": "7.7.1" }, "devDependencies": { "@tauri-apps/cli": "1.6.3", "npm-run-all": "4.1.5", "sass": "1.79.4", - "vite": "^6.2.1", + "vite": "6.2.1", "vite-plugin-svgr": "4.3.0" } }, diff --git a/src-ui/app/App.jsx b/src-ui/app/App.jsx index fe4f5745..22c5107c 100644 --- a/src-ui/app/App.jsx +++ b/src-ui/app/App.jsx @@ -41,7 +41,6 @@ export const App = () => { - {(currentIsBackendReady.data === false || currentIsVrctAvailable.data === false) ? @@ -57,6 +56,7 @@ const Contents = () => { const { currentIsSoftwareUpdating } = useIsSoftwareUpdating(); return ( <> + diff --git a/src-ui/app/_app_controllers/PluginsController.jsx b/src-ui/app/_app_controllers/PluginsController.jsx index 82abb5d2..16305224 100644 --- a/src-ui/app/_app_controllers/PluginsController.jsx +++ b/src-ui/app/_app_controllers/PluginsController.jsx @@ -1,15 +1,24 @@ +import React, { useEffect, useRef } from "react"; import { usePlugins } from "@logics_configs"; - -import React, { useEffect } from "react"; if (typeof window !== "undefined") { window.React = React; } export const PluginsController = () => { - const { loadAllPlugins } = usePlugins(); + const hasRunRef = useRef(false); + const { + loadAllPlugins, + asyncUpdatePluginInfoList, + } = usePlugins(); + useEffect(() => { - loadAllPlugins(); + if (!hasRunRef.current) { + asyncUpdatePluginInfoList().then(() => { + loadAllPlugins(); + }); + } + return () => hasRunRef.current = true; }, []); return null; diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx new file mode 100644 index 00000000..41c3b7d9 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.jsx @@ -0,0 +1,41 @@ +import { useTranslation } from "react-i18next"; +import CircularProgress from "@mui/material/CircularProgress"; +import styles from "./_DownloadButton.module.scss"; + +export const _DownloadButton = ({option, ...props}) => { + const { t } = useTranslation(); + + const renderContent = () => { + const circular_progress = Math.floor(option.progress / 10) * 10; + + switch (true) { + case option.progress !== null: + return ( + <> + +

{`${Math.round(option.progress)}%`}

+ + ); + case option.is_pending: + return ; + case !option.is_downloaded: + return ( + + ); + default: + return null; + } + }; + + return
{renderContent()}
; +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss new file mode 100644 index 00000000..59fabfe3 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/_atoms/_download_button/_DownloadButton.module.scss @@ -0,0 +1,30 @@ +@import "@scss_mixins"; + +.download_container { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + max-width: 8rem; +} + +.download_button { + pointer-events: auto; + background-color: var(--dark_800_color); + padding: 0.8rem; + flex-shrink: 0; + &:hover { + background-color: var(--dark_750_color); + } + &:active { + background-color: var(--dark_800_color); + } +} +.download_button_label { + font-size: 1.2rem; +} + +.progress_label { + position: absolute; + font-size: 1rem; +} \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx index b1f2360b..73656a0f 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.jsx @@ -1,9 +1,7 @@ -import { useTranslation } from "react-i18next"; -import CircularProgress from "@mui/material/CircularProgress"; -import styles from "./DownloadModels.module.scss"; import { RadioButton, } from "../index"; +import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; export const DownloadModels = (props) => { const options = props.options.map(item => ({ @@ -19,47 +17,9 @@ export const DownloadModels = (props) => { options={options} checked_variable={props.checked_variable} column={true} - ChildComponent={ModelSelector} + ChildComponent={_DownloadButton} downloadStartFunction={props.downloadStartFunction} /> ); -}; - -const ModelSelector = ({option, ...props}) => { - const { t } = useTranslation(); - - const renderContent = () => { - const circular_progress = Math.floor(option.progress / 10) * 10; - - switch (true) { - case option.progress !== null: - return ( - <> - -

{`${Math.round(option.progress)}%`}

- - ); - case option.is_pending: - return ; - case !option.is_downloaded: - return ( - - ); - default: - return null; - } - }; - - return
{renderContent()}
; -}; +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss index 59fabfe3..e69de29b 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_models/DownloadModels.module.scss @@ -1,30 +0,0 @@ -@import "@scss_mixins"; - -.download_container { - display: flex; - align-items: center; - justify-content: center; - width: 100%; - max-width: 8rem; -} - -.download_button { - pointer-events: auto; - background-color: var(--dark_800_color); - padding: 0.8rem; - flex-shrink: 0; - &:hover { - background-color: var(--dark_750_color); - } - &:active { - background-color: var(--dark_800_color); - } -} -.download_button_label { - font-size: 1.2rem; -} - -.progress_label { - position: absolute; - font-size: 1rem; -} \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx new file mode 100644 index 00000000..7c1a17b9 --- /dev/null +++ b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.jsx @@ -0,0 +1,33 @@ +import { + SwitchBox, +} from "../index"; +import { _DownloadButton } from "../_atoms/_download_button/_DownloadButton"; + +export const DownloadPlugins = ({plugin_info, ...props}) => { + + const option = { + is_pending: plugin_info.is_pending, + is_downloaded: plugin_info.is_downloaded, + } + console.log(plugin_info); + + + return ( +
+ {/* */} + {plugin_info.is_plugin_supported ? + <_DownloadButton + option={option} + downloadStartFunction={props.downloadStartFunction} + /> + : +
+ Unavailable +
+ } +
+ ); +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/_components/download_plugins/DownloadPlugins.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/src-ui/app/config_page/setting_section/setting_box/_components/index.js b/src-ui/app/config_page/setting_section/setting_box/_components/index.js index 3a295d3c..33b0837d 100644 --- a/src-ui/app/config_page/setting_section/setting_box/_components/index.js +++ b/src-ui/app/config_page/setting_section/setting_box/_components/index.js @@ -11,4 +11,5 @@ export { Slider } from "./slider/Slider"; export { SwitchBox } from "./switch_box/SwitchBox"; export { ThresholdComponent } from "./threshold_component/ThresholdComponent"; export { WordFilter, WordFilterListToggleComponent } from "./word_filter/WordFilter"; -export { DownloadModels } from "./download_models/DownloadModels"; \ No newline at end of file +export { DownloadModels } from "./download_models/DownloadModels"; +export { DownloadPlugins } from "./download_plugins/DownloadPlugins"; \ 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 e290f292..8201cdd8 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 @@ -1,12 +1,7 @@ import React, { useState, useEffect } from "react"; -import semver from "semver"; import { usePlugins } from "@logics_configs"; import styles from "./Plugins.module.scss"; -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; - -const MAIN_VRCT_VERSION = "3.0.5"; -// PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL -const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; +import { DownloadPlugins } from "../_components"; export const Plugins = () => { return ( @@ -17,146 +12,85 @@ export const Plugins = () => { }; const PluginDownloadContainer = () => { - const [plugin_list, set_plugin_list] = useState([]); - const [download_progress, set_download_progress] = useState({}); - const { downloadAndExtractPlugin } = usePlugins(); + const { + downloadAndExtractPlugin, + currentPluginsInfoList, + updatePluginsInfoList, + } = usePlugins(); - useEffect(() => { - async function asyncFetchPluginInfoList() { - try { - // tauriFetch を使用して vrct_plugins_list.json を取得(CORS 対策) - const response = await tauriFetch(PLUGIN_LIST_URL, { - method: "GET", - responseType: ResponseType.Json, - headers: { "Cache-Control": "no-cache" } - }); - if (response.status !== 200) { - throw new Error("Failed to fetch plugin list, status: " + response.status); + const downloadStartFunction = async (plugin) => { + updatePluginsInfoList((old_value) => { + const new_value = old_value.data.map(d => { + if (d.plugin_id === plugin.plugin_id) { + d.is_pending = true; } - // 取得される plugin_list.json は各プラグインの plugin_info.json への raw URL の配列とする - const plugins_data = response.data; - const updated_list = await Promise.all( - plugins_data.map(async (plugin_data) => { - try { - const plugin_manifest = await asyncFetchPluginManifest(plugin_data.url); - return { ...plugin_manifest }; - } catch (error) { - console.error("Error fetching manifest for URL:", plugin_data.url, error); - // エラー発生時は、plugin_data.title とエラーメッセージを返す - return { - title: plugin_data.title, - plugin_id: plugin_data.plugin_id || plugin_data.title, - error: error.message, - url: plugin_data.url - }; - } - }) - ); - set_plugin_list(updated_list); - } catch (error) { - console.error("Error fetching plugin info list:", error); - } - } - asyncFetchPluginInfoList(); - }, []); - - const handleDownload = async (plugin) => { + return d; + }); + return new_value; + }); await downloadAndExtractPlugin(plugin); + updatePluginsInfoList((old_value) => { + const new_value = old_value.data.map(d => { + if (d.plugin_id === plugin.plugin_id) { + d.is_pending = false; + } + return d; + }); + return new_value; + }); }; + const plugin_list = currentPluginsInfoList.data; + // const plugin_list = [ + // { + // title: "VRCT Example Plugins 1", + // plugin_id: "vrct_plugin_example_1", + // asset_name: "vrct_plugin_example_1.zip", + // plugin_version: "0.0.6", + // min_supported_vrct_version: "3.0.4", + // max_supported_vrct_version: "3.0.6", + // is_plugin_supported: true, + // // url: manifest_url + // }, + // { + // title: "VRCT Example Plugins 2", + // plugin_id: "vrct_plugin_example_2", + // asset_name: "vrct_plugin_example_2.zip", + // plugin_version: "0.0.1", + // min_supported_vrct_version: "3.0.4", + // max_supported_vrct_version: "3.0.7", + // is_plugin_supported: true, + // // url: manifest_url + // }, + // ]; + + + + return ( -
+
{plugin_list.map((plugin) => ( -
-

{plugin.title}

-

{plugin.plugin_id}

+
+

{plugin.title}

+

{plugin.plugin_id}

{plugin.error ? (

Error: {plugin.error}

) : ( - <> -

Version: {plugin.plugin_version}

-

- Compatible: {plugin.min_compatible_version} ~ {plugin.max_compatible_version} -

- - {download_progress[plugin.plugin_id] !== undefined && ( -
- Download Progress: {download_progress[plugin.plugin_id].toFixed(0)}% -
- )} - +
+
+

Version: {plugin.plugin_version}

+

+ Compatible: {plugin.min_supported_vrct_version} ~ {plugin.max_supported_vrct_version} +

+
+ +
)}
))}
); -}; - -// GitHub Releases の latest 情報から plugin_info.json を取得する(tauriFetch を使用) -async function asyncFetchPluginManifest(manifest_url) { - // リリース情報を取得 - const release_response = await tauriFetch(manifest_url, { - method: "GET", - responseType: ResponseType.Json, - headers: { - "Accept": "application/vnd.github+json", - "User-Agent": "VRCTPluginApp" - } - }); - if (release_response.status !== 200) { - throw new Error(`Failed to fetch release info from ${manifest_url}`); - } - const release_data = release_response.data; - // assets 内に plugin_info.json があるかチェック - const manifest_asset = release_data.assets.find(asset => asset.name === "plugin_info.json"); - if (!manifest_asset) { - throw new Error("plugin_info.json not found in release assets"); - } - // plugin_info.json の内容を取得 - const manifest_response = await tauriFetch(manifest_asset.browser_download_url, { - method: "GET", - responseType: ResponseType.Json, - headers: { - "Accept": "application/json", - "User-Agent": "VRCTPluginApp", - "Cache-Control": "no-cache" - } - }); - if (manifest_response.status !== 200) { - throw new Error(`Failed to fetch plugin_info.json from ${manifest_asset.browser_download_url}`); - } - const plugin_manifest = manifest_response.data; - return { - title: plugin_manifest.title, - plugin_id: plugin_manifest.plugin_id, - plugin_version: plugin_manifest.plugin_version, - min_compatible_version: plugin_manifest.min_compatible_version, - max_compatible_version: plugin_manifest.max_compatible_version, - asset_name: plugin_manifest.asset_name, - url: manifest_url - }; -} - -export { PluginDownloadContainer }; - - - - // // プラグインのマニフェスト(plugin.json から取得した情報の例) - // const plugin_manifest = { - // compatible_lower_version: "3.0.4", - // compatible_upper_version: "3.0.6", - // // 他の情報... - // }; - - // const isPluginCompatible = (main_version, lower_version, upper_version) => { - // // lower_version 以上かつ upper_version 以下なら互換性ありと判定 - // return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); - // }; - - // if (isPluginCompatible(currentSoftwareVersion.data, plugin_manifest.compatible_lower_version, plugin_manifest.compatible_upper_version)) { - // console.log("プラグインは互換性があります。"); - // } else { - // console.error("プラグインは現在の VRCT バージョンと互換性がありません。"); - // } \ No newline at end of file +}; \ No newline at end of file diff --git a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss index a49fed11..70eb0860 100644 --- a/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss +++ b/src-ui/app/config_page/setting_section/setting_box/plugins/Plugins.module.scss @@ -2,4 +2,34 @@ display: flex; gap: 6.4rem; flex-direction: column; +} + +.plugins_list_container { + display: flex; + flex-direction: column; + align-items: center; +} + +.plugin_wrapper { + width: 100%; + display: flex; + flex-direction: column; + padding: 2rem; + &:not(:last-child) { + border-bottom: 0.1rem solid var(--dark_750_color); + } +} + +.title { + font-size: 1.6rem; +} + +.plugin_id { + font-size: 1rem; +} + +.download_button { + background-color: var(--dark_750_color); + padding: 0.4rem 0.6rem; + font-size: 1.2rem; } \ No newline at end of file diff --git a/src-ui/app/main_page/main_section/PluginHost.jsx b/src-ui/app/main_page/main_section/PluginHost.jsx index 8fc162ff..d4d50774 100644 --- a/src-ui/app/main_page/main_section/PluginHost.jsx +++ b/src-ui/app/main_page/main_section/PluginHost.jsx @@ -1,10 +1,8 @@ -// PluginHost.jsx import React from "react"; -import { useStore_LoadedPluginsList } from "@store"; +import { usePlugins } from "@logics_configs"; -// export const PluginHost = ({ location }) => { export const PluginHost = () => { - const { currentLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentLoadedPluginsList } = usePlugins(); console.log(currentLoadedPluginsList.data); return ( diff --git a/src-ui/logics/common/index.js b/src-ui/logics/common/index.js index 035f844b..5feee60e 100644 --- a/src-ui/logics/common/index.js +++ b/src-ui/logics/common/index.js @@ -11,4 +11,5 @@ export { useMessage } from "./useMessage"; export { useUpdateSoftware } from "./useUpdateSoftware"; export { useVolume } from "./useVolume"; export { useHandleNetworkConnection } from "./useHandleNetworkConnection"; -export { useIsVrctAvailable } from "./useIsVrctAvailable"; \ No newline at end of file +export { useIsVrctAvailable } from "./useIsVrctAvailable"; +export { useFetch } from "./useFetch"; \ No newline at end of file diff --git a/src-ui/logics/common/useFetch.js b/src-ui/logics/common/useFetch.js new file mode 100644 index 00000000..af9c82f6 --- /dev/null +++ b/src-ui/logics/common/useFetch.js @@ -0,0 +1,21 @@ +import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; + +export const useFetch = () => { + const asyncTauriFetchGithub = async (url) => { + console.log("tauriFetch"); + + const release_response = await tauriFetch(url, { + method: "GET", + responseType: ResponseType.Json, + headers: { + "Accept": "application/vnd.github+json", + "User-Agent": "VRCTPluginApp" + } + }); + return release_response; + }; + + return { + asyncTauriFetchGithub, + }; +}; \ No newline at end of file diff --git a/src-ui/logics/configs/plugins/usePlugins.js b/src-ui/logics/configs/plugins/usePlugins.js index b9ebc97c..4e385366 100644 --- a/src-ui/logics/configs/plugins/usePlugins.js +++ b/src-ui/logics/configs/plugins/usePlugins.js @@ -1,16 +1,31 @@ -import { invoke } from '@tauri-apps/api/tauri'; -import { createAtomWithHook, useStore_LoadedPluginsList } from "@store"; -import { useSoftwareVersion } from "@logics_configs"; +import semver from "semver"; + +import { invoke } from "@tauri-apps/api/tauri"; +import { + createAtomWithHook, + useStore_LoadedPluginsList, + useStore_PluginsInfoList, +} from "@store"; + import { transform } from "@babel/standalone"; import { writeFile, createDir, exists, removeDir, readDir, BaseDirectory, readTextFile } from "@tauri-apps/api/fs"; const dev_plugin_mapping = import.meta.glob("/src-tauri/plugins/**/index.jsx", { eager: true }); import JSZip from "jszip"; -import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; + +import { useFetch } from "@logics_common"; +import { useSoftwareVersion } from "@logics_configs"; + + +// PLUGIN_LIST_URL は中央リポジトリにある、各プラグインの plugin_info.json への URL の配列を保持する JSON の URL +const PLUGIN_LIST_URL = "https://raw.githubusercontent.com/ShiinaSakamoto/vrct_plugins_list/main/vrct_plugins_list.json"; export const usePlugins = () => { - const { updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentLoadedPluginsList, updateLoadedPluginsList } = useStore_LoadedPluginsList(); + const { currentPluginsInfoList, updatePluginsInfoList, pendingPluginsInfoList } = useStore_PluginsInfoList(); const { currentSoftwareVersion } = useSoftwareVersion(); + const { asyncTauriFetchGithub } = useFetch(); + const plugin_context = { registerComponent: ({ plugin_id, location, component }) => { if (!plugin_id || !location || !component) { @@ -137,17 +152,9 @@ export const usePlugins = () => { } }; - // GitHub API を使用して、最新リリース情報から asset_name に一致するアセットのブラウザダウンロード URL を返す const fetchLatestPluginZipUrl = async (plugin) => { const api_url = plugin.url; - const response = await tauriFetch(api_url, { - method: "GET", - responseType: ResponseType.Json, - headers: { - "Accept": "application/vnd.github+json", - "User-Agent": "VRCTPluginApp" - } - }); + const response = await asyncTauriFetchGithub(api_url); if (response.status !== 200) { throw new Error("Failed to fetch latest release info, status: " + response.status); } @@ -159,9 +166,87 @@ export const usePlugins = () => { return asset.browser_download_url; }; + + const asyncUpdatePluginInfoList = async () => { + pendingPluginsInfoList(); + try { + const response = await asyncTauriFetchGithub(PLUGIN_LIST_URL); + if (response.status !== 200) { + throw new Error("Failed to fetch plugin list, status: " + response.status); + } + const plugins_data = response.data; + const updated_list = await Promise.all( + plugins_data.map(async (plugin_data) => { + try { + 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); + // エラー発生時は、plugin_data.title とエラーメッセージを返す + return { + title: plugin_data.title, + plugin_id: plugin_data.plugin_id || plugin_data.title, + error: error.message, + url: plugin_data.url + }; + } + }) + ); + updatePluginsInfoList(updated_list); + } catch (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}`); + } + const plugin_info_json = release_response.data.assets.find(asset => asset.name === "plugin_info.json"); + if (!plugin_info_json) { + throw new Error("plugin_info.json not found in release assets"); + } + const plugin_info_json_response = await asyncTauriFetchGithub(plugin_info_json.browser_download_url); + if (plugin_info_json_response.status !== 200) { + throw new Error(`Failed to fetch plugin_info.json from ${plugin_info_json.browser_download_url}`); + } + const plugin_info = plugin_info_json_response.data; + + const isPluginCompatible = (main_version, lower_version, upper_version) => { + console.log(main_version, lower_version, upper_version); + + // lower_version 以上かつ upper_version 以下なら互換性ありと判定 + return semver.gte(main_version, lower_version) && semver.lte(main_version, upper_version); + }; + + const is_plugin_supported = isPluginCompatible(currentSoftwareVersion.data, plugin_info.min_supported_vrct_version, plugin_info.max_supported_vrct_version); + + return { + title: plugin_info.title, + plugin_id: plugin_info.plugin_id, + plugin_version: plugin_info.plugin_version, + min_supported_vrct_version: plugin_info.min_supported_vrct_version, + max_supported_vrct_version: plugin_info.max_supported_vrct_version, + is_plugin_supported: is_plugin_supported, + asset_name: plugin_info.asset_name, + url: plugin_info_asset_url + }; + } + + + return { + asyncUpdatePluginInfoList, + loadAllPlugins, downloadAndExtractPlugin, + + currentLoadedPluginsList, + updateLoadedPluginsList, + + currentPluginsInfoList, + updatePluginsInfoList, }; }; diff --git a/src-ui/store.js b/src-ui/store.js index da4c1f45..d35f02aa 100644 --- a/src-ui/store.js +++ b/src-ui/store.js @@ -275,8 +275,8 @@ export const { atomInstance: Atom_Hotkeys, useHook: useStore_Hotkeys } = createA }, "Hotkeys"); // Plugins -export const { atomInstance: Atom_InstalledPluginsPath, useHook: useStore_InstalledPluginsPath } = createAtomWithHook([], "InstalledPluginsPath"); export const { atomInstance: Atom_LoadedPluginsList, useHook: useStore_LoadedPluginsList } = createAtomWithHook([], "LoadedPluginsList"); +export const { atomInstance: Atom_PluginsInfoList, useHook: useStore_PluginsInfoList } = createAtomWithHook([], "PluginsInfoList"); // Advanced Settings export const { atomInstance: Atom_OscIpAddress, useHook: useStore_OscIpAddress } = createAtomWithHook("127.0.0.1", "OscIpAddress");