[Refactor] Move to src-ui/views and src-ui/logics structure.
This commit is contained in:
88
src-ui/views/app/others/error_boundary/AppErrorBoundary.jsx
Normal file
88
src-ui/views/app/others/error_boundary/AppErrorBoundary.jsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import XMarkSvg from "@images/cancel.svg?react";
|
||||
import CopySvg from "@images/copy.svg?react";
|
||||
import CheckMarkSvg from "@images/check_mark.svg?react";
|
||||
|
||||
import { ContactsContainer } from "./contacts_container/ContactsContainer";
|
||||
|
||||
import { useWindow } from "@logics_common";
|
||||
|
||||
import styles from "./AppErrorBoundary.module.scss";
|
||||
|
||||
export const AppErrorBoundary = ({children}) => {
|
||||
return (
|
||||
<ErrorBoundary
|
||||
fallbackRender={({ error }) => (
|
||||
<ErrorContainer error={error} />
|
||||
)
|
||||
}>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
const ErrorContainer = ({error}) => {
|
||||
const [is_copied, setIsCopied] = useState(false);
|
||||
|
||||
const formatted_stack = error ? formatStackTrace(error.stack) : "Unknown error";
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
if (is_copied) return;
|
||||
|
||||
await navigator.clipboard.writeText(formatted_stack);
|
||||
setIsCopied(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsCopied(false);
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<CloseButtonContainer />
|
||||
<div className={styles.wrapper}>
|
||||
<p className={styles.error_message}>An error occurred. Please restart VRCT or contact the developers.</p>
|
||||
{error ?
|
||||
<div className={styles.error_detail_container}>
|
||||
<div className={styles.error_stack_container}>
|
||||
<p className={styles.error_stack}>
|
||||
{formatted_stack}
|
||||
</p>
|
||||
</div>
|
||||
<button className={styles.copy_error_message_button} onClick={copyToClipboard}>
|
||||
<p className={styles.copy_text}>Copy</p>
|
||||
{is_copied
|
||||
? <CheckMarkSvg className={styles.check_mark_svg}/>
|
||||
: <CopySvg className={styles.copy_svg}/>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
: null}
|
||||
<ContactsContainer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Duplicated
|
||||
const CloseButtonContainer = () => {
|
||||
const { asyncCloseApp } = useWindow();
|
||||
return (
|
||||
<button className={styles.close_button_wrapper} onClick={asyncCloseApp}>
|
||||
<div className={styles.close_button}>
|
||||
<XMarkSvg className={styles.x_mark_svg}/>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const formatStackTrace = (stack) => {
|
||||
if (!stack) return "";
|
||||
// フルパスの除去(例として window.location.origin や絶対パス部分を削除)
|
||||
// ※必要に応じて正規表現を調整してください
|
||||
const formatted = stack.replace(new RegExp(window.location.origin, "g"), "");
|
||||
|
||||
return formatted;
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
justify-content: safe center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.error_message {
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
user-select: text;
|
||||
margin-bottom: 3.2rem;
|
||||
}
|
||||
|
||||
|
||||
.error_detail_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
gap: 1rem;
|
||||
}
|
||||
.error_stack_container {
|
||||
max-height: 10rem;
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
padding: 1rem;
|
||||
background-color: var(--dark_950_color);
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
.error_stack {
|
||||
font-size: 1rem;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.copy_error_message_button {
|
||||
// background-color: var(--dark_800_color);
|
||||
padding: 0.8rem 1rem;
|
||||
font-size: 1.4rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 0.4rem;
|
||||
background-color: var(--dark_825_color);
|
||||
&:hover {
|
||||
background-color: var(--dark_800_color);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--dark_850_color);
|
||||
}
|
||||
}
|
||||
|
||||
.copy_svg {
|
||||
width: 1.4rem;
|
||||
color: var(--dark_500_color);
|
||||
}
|
||||
|
||||
.check_mark_svg {
|
||||
width: 1.4rem;
|
||||
color: var(--primary_300_color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Duplicated
|
||||
.close_button_wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
width: 68px;
|
||||
aspect-ratio: 1 / 1;
|
||||
background-color: var(--error_bc_color);
|
||||
& .x_mark_svg {
|
||||
color: var(--dark_200_color);
|
||||
}
|
||||
&:hover {
|
||||
& .x_mark_svg {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--error_bc_active_color);
|
||||
}
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
|
||||
.close_button {
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
}
|
||||
|
||||
.x_mark_svg {
|
||||
width: 24px;
|
||||
transform: rotate(-45deg);
|
||||
color: var(--dark_700_color);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import styles from "./ContactsContainer.module.scss";
|
||||
|
||||
export const ContactsContainer = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<OpenLinkContainer className={styles.github_issues} href_id="github_issues" text="Github Issues"/>
|
||||
<OpenLinkContainer className={styles.google_forms} href_id="google_forms" text="Google Forms"/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
import dev_github_icon from "@images/about_vrct/dev_github_icon.png";
|
||||
import document from "@images/document.png";
|
||||
|
||||
const contacts_links = {
|
||||
github_issues: { img: dev_github_icon, href: "https://github.com/misyaguziya/VRCT/issues" },
|
||||
google_forms: { img: document, href: "https://docs.google.com/forms/d/e/1FAIpQLSei-xoydOY60ivXqhOjaTzNN8PiBQIDcNhzfy6cw2sjYkcg_g/viewform" },
|
||||
};
|
||||
|
||||
const OpenLinkContainer = ({className, href_id, text}) => {
|
||||
const href = contacts_links[href_id].href;
|
||||
const img = contacts_links[href_id].img;
|
||||
return (
|
||||
<a className={className} href={href} target="_blank" rel="noreferrer" >
|
||||
<img className={styles.contact_button_icon} src={img} />
|
||||
<p className={styles.contact_button_label}>{text}</p>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
.container {
|
||||
display: flex;
|
||||
gap: 3.2rem;
|
||||
}
|
||||
|
||||
.github_issues, .google_forms {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 1rem;
|
||||
border-radius: 0.4rem;
|
||||
gap: 1rem;
|
||||
&:hover {
|
||||
background-color: var(--dark_800_color);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--dark_900_color)
|
||||
}
|
||||
}
|
||||
.contact_button_icon {
|
||||
width: 5.2rem;
|
||||
}
|
||||
.contact_button_label {
|
||||
font-size: 1.4rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
6
src-ui/views/app/others/index.js
Normal file
6
src-ui/views/app/others/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export { WindowTitleBar } from "./window_title_bar/WindowTitleBar.jsx";
|
||||
export { SplashComponent } from "./splash_component/SplashComponent.jsx";
|
||||
export { UpdatingComponent } from "./updating_component/UpdatingComponent.jsx";
|
||||
export { ModalController } from "./modal_controller/ModalController.jsx";
|
||||
export { SnackbarController } from "./snackbar_controller/SnackbarController.jsx";
|
||||
export { AppErrorBoundary } from "./error_boundary/AppErrorBoundary.jsx";
|
||||
34
src-ui/views/app/others/modal_controller/ModalController.jsx
Normal file
34
src-ui/views/app/others/modal_controller/ModalController.jsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import styles from "./ModalController.module.scss";
|
||||
import { useStore_OpenedQuickSetting } from "@store";
|
||||
import { Vr, VrcMicMuteSyncContainer, Plugins } from "@setting_box";
|
||||
import { UpdateModal } from "./update_modal/UpdateModal";
|
||||
|
||||
export const ModalController = () => {
|
||||
const { currentOpenedQuickSetting, updateOpenedQuickSetting } = useStore_OpenedQuickSetting();
|
||||
if (currentOpenedQuickSetting.data === "") return null;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.bg_onclick_close_area} onClick={() => updateOpenedQuickSetting("")}></div>
|
||||
<div className={styles.wrapper}>
|
||||
<QuickSettingsController />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const QuickSettingsController = () => {
|
||||
const { currentOpenedQuickSetting, updateOpenedQuickSetting } = useStore_OpenedQuickSetting();
|
||||
|
||||
switch (currentOpenedQuickSetting.data) {
|
||||
case "plugins":
|
||||
return <Plugins />;
|
||||
case "vrc_mic_mute_sync":
|
||||
return <VrcMicMuteSyncContainer />;
|
||||
case "overlay":
|
||||
return <Vr />;
|
||||
case "update_software":
|
||||
return <UpdateModal />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bg_onclick_close_area {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--dark_550_color_22);
|
||||
backdrop-filter: blur(0.2rem);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
overflow-y: auto;
|
||||
background-color: var(--dark_900_color);
|
||||
width: 80%;
|
||||
height: 96%;
|
||||
padding: 2rem;
|
||||
border-radius: 0.6rem;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import clsx from "clsx";
|
||||
import styles from "./UpdateModal.module.scss";
|
||||
import { useI18n } from "@useI18n";
|
||||
import { useStore_OpenedQuickSetting } from "@store";
|
||||
import { usePlugins } from "@logics_configs";
|
||||
import {
|
||||
useComputeMode,
|
||||
useUpdateSoftware,
|
||||
useIsSoftwareUpdating,
|
||||
useSoftwareVersion,
|
||||
} from "@logics_common";
|
||||
|
||||
import { PluginCompatibilityList } from "./plugins_compatibility_list/PluginCompatibilityList";
|
||||
|
||||
export const UpdateModal = () => {
|
||||
const { t } = useI18n();
|
||||
const { updateOpenedQuickSetting } = useStore_OpenedQuickSetting();
|
||||
const { updateSoftware, updateSoftware_CUDA } = useUpdateSoftware();
|
||||
const { updateIsSoftwareUpdating } = useIsSoftwareUpdating();
|
||||
const { currentComputeMode } = useComputeMode();
|
||||
const { currentLatestSoftwareVersionInfo } = useSoftwareVersion();
|
||||
const { isAnyPluginEnabled } = usePlugins();
|
||||
|
||||
const is_latest_version_already = currentLatestSoftwareVersionInfo.data.is_update_available === false;
|
||||
const is_cpu_version = currentComputeMode.data === "cpu";
|
||||
|
||||
const onClickUpdateSoftware = () => {
|
||||
updateIsSoftwareUpdating(true);
|
||||
updateSoftware();
|
||||
}
|
||||
const onClickUpdateSoftware_CUDA = () => {
|
||||
updateIsSoftwareUpdating(true);
|
||||
updateSoftware_CUDA();
|
||||
}
|
||||
|
||||
const cpu_accept_button_class_name = clsx(styles.accept_button, {
|
||||
[styles.current_compute_version]: is_cpu_version,
|
||||
[styles.is_latest_version_already]: is_latest_version_already,
|
||||
})
|
||||
const cuda_accept_button_class_name = clsx(styles.accept_button, {
|
||||
[styles.current_compute_version]: !is_cpu_version,
|
||||
[styles.is_latest_version_already]: is_latest_version_already,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.update_section_wrapper}>
|
||||
{isAnyPluginEnabled() && <PluginCompatibilityList />}
|
||||
<div className={styles.update_section}>
|
||||
<div className={styles.cpu_section}>
|
||||
<div className={styles.button_wrapper}>
|
||||
<button className={cpu_accept_button_class_name} onClick={onClickUpdateSoftware}>CPU</button>
|
||||
{is_cpu_version ? <CurrentVersionLabel is_latest_version_already={is_latest_version_already} /> : null}
|
||||
</div>
|
||||
<div className={styles.version_desc_container}>
|
||||
<VersionDescComponent desc={t("update_modal.cpu_desc")} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.cuda_section}>
|
||||
<div className={styles.button_wrapper}>
|
||||
<button className={cuda_accept_button_class_name} onClick={onClickUpdateSoftware_CUDA}>CUDA (CPU/GPU)</button>
|
||||
{!is_cpu_version ? <CurrentVersionLabel is_latest_version_already={is_latest_version_already} is_cuda={true}/> : null}
|
||||
</div>
|
||||
<div className={styles.version_desc_container}>
|
||||
<VersionDescComponent desc={t("update_modal.cuda_desc")} />
|
||||
<VersionDescComponent desc={t("update_modal.cuda_compare_cpu_desc")} />
|
||||
<VersionDescComponent desc={t("update_modal.cuda_disk_space_desc", {size: "5GB"})} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className={styles.update_desc}>{t("update_modal.download_latest_and_restart")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.button_wrapper}>
|
||||
<button className={styles.deny_button} onClick={() => updateOpenedQuickSetting("")} >{t("update_modal.close_modal")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const VersionDescComponent = (props) => {
|
||||
return (
|
||||
<div className={styles.version_desc_wrapper}>
|
||||
<div className={styles.version_desc_point}></div>
|
||||
<p className={styles.version_desc}>{`- ${props.desc}`}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CurrentVersionLabel = (props) => {
|
||||
const { t } = useI18n();
|
||||
|
||||
if (props.is_latest_version_already) {
|
||||
return <p className={clsx(styles.current_version_label, {[styles.is_cuda]: props.is_cuda})}>{t("update_modal.is_latest_version_already")}</p>;
|
||||
}
|
||||
return <p className={clsx(styles.current_version_label, {[styles.is_cuda]: props.is_cuda})}>{t("update_modal.is_current_compute_device")}</p>;
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: safe center;
|
||||
align-items: center;
|
||||
gap: 2.4rem;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8rem;
|
||||
}
|
||||
|
||||
.update_section_wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.update_section {
|
||||
border: 0.1rem solid var(--dark_600_color);
|
||||
border-radius: 0.4rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 4rem;
|
||||
padding: 6rem 2rem;
|
||||
position: relative;
|
||||
}
|
||||
.update_desc {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 1.4rem;
|
||||
background-color: var(--dark_900_color);
|
||||
padding: 0 2.8rem;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
.cpu_section, .cuda_section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.button_wrapper {
|
||||
display: flex;
|
||||
width: 16rem;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.deny_button, .accept_button {
|
||||
font-size: 1.6rem;
|
||||
padding: 1rem;
|
||||
min-width: 10rem;
|
||||
flex: 1;
|
||||
max-width: 20rem;
|
||||
text-align: center;
|
||||
border-radius: 0.4rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.accept_button {
|
||||
background-color: var(--primary_700_color);
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
&:hover {
|
||||
background-color: var(--primary_450_color);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--primary_600_color);
|
||||
}
|
||||
&.current_compute_version {
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -1rem;
|
||||
right: -1rem;
|
||||
bottom: -1rem;
|
||||
left: -1rem;
|
||||
border: 0.1rem solid var(--primary_400_color);
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
&.is_latest_version_already {
|
||||
background-color: var(--dark_825_color);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.deny_button {
|
||||
background-color: var(--dark_825_color);
|
||||
&:hover {
|
||||
background-color: var(--dark_800_color);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--dark_850_color);
|
||||
}
|
||||
}
|
||||
|
||||
.current_version_label {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin: 0 0 0.4rem 0;
|
||||
font-size: 1.2rem;
|
||||
width: max-content;
|
||||
height: max-content;
|
||||
background-color: var(--dark_900_color);
|
||||
padding: 0 1rem;
|
||||
font-weight: 300;
|
||||
&.is_cuda {
|
||||
top: 100%;
|
||||
margin: 0.4rem 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.version_desc_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.version_desc_wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
.version_desc_point {
|
||||
width: 0.3rem;
|
||||
border-radius: 50%;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
.version_desc {
|
||||
font-size: 1.4rem;
|
||||
max-width: 48rem;
|
||||
// text-align: center;
|
||||
font-weight: 300;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useEffect } from "react";
|
||||
import styles from "./PluginCompatibilityList.module.scss";
|
||||
import { usePlugins } from "@logics_configs";
|
||||
import CheckMarkSvg from "@images/check_mark.svg?react";
|
||||
import XSvg from "@images/x_mark.svg?react";
|
||||
import WarningSvg from "@images/warning.svg?react";
|
||||
|
||||
export const PluginCompatibilityList = () => {
|
||||
const {
|
||||
enabledPluginsList,
|
||||
asyncFetchPluginsInfo,
|
||||
currentFetchedPluginsInfo,
|
||||
} = usePlugins();
|
||||
|
||||
useEffect(() => {
|
||||
asyncFetchPluginsInfo();
|
||||
}, []);
|
||||
|
||||
// ダウンロード済みのもの
|
||||
const downloaded_plugin = enabledPluginsList().filter(p => p.is_downloaded);
|
||||
|
||||
const compatible_plugins_list = [];
|
||||
const incompatible_plugins_list = [];
|
||||
for (const p of downloaded_plugin) {
|
||||
if (!p.downloaded_plugin_info?.is_plugin_supported_latest_vrct && !p.latest_plugin_info?.is_plugin_supported_latest_vrct) {
|
||||
// プラグイン最新版でも、VRCT最新版(VRCTアプデ後)に非対応のもの
|
||||
incompatible_plugins_list.push(p);
|
||||
} else {
|
||||
// 現プラグイン or 最新版が、VRCT最新版(VRCTアプデ後)に対応しているもの
|
||||
compatible_plugins_list.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
const is_any_incompatible_plugin = incompatible_plugins_list.length > 0;
|
||||
const is_any_compatible_plugin = compatible_plugins_list.length > 0;
|
||||
|
||||
if (!is_any_incompatible_plugin && !is_any_compatible_plugin) return null; // This is just for safety.
|
||||
|
||||
// Duplicate
|
||||
const is_failed_to_fetch = currentFetchedPluginsInfo.state === "error";
|
||||
const is_fetching = currentFetchedPluginsInfo.state === "pending";
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<p className={styles.title}>使用中プラグインの互換性チェック</p>
|
||||
{is_failed_to_fetch && <p>Failed to fetch plugins data</p>}
|
||||
{is_fetching && <p>Fetching plugins data...</p>}
|
||||
<div className={styles.plugins_compatibility_container}>
|
||||
{incompatible_plugins_list.map(plugin => {
|
||||
const target_data = plugin.downloaded_plugin_info;
|
||||
return <PluginContainer key={target_data.plugin_id} target_data={target_data} is_compatible={false}/>;
|
||||
})}
|
||||
{compatible_plugins_list.map(plugin => {
|
||||
const target_data = plugin.downloaded_plugin_info;
|
||||
return <PluginContainer key={target_data.plugin_id} target_data={target_data} is_compatible={true} />;
|
||||
})}
|
||||
</div>
|
||||
{is_any_incompatible_plugin &&
|
||||
<div className={styles.warning_container}>
|
||||
<WarningSvg className={styles.warning_svg}/>
|
||||
<p className={styles.warning_text}>VRCT最新バージョンで互換性のないプラグインはアップデート後に無効化されます。引き続き使用したい場合は、各プラグインの更新を待ってください。</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const PluginContainer = ({ target_data, is_compatible }) => {
|
||||
return (
|
||||
<div className={styles.plugin_box}>
|
||||
<p className={clsx(styles.plugin_label, {[styles.is_compatible]: is_compatible})} >{target_data.title}</p>
|
||||
{is_compatible
|
||||
? <CheckMarkSvg className={styles.check_mark_svg}/>
|
||||
: <XSvg className={styles.x_svg}/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.plugins_compatibility_container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.2rem 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.plugin_box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.4rem 0.6rem;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.plugin_label {
|
||||
font-size: 1.4rem;
|
||||
color: var(--error_bc_color);
|
||||
&.is_compatible {
|
||||
color: var(--primary_300_color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.check_mark_svg {
|
||||
width: 1.8rem;
|
||||
color: var(--primary_300_color);
|
||||
}
|
||||
.x_svg {
|
||||
width: 1.8rem;
|
||||
color: var(--error_bc_color);
|
||||
}
|
||||
|
||||
.warning_container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.warning_svg {
|
||||
padding-bottom: 0.4rem;
|
||||
width: 2.4rem;
|
||||
color: var(--warning_color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning_text {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
:root {
|
||||
--toastify-color-light: #fff;
|
||||
--toastify-color-dark: var(--dark_950_color);
|
||||
--toastify-color-info: var(--sent_400_color);
|
||||
--toastify-color-success: var(--primary_400_color);
|
||||
--toastify-color-warning: var(--warning_bc_color);
|
||||
--toastify-color-error: var(--error_bc_color);
|
||||
--toastify-color-transparent: rgba(255, 255, 255, 0.7);
|
||||
|
||||
--toastify-icon-color-info: var(--toastify-color-info);
|
||||
--toastify-icon-color-success: var(--toastify-color-success);
|
||||
--toastify-icon-color-warning: var(--toastify-color-warning);
|
||||
--toastify-icon-color-error: var(--toastify-color-error);
|
||||
|
||||
--toastify-container-width: fit-content;
|
||||
--toastify-toast-width: 32rem;
|
||||
--toastify-toast-offset: 1.6rem;
|
||||
--toastify-toast-top: max(var(--toastify-toast-offset), env(safe-area-inset-top));
|
||||
--toastify-toast-right: max(var(--toastify-toast-offset), env(safe-area-inset-right));
|
||||
--toastify-toast-left: max(var(--toastify-toast-offset), env(safe-area-inset-left));
|
||||
--toastify-toast-bottom: max(var(--toastify-toast-offset), env(safe-area-inset-bottom));
|
||||
--toastify-toast-background: #fff;
|
||||
--toastify-toast-padding: 1.4rem;
|
||||
--toastify-toast-min-height: 6.4rem;
|
||||
--toastify-toast-max-height: 80rem;
|
||||
--toastify-toast-bd-radius: 0.6rem;
|
||||
--toastify-toast-shadow: .0 0.4rem 1.2rem rgba(0, 0, 0, 0.1);
|
||||
--toastify-font-family: var(--font_family);
|
||||
--toastify-z-index: 9999;
|
||||
--toastify-text-color-light: #757575;
|
||||
--toastify-text-color-dark: var(--dark_basic_text_color);
|
||||
|
||||
/* Used only for colored theme */
|
||||
--toastify-text-color-info: var(--dark_basic_text_color);
|
||||
--toastify-text-color-success: var(--dark_basic_text_color);
|
||||
--toastify-text-color-warning: var(--dark_basic_text_color);
|
||||
--toastify-text-color-error: var(--dark_basic_text_color);
|
||||
|
||||
--toastify-spinner-color: #616161;
|
||||
--toastify-spinner-color-empty-area: #e0e0e0;
|
||||
--toastify-color-progress-light: linear-gradient(to right, #4cd964, #5ac8fa, #007aff, #34aadc, #5856d6, #ff2d55);
|
||||
--toastify-color-progress-dark: #bb86fc;
|
||||
--toastify-color-progress-info: var(--toastify-color-info);
|
||||
--toastify-color-progress-success: var(--toastify-color-success);
|
||||
--toastify-color-progress-warning: var(--toastify-color-warning);
|
||||
--toastify-color-progress-error: var(--toastify-color-error);
|
||||
/* used to control the opacity of the progress trail */
|
||||
--toastify-color-progress-bgo: 0.2;
|
||||
}
|
||||
|
||||
|
||||
.Toastify__toast {
|
||||
// --------------------------------------------------------
|
||||
// Default Settings
|
||||
// --------------------------------------------------------
|
||||
position: relative;
|
||||
touch-action: none;
|
||||
// width: var(--toastify-toast-width);
|
||||
min-height: var(--toastify-toast-min-height);
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 1rem;
|
||||
// padding: var(--toastify-toast-padding);
|
||||
border-radius: 0.6rem;
|
||||
box-shadow: none;
|
||||
max-height: var(--toastify-toast-max-height);
|
||||
// font-family: "Yu Gothic UI";
|
||||
// font-family: var(--toastify-font-family);
|
||||
// z-index: 0;
|
||||
// display: flex;
|
||||
// flex: 1 auto;
|
||||
// align-items: center;
|
||||
word-break: break-word;
|
||||
// --------------------------------------------------------
|
||||
// --------------------------------------------------------
|
||||
|
||||
|
||||
// Comment out above and override. Commented out is just for memorization.
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
font-size: 1.4rem;
|
||||
width: fit-content;
|
||||
max-width: 70vw;
|
||||
padding-right: 4rem;
|
||||
background-color: var(--dark_950_color);
|
||||
gap: 0.6rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.Toastify__progress-bar--wrp {
|
||||
height: 0.4rem;
|
||||
}
|
||||
.Toastify__progress-bar--success {
|
||||
background: var(--success_bc_color);
|
||||
}
|
||||
|
||||
.Toastify__progress-bar--warning {
|
||||
background: var(--warning_bc_color);
|
||||
}
|
||||
|
||||
.Toastify__progress-bar--error {
|
||||
background: var(--error_bc_color);
|
||||
}
|
||||
|
||||
|
||||
.Toastify__toast-icon {
|
||||
width: fit-content;
|
||||
max-width: 2.8rem;
|
||||
min-width: 2.8rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes fade_in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes fade_out {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.fade_in {
|
||||
opacity: 0;
|
||||
animation-name: fade_in;
|
||||
animation-duration: 0.1s;
|
||||
animation-timing-function: ease-out;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
.fade_out {
|
||||
opacity: 1;
|
||||
animation-name: fade_out;
|
||||
animation-duration: 0.1s;
|
||||
animation-timing-function: ease-in;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import React, { useEffect, useState, isValidElement } from "react";
|
||||
import { ToastContainer, toast, cssTransition } from "react-toastify";
|
||||
import clsx from "clsx";
|
||||
|
||||
import "./ReactToastifyOverrideClass.scss";
|
||||
import styles from "./SnackbarController.module.scss";
|
||||
|
||||
import XMarkSvg from "@images/cancel.svg?react";
|
||||
import WarningSvg from "@images/warning.svg?react";
|
||||
import MegaphoneSvg from "@images/megaphone.svg?react";
|
||||
import CheckMarkSvg from "@images/check_mark.svg?react";
|
||||
import ErrorSvg from "@images/error.svg?react";
|
||||
|
||||
import { useNotificationStatus } from "@logics_common";
|
||||
|
||||
const CustomTransition = cssTransition({
|
||||
enter: "fade_in",
|
||||
exit: "fade_out",
|
||||
collapse: false,
|
||||
});
|
||||
|
||||
|
||||
export const SnackbarController = () => {
|
||||
const { currentNotificationStatus, closeNotification } = useNotificationStatus();
|
||||
const [containerKey, setContainerKey] = useState(0);
|
||||
|
||||
const settings = currentNotificationStatus.data;
|
||||
|
||||
const snackbar_classname = clsx(
|
||||
styles.snackbar_content,
|
||||
{
|
||||
[styles.is_success]: settings.status === "success",
|
||||
[styles.is_warning]: settings.status === "warning",
|
||||
[styles.is_error]: settings.status === "error",
|
||||
}
|
||||
);
|
||||
|
||||
let hide_duration = 5000;
|
||||
if (settings.options?.hide_duration === null) {
|
||||
hide_duration = false;
|
||||
} else if (Number(settings.options?.hide_duration)) {
|
||||
hide_duration = Number(settings.options?.hide_duration);
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!settings.is_open) return;
|
||||
|
||||
const message_text = settings.message;
|
||||
const category_id = settings.category_id ? settings.category_id : message_text;
|
||||
|
||||
const to_hide_progress_bar = (settings.options?.to_hide_progress_bar === true) ? true : false;
|
||||
|
||||
const asyncShowNotification = async () => {
|
||||
setTimeout(() => {
|
||||
toast(message_text, {
|
||||
toastId: category_id,
|
||||
type: settings.status,
|
||||
autoClose: hide_duration,
|
||||
transition: CustomTransition,
|
||||
toastClassName: snackbar_classname,
|
||||
hideProgressBar: to_hide_progress_bar,
|
||||
progressClassName: styles.toast_progress,
|
||||
closeButton: <CloseButtonContainer />,
|
||||
onClose: () => {
|
||||
closeNotification();
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// setContainerKey(prevKey => prevKey + 1);
|
||||
asyncShowNotification();
|
||||
|
||||
}, [settings]);
|
||||
|
||||
return (
|
||||
<ToastContainer
|
||||
// key={containerKey}
|
||||
position="bottom-left"
|
||||
transition={CustomTransition}
|
||||
hideProgressBar={false}
|
||||
newestOnTop={false}
|
||||
closeOnClick={false}
|
||||
pauseOnFocusLoss={false}
|
||||
draggable={false}
|
||||
pauseOnHover={true}
|
||||
theme="dark"
|
||||
icon={({ type }) => {
|
||||
switch (type) {
|
||||
case "info":
|
||||
return <MegaphoneSvg className={styles.megaphone_svg} />;
|
||||
case "error":
|
||||
return <ErrorSvg className={styles.error_svg} />;
|
||||
case "success":
|
||||
return <CheckMarkSvg className={styles.check_mark_svg} />;
|
||||
case "warning":
|
||||
return <WarningSvg className={styles.warning_svg} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const CloseButtonContainer = ({ closeToast }) => {
|
||||
return (
|
||||
<button className={styles.close_button_wrapper} onClick={closeToast}>
|
||||
<div className={styles.close_button}>
|
||||
<XMarkSvg className={styles.x_mark_svg} />
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
.snackbar_content {
|
||||
position: relative;
|
||||
padding: 1.2rem 1.6rem;
|
||||
border-radius: 0.8rem;
|
||||
font-size: 1.4rem;
|
||||
box-shadow: 0 0.2rem 0.8rem rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
// .is_success {
|
||||
// background-color: var(--success_bc_color);
|
||||
// }
|
||||
|
||||
// .is_warning {
|
||||
// background-color: var(--warning_bc_color);
|
||||
// }
|
||||
|
||||
// .is_error {
|
||||
// background-color: var(--error_bc_color);
|
||||
// }
|
||||
|
||||
|
||||
.megaphone_svg {
|
||||
color: var(--dark_200_color);
|
||||
width: 2.4rem;
|
||||
}
|
||||
.error_svg {
|
||||
color: var(--error_bc_color);
|
||||
width: 2.4rem;
|
||||
}
|
||||
.check_mark_svg {
|
||||
color: var(--primary_400_color);
|
||||
width: 2rem;
|
||||
}
|
||||
.warning_svg {
|
||||
color: var(--warning_color);
|
||||
width: 2.4rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Duplicated (Customized)
|
||||
.close_button_wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
width: 5.6rem;
|
||||
aspect-ratio: 1 / 1;
|
||||
&:hover {
|
||||
background-color: var(--error_bc_color);
|
||||
& .x_mark_svg {
|
||||
color: var(--dark_200_color);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--error_bc_active_color);
|
||||
}
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
|
||||
.close_button {
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
}
|
||||
|
||||
.x_mark_svg {
|
||||
width: 2rem;
|
||||
transform: rotate(-45deg);
|
||||
color: var(--dark_700_color);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
85
src-ui/views/app/others/splash_component/SplashComponent.jsx
Normal file
85
src-ui/views/app/others/splash_component/SplashComponent.jsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import styles from "./SplashComponent.module.scss";
|
||||
import { StartUpProgressContainer } from "./start_up_progress_container/StartUpProgressContainer";
|
||||
import { DownloadModelsContainer } from "./download_models_container/DownloadModelsContainer";
|
||||
import MegaphoneSvg from "@images/megaphone.svg?react";
|
||||
import XMarkSvg from "@images/cancel.svg?react";
|
||||
import { useWindow } from "@logics_common";
|
||||
import clsx from "clsx";
|
||||
|
||||
export const SplashComponent = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<StartUpProgressContainer />
|
||||
<DownloadModelsContainer />
|
||||
<AnnouncementsContainer />
|
||||
<CloseButtonContainer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SHOW_MEGAPHONE_TIME = 500;
|
||||
|
||||
const AnnouncementsContainer = () => {
|
||||
const labels = ["Check the Latest Status", "最新の状況を確認"];
|
||||
const [is_shown, setIsShown] = useState(0);
|
||||
const [currentLabelIndex, setCurrentLabelIndex] = useState(0);
|
||||
const [is_labels_active, setIsLabelsActive] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const showTimeout = setTimeout(() => {
|
||||
setIsShown(true);
|
||||
}, SHOW_MEGAPHONE_TIME);
|
||||
|
||||
const labelsTimeout = setTimeout(() => {
|
||||
setIsLabelsActive(true);
|
||||
}, SHOW_MEGAPHONE_TIME + 15000);
|
||||
|
||||
let labelInterval;
|
||||
if (is_labels_active) {
|
||||
labelInterval = setInterval(() => {
|
||||
setCurrentLabelIndex((prevIndex) => (prevIndex + 1) % labels.length);
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
return () => {
|
||||
clearTimeout(showTimeout);
|
||||
clearTimeout(labelsTimeout);
|
||||
if (labelInterval) clearInterval(labelInterval);
|
||||
};
|
||||
}, [is_labels_active, labels.length]);
|
||||
|
||||
|
||||
return (
|
||||
<a
|
||||
className={clsx(styles.announcements_button_wrapper, {
|
||||
[styles.is_shown]: is_shown,
|
||||
[styles.is_labels_active]: is_labels_active,
|
||||
})}
|
||||
href="https://docs.google.com/spreadsheets/d/1_L5i-1U6PB1dnaPPTE_5uKMfqOpkLziPyRkiMLi4mqU/edit?usp=sharing"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<button className={styles.announcements_button}>
|
||||
<MegaphoneSvg className={styles.announcements_link_svg} />
|
||||
<p className={styles.announcements_label}>
|
||||
{labels[currentLabelIndex]}
|
||||
</p>
|
||||
</button>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Duplicated
|
||||
const CloseButtonContainer = () => {
|
||||
const { asyncCloseApp } = useWindow();
|
||||
|
||||
return (
|
||||
<button className={styles.close_button_wrapper} onClick={asyncCloseApp}>
|
||||
<div className={styles.close_button}>
|
||||
<XMarkSvg className={styles.x_mark_svg}/>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.announcements_button_wrapper {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease, border 0.3s ease;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
&.is_shown {
|
||||
opacity: 1;
|
||||
}
|
||||
&.is_labels_active {
|
||||
& .announcements_label {
|
||||
display: block;
|
||||
animation: appear .3s ease;
|
||||
}
|
||||
& .announcements_link_svg {
|
||||
color: var(--dark_200_color);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--dark_825_color);
|
||||
& .announcements_label {
|
||||
color: var(--dark_200_color);
|
||||
}
|
||||
& .announcements_link_svg {
|
||||
color: var(--primary_300_color);
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--dark_850_color);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.announcements_button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
|
||||
.announcements_label {
|
||||
font-size: 12px;
|
||||
color: var(--dark_400_color);
|
||||
display: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.announcements_link_svg {
|
||||
width: 20px;
|
||||
color: var(--dark_600_color);
|
||||
}
|
||||
|
||||
|
||||
// Duplicated
|
||||
.close_button_wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
width: 68px;
|
||||
aspect-ratio: 1 / 1;
|
||||
&:hover {
|
||||
background-color: var(--error_bc_color);
|
||||
& .x_mark_svg {
|
||||
color: var(--dark_200_color);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--error_bc_active_color);
|
||||
}
|
||||
transition: all 0.1s ease;
|
||||
}
|
||||
|
||||
.close_button {
|
||||
// width: 100%;
|
||||
// height: 100%;
|
||||
}
|
||||
|
||||
.x_mark_svg {
|
||||
width: 24px;
|
||||
transform: rotate(-45deg);
|
||||
color: var(--dark_700_color);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import styles from "./DownloadModelsContainer.module.scss";
|
||||
import vrct_logo_for_dark_mode from "@images/vrct_logo_for_dark_mode.png";
|
||||
import vrct_now_downloading from "@images/VRCT_now_downloading.png";
|
||||
|
||||
import {
|
||||
useTranslation,
|
||||
useTranscription,
|
||||
} from "@logics_configs";
|
||||
|
||||
export const DownloadModelsContainer = () => {
|
||||
const { currentCTranslate2WeightTypeStatus } = useTranslation();
|
||||
const { currentWhisperWeightTypeStatus } = useTranscription();
|
||||
|
||||
const c_translate_2 = currentCTranslate2WeightTypeStatus.data.find(d => d.id === "m2m100_418M-ct2-int8");
|
||||
const whisper = currentWhisperWeightTypeStatus.data.find(d => d.id === "base");
|
||||
|
||||
if (c_translate_2.progress === null && whisper.progress === null) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.progress_container}>
|
||||
<DownloadModelsProgress progress={c_translate_2.progress} type_label="Translation Model"/>
|
||||
<DownloadModelsProgress progress={whisper.progress} type_label="Transcription Model"/>
|
||||
</div>
|
||||
<div className={styles.labels_wrapper}>
|
||||
<img src={vrct_logo_for_dark_mode} className={styles.logo_img}/>
|
||||
<img src={vrct_now_downloading} className={styles.vrct_now_downloading_img}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const DownloadModelsProgress = (props) => {
|
||||
if (props.progress === null) return null;
|
||||
const circular_progress = Math.floor(props.progress / 5) * 5;
|
||||
|
||||
const progress_color = generateGradientColor({
|
||||
value: circular_progress,
|
||||
colorStart: [242, 242, 242], // #f2f2f2
|
||||
colorEnd: [72, 164, 149], // #48a495
|
||||
});
|
||||
|
||||
return(
|
||||
<div className={styles.progress_bar_container}>
|
||||
<div className={styles.progress_bar_wrapper}>
|
||||
<div
|
||||
className={styles.progress_bar}
|
||||
style={{
|
||||
width: `${props.progress}%`,
|
||||
backgroundColor: progress_color,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<p className={styles.progress_label}>{`${props.type_label}: ${Math.round(props.progress)}%`}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const generateGradientColor = ({ value, colorStart, colorEnd }) => {
|
||||
const normalizedValue = Math.max(0, Math.min(100, value)) / 100;
|
||||
const interpolatedColor = colorStart.map((start, i) => {
|
||||
const end = colorEnd[i];
|
||||
return Math.round(start + (end - start) * normalizedValue);
|
||||
});
|
||||
const hexColor = `#${interpolatedColor.map(val => val.toString(16).padStart(2, '0')).join('')}`;
|
||||
return hexColor;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
.container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
background-color: var(--dark_888_color);
|
||||
}
|
||||
|
||||
.progress_container {
|
||||
position: absolute;
|
||||
top: 77%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 90%;
|
||||
gap: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.labels_wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.logo_img {
|
||||
position: absolute;
|
||||
top: 42%;
|
||||
left: 50%;
|
||||
width: 280px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.vrct_now_downloading_img {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 10px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.progress_bar_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.progress_bar_wrapper {
|
||||
background-color: var(--dark_800_color);
|
||||
}
|
||||
|
||||
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
||||
// Duplicated
|
||||
.progress_bar {
|
||||
height: 8px;
|
||||
transition: width 0.3s $progress_ease;
|
||||
}
|
||||
|
||||
.progress_label {
|
||||
text-align: end;
|
||||
font-size: 12px;
|
||||
color: var(--dark_400_color);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import clsx from "clsx";
|
||||
import styles from "./StartUpProgressContainer.module.scss";
|
||||
|
||||
import { useInitProgress } from "@logics_common";
|
||||
import chat_white_square from "@images/chato_white_square.png";
|
||||
import vrct_explanation from "@images/vrchat_chatbox_trasnlator_transcription.png";
|
||||
import vrct_starting_up from "@images/vrct_starting_up.png";
|
||||
|
||||
export const StartUpProgressContainer = () => {
|
||||
const { currentInitProgress } = useInitProgress();
|
||||
|
||||
const progress = currentInitProgress.data;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.progress_bar_wrapper}>
|
||||
{[...Array(4)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={clsx(styles.progress_bar, {
|
||||
[styles.progressed]: index < progress && progress !== 0,
|
||||
})}
|
||||
>
|
||||
{index === 3
|
||||
?
|
||||
<div className={styles.chato_box}>
|
||||
<img src={chat_white_square} className={styles.chato_img}/>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.labels_wrapper}>
|
||||
<img src={vrct_starting_up} className={styles.vrct_starting_up_img}/>
|
||||
<img src={vrct_explanation} className={styles.vrct_explanation_img}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
||||
// Duplicated
|
||||
|
||||
.container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress_bar_wrapper {
|
||||
position: absolute;
|
||||
top: 48%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
gap: 36px;
|
||||
}
|
||||
|
||||
.progress_bar {
|
||||
width: 60px;
|
||||
height: 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress_bar::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 0;
|
||||
height: 100%;
|
||||
background-color: var(--dark_200_color);
|
||||
transition: width 0.3s $progress_ease;
|
||||
}
|
||||
|
||||
.progress_bar.progressed::before {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress_bar:last-child::before {
|
||||
background-color: var(--primary_400_color);
|
||||
}
|
||||
|
||||
|
||||
.chato_box {
|
||||
position: relative;
|
||||
top: 0;
|
||||
transform: translateY(-100%);
|
||||
width: 100%;
|
||||
height: 8rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chato_img {
|
||||
position: absolute;
|
||||
top: 150%;
|
||||
left: 51%;
|
||||
transform: translate(-50%, -50%) rotate(-90deg);
|
||||
width: 2.8rem;
|
||||
transition: all 0.3s $progress_ease 0.2s;
|
||||
}
|
||||
|
||||
.progress_bar.progressed .chato_img {
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) rotate(30deg);
|
||||
animation: infinite-rotation 20s linear infinite 0.5s;
|
||||
}
|
||||
|
||||
@keyframes infinite-rotation {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(30deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(390deg);
|
||||
}
|
||||
}
|
||||
|
||||
.labels_wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.vrct_starting_up_img {
|
||||
position: absolute;
|
||||
top: 68%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100px;
|
||||
// transform: translate(-50%, 50%);
|
||||
}
|
||||
.vrct_explanation_img {
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
right: 10px;
|
||||
width: 280px;
|
||||
// transform: translate(-50%, 50%);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import styles from "./UpdatingComponent.module.scss";
|
||||
import { useI18n } from "@useI18n";
|
||||
import CircularProgress from "@mui/material/CircularProgress";
|
||||
import chat_white_square from "@images/chato_white_square.png";
|
||||
|
||||
export const UpdatingComponent = () => {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.chato_box}>
|
||||
<img src={chat_white_square} className={styles.chato_img}/>
|
||||
</div>
|
||||
<div className={styles.circular_box}>
|
||||
<CircularProgress size="20rem" sx={{
|
||||
color: "var(--primary_300_color)",
|
||||
}}/>
|
||||
</div>
|
||||
<p className={styles.label}>{t("main_page.updating")}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.label {
|
||||
position: absolute;
|
||||
top: 60%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.circular_box {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.chato_box {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chato_img {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 4.8rem;
|
||||
animation: infinite-rotation 20s linear infinite 0.5s;
|
||||
}
|
||||
@keyframes infinite-rotation {
|
||||
from {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
33
src-ui/views/app/others/window_title_bar/WindowTitleBar.jsx
Normal file
33
src-ui/views/app/others/window_title_bar/WindowTitleBar.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useWindow } from "@logics_common";
|
||||
// import clsx from "clsx";
|
||||
import styles from "./WindowTitleBar.module.scss";
|
||||
import XMarkSvg from "@images/cancel.svg?react";
|
||||
import SquareSvg from "@images/square.svg?react";
|
||||
import LineSvg from "@images/line.svg?react";
|
||||
import VrctSvg from "@images/vrct.svg?react";
|
||||
|
||||
export const WindowTitleBar = () => {
|
||||
const { asyncCloseApp, asyncToggleMaximizeApp, asyncMinimizeApp} = useWindow();
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.wrapper} data-tauri-drag-region>
|
||||
<div className={styles.title_wrapper}>
|
||||
<VrctSvg className={styles.title_svg}/>
|
||||
</div>
|
||||
|
||||
<div className={styles.window_control_wrapper}>
|
||||
<div className={styles.minimize_button} onClick={asyncMinimizeApp}>
|
||||
<LineSvg className={styles.line_svg}/>
|
||||
</div>
|
||||
<div className={styles.maximize_button} onClick={asyncToggleMaximizeApp}>
|
||||
<SquareSvg className={styles.square_svg}/>
|
||||
</div>
|
||||
<div className={styles.close_button} onClick={asyncCloseApp}>
|
||||
<XMarkSvg className={styles.x_mark_svg}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
background-color: var(--dark_900_color);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: var(--title_bar_height);
|
||||
margin-top: 0.4rem;
|
||||
}
|
||||
|
||||
.title_wrapper {
|
||||
padding-left: 1rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.title_svg {
|
||||
color: var(--dark_800_color);
|
||||
width: 4rem;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
|
||||
.window_control_wrapper {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.minimize_button, .maximize_button, .close_button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 2.8rem;
|
||||
margin-bottom: 0.4rem;
|
||||
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
.x_mark_svg, .square_svg, .line_svg {
|
||||
color: var(--dark_100_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.minimize_button, .maximize_button {
|
||||
&:hover {
|
||||
background-color: var(--dark_800_color);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--dark_950_color);
|
||||
}
|
||||
}
|
||||
.close_button {
|
||||
&:hover {
|
||||
background-color: var(--error_bc_color);
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--error_bc_active_color);
|
||||
}
|
||||
}
|
||||
|
||||
.x_mark_svg, .square_svg, .line_svg {
|
||||
color: var(--dark_450_color);
|
||||
height: 100%;
|
||||
}
|
||||
.x_mark_svg {
|
||||
width: 1.8rem;
|
||||
}
|
||||
.square_svg {
|
||||
width: 1.2rem;
|
||||
}
|
||||
.line_svg {
|
||||
padding-top: 0.1rem;
|
||||
width: 1.8rem;
|
||||
}
|
||||
Reference in New Issue
Block a user