Merge branch 'ui_handle_osc_has_disabled' into develop

This commit is contained in:
Sakamoto Shiina
2025-11-18 09:12:09 +09:00
12 changed files with 119 additions and 43 deletions

View File

@@ -83,6 +83,8 @@ config_page:
type_template_auto: "Automatic" type_template_auto: "Automatic"
type_template_low: "{{type_name}} (Lower accuracy, faster processing)" type_template_low: "{{type_name}} (Lower accuracy, faster processing)"
type_template_high: "{{type_name}} (Higher accuracy, slower processing)" type_template_high: "{{type_name}} (Higher accuracy, slower processing)"
warning_labels:
unable_to_use_osc_query: "Due to the OSC IP Address settings, OSC data cannot be received, so this feature is currently unavailable."
side_menu_labels: side_menu_labels:
device: "Device" device: "Device"

View File

@@ -83,6 +83,8 @@ config_page:
type_template_auto: "自動" type_template_auto: "自動"
type_template_low: "{{type_name}} (精度が悪く、処理は早い)" type_template_low: "{{type_name}} (精度が悪く、処理は早い)"
type_template_high: "{{type_name}} (精度が良く、処理は遅い)" type_template_high: "{{type_name}} (精度が良く、処理は遅い)"
warning_labels:
unable_to_use_osc_query: "OSC IP Address の設定によりOSCデータの受信ができないため、現在この機能は使用できません。"
side_menu_labels: side_menu_labels:
device: "デバイス" device: "デバイス"

View File

@@ -12,5 +12,6 @@ export { useUpdateSoftware } from "./useUpdateSoftware";
export { useVolume } from "./useVolume"; export { useVolume } from "./useVolume";
export { useHandleNetworkConnection } from "./useHandleNetworkConnection"; export { useHandleNetworkConnection } from "./useHandleNetworkConnection";
export { useHandleOscQuery } from "./useHandleOscQuery"; export { useHandleOscQuery } from "./useHandleOscQuery";
export { useIsOscAvailable } from "./useIsOscAvailable";
export { useIsVrctAvailable } from "./useIsVrctAvailable"; export { useIsVrctAvailable } from "./useIsVrctAvailable";
export { useFetch } from "./useFetch"; export { useFetch } from "./useFetch";

View File

@@ -1,45 +1,36 @@
import { useI18n } from "@useI18n"; import { useI18n } from "@useI18n";
import { useNotificationStatus } from "@logics_common"; import {
import { useOthers } from "@logics_configs"; useNotificationStatus,
useIsOscAvailable,
} from "@logics_common";
export const useHandleOscQuery = () => { export const useHandleOscQuery = () => {
const { t } = useI18n(); const { t } = useI18n();
const { showNotification_Warning } = useNotificationStatus(); const { showNotification_Warning } = useNotificationStatus();
const { updateEnableVrcMicMuteSync } = useOthers(); const { updateIsOscAvailable } = useIsOscAvailable();
const handleOscQuery = (payload) => { const handleOscQuery = (payload) => {
const is_osc_query_enabled = payload.data; const is_osc_query_enabled = payload.data;
const disabled_functions = payload.disabled_functions; const disabled_functions = payload.disabled_functions;
// OSC無効になるのは、OSC IP Addressが127.0.0.1、localhost以外の場合で発生。
if (is_osc_query_enabled) { if (is_osc_query_enabled) {
updateEnableVrcMicMuteSync(prev => ({ updateIsOscAvailable(true);
...prev.data,
is_available: true,
}));
return;
}
if (!disabled_functions.length) { } else { // OSC自体は無効だが、無効になった機能がない場合。
updateEnableVrcMicMuteSync(prev => ({ updateIsOscAvailable(false);
...prev.data,
is_available: false,
}));
return;
}
const items_label = disabled_functions if (disabled_functions.length > 0) { // 無効になった機能がある場合は通知。
.filter(fn => fn === "vrc_mic_mute_sync") const items_label = disabled_functions
.map(() => `- ${t("config_page.others.vrc_mic_mute_sync.label")}`) .filter(fn => fn === "vrc_mic_mute_sync")
.join("\n"); .map(() => `- ${t("config_page.others.vrc_mic_mute_sync.label")}`)
.join("\n");
updateEnableVrcMicMuteSync({ if (items_label) {
is_enabled: false, const message = `${t("common_warning.unable_to_use_osc_query")}\n${items_label}`;
is_available: false, showNotification_Warning(message, { hide_duration: 10000 });
}); }
}
if (items_label) {
const message = `${t("common_warning.unable_to_use_osc_query")}\n${items_label}`;
showNotification_Warning(message, { hide_duration: 10000 });
} }
}; };

View File

@@ -0,0 +1,10 @@
import { useStore_IsOscAvailable } from "@store";
export const useIsOscAvailable = () => {
const { currentIsOscAvailable, updateIsOscAvailable } = useStore_IsOscAvailable();
return {
currentIsOscAvailable,
updateIsOscAvailable,
};
};

View File

@@ -146,6 +146,7 @@ export const registerMany = (settingsArray = []) => {
// Common // Common
export const { atomInstance: Atom_IsBackendReady, useHook: useStore_IsBackendReady } = createAtomWithHook(false, "IsBackendReady"); export const { atomInstance: Atom_IsBackendReady, useHook: useStore_IsBackendReady } = createAtomWithHook(false, "IsBackendReady");
export const { atomInstance: Atom_IsVrctAvailable, useHook: useStore_IsVrctAvailable } = createAtomWithHook(true, "IsVrctAvailable"); export const { atomInstance: Atom_IsVrctAvailable, useHook: useStore_IsVrctAvailable } = createAtomWithHook(true, "IsVrctAvailable");
export const { atomInstance: Atom_IsOscAvailable, useHook: useStore_IsOscAvailable } = createAtomWithHook(true, "IsOscAvailable");
export const { atomInstance: Atom_ComputeMode, useHook: useStore_ComputeMode } = createAtomWithHook("", "ComputeMode"); export const { atomInstance: Atom_ComputeMode, useHook: useStore_ComputeMode } = createAtomWithHook("", "ComputeMode");
export const { atomInstance: Atom_IsOpenedConfigPage, useHook: useStore_IsOpenedConfigPage } = createAtomWithHook(false, "IsOpenedConfigPage"); export const { atomInstance: Atom_IsOpenedConfigPage, useHook: useStore_IsOpenedConfigPage } = createAtomWithHook(false, "IsOpenedConfigPage");
export const { atomInstance: Atom_MainFunctionsStateMemory, useHook: useStore_MainFunctionsStateMemory } = createAtomWithHook({ export const { atomInstance: Atom_MainFunctionsStateMemory, useHook: useStore_MainFunctionsStateMemory } = createAtomWithHook({

View File

@@ -1,5 +1,6 @@
import styles from "./LabelComponent.module.scss"; import styles from "./LabelComponent.module.scss";
import { _OpenWebpageButton } from "../_atoms/_open_webpage_button/_OpenWebpageButton"; import { _OpenWebpageButton } from "../_atoms/_open_webpage_button/_OpenWebpageButton";
import WarningSvg from "@images/warning.svg?react";
export const LabelComponent = (props) => { export const LabelComponent = (props) => {
return ( return (
@@ -9,6 +10,17 @@ export const LabelComponent = (props) => {
? <p className={styles.desc}>{props.desc}</p> ? <p className={styles.desc}>{props.desc}</p>
: null : null
} }
{props.add_warnings && Array.isArray(props.add_warnings) && props.add_warnings.length > 0 && (
<div className={styles.warnings_section}>
{props.add_warnings.map((w, i) => (
<div className={styles.warning_item} key={i}>
<WarningSvg className={styles.warning_svg} />
<p className={styles.warning_label}>{w.label}</p>
</div>
))}
</div>
)}
{props.webpage_url && <_OpenWebpageButton webpage_url={props.webpage_url} open_webpage_label={props.open_webpage_label} /> } {props.webpage_url && <_OpenWebpageButton webpage_url={props.webpage_url} open_webpage_label={props.open_webpage_label} /> }
</div> </div>
); );

View File

@@ -2,7 +2,8 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 0.4rem; gap: 0.4rem;
// flex-shrink: 0; max-width: 38rem;
overflow-wrap: break-word;
} }
.label { .label {
@@ -16,6 +17,28 @@
font-size: 1.4rem; font-size: 1.4rem;
font-weight: 300; font-weight: 300;
color: var(--dark_500_color); color: var(--dark_500_color);
max-width: 38rem;
overflow-wrap: break-word; overflow-wrap: break-word;
}
.warnings_section {
font-weight: 300;
color: var(--dark_500_color);
}
.warning_item {
display: flex;
align-items: center;
gap: 0.6rem;
}
.warning_label {
font-size: 1.2rem;
color: var(--warning_color);
overflow-wrap: break-word;
}
.warning_svg {
width: 1.4rem;
color: var(--warning_color);
flex-shrink: 0;
} }

View File

@@ -1,7 +1,11 @@
import { useI18n } from "@useI18n"; import { useI18n } from "@useI18n";
import styles from "./Others.module.scss"; import styles from "./Others.module.scss";
import { useOpenFolder } from "@logics_common"; import {
useOpenFolder,
useIsOscAvailable,
} from "@logics_common";
import { import {
useOthers, useOthers,
} from "@logics_configs"; } from "@logics_configs";
@@ -104,18 +108,22 @@ const AutoExportMessageLogsContainer = () => {
export const VrcMicMuteSyncContainer = () => { export const VrcMicMuteSyncContainer = () => {
const { t } = useI18n(); const { t } = useI18n();
const { currentEnableVrcMicMuteSync, toggleEnableVrcMicMuteSync } = useOthers(); const { currentEnableVrcMicMuteSync, toggleEnableVrcMicMuteSync } = useOthers();
const { currentIsOscAvailable } = useIsOscAvailable();
const variable = { const add_warnings = [];
state: currentEnableVrcMicMuteSync.state, if (currentIsOscAvailable.data === false) {
data: currentEnableVrcMicMuteSync.data.is_enabled, add_warnings.push({
}; label: t("config_page.common.warning_labels.unable_to_use_osc_query"),
});
}
return ( return (
<CheckboxContainer <CheckboxContainer
label={t("config_page.others.vrc_mic_mute_sync.label")} label={t("config_page.others.vrc_mic_mute_sync.label")}
desc={t("config_page.others.vrc_mic_mute_sync.desc")} desc={t("config_page.others.vrc_mic_mute_sync.desc")}
variable={variable} variable={currentEnableVrcMicMuteSync}
is_available={currentEnableVrcMicMuteSync.data.is_available} is_available={currentIsOscAvailable.data}
add_warnings={add_warnings}
toggleFunction={toggleEnableVrcMicMuteSync} toggleFunction={toggleEnableVrcMicMuteSync}
/> />
); );

View File

@@ -4,7 +4,11 @@ import RefreshSvg from "@images/refresh.svg?react";
import HelpSvg from "@images/help.svg?react"; import HelpSvg from "@images/help.svg?react";
import { useStore_OpenedQuickSetting } from "@store"; import { useStore_OpenedQuickSetting } from "@store";
import { useSoftwareVersion } from "@logics_common"; import {
useSoftwareVersion,
useIsOscAvailable,
} from "@logics_common";
import { useVr, useOthers } from "@logics_configs"; import { useVr, useOthers } from "@logics_configs";
import { OpenQuickSettingButton } from "./_buttons/OpenQuickSettingButton"; import { OpenQuickSettingButton } from "./_buttons/OpenQuickSettingButton";
@@ -70,6 +74,7 @@ const PluginsQuickSetting = () => {
const OpenVrcMicMuteSyncQuickSetting = () => { const OpenVrcMicMuteSyncQuickSetting = () => {
const { t } = useI18n(); const { t } = useI18n();
const { updateOpenedQuickSetting } = useStore_OpenedQuickSetting(); const { updateOpenedQuickSetting } = useStore_OpenedQuickSetting();
const { currentIsOscAvailable } = useIsOscAvailable();
const { currentEnableVrcMicMuteSync } = useOthers(); const { currentEnableVrcMicMuteSync } = useOthers();
const onClickFunction = () => { const onClickFunction = () => {
@@ -79,7 +84,8 @@ const OpenVrcMicMuteSyncQuickSetting = () => {
return ( return (
<OpenQuickSettingButton <OpenQuickSettingButton
label={t("config_page.others.vrc_mic_mute_sync.label")} label={t("config_page.others.vrc_mic_mute_sync.label")}
variable={currentEnableVrcMicMuteSync.data.is_enabled} variable={currentEnableVrcMicMuteSync.data}
is_available={currentIsOscAvailable.data}
onClickFunction={onClickFunction} onClickFunction={onClickFunction}
/> />
); );

View File

@@ -1,21 +1,31 @@
import { useI18n } from "@useI18n"; import { useI18n } from "@useI18n";
import clsx from "clsx"; import clsx from "clsx";
import styles from "./OpenQuickSettingButton.module.scss"; import styles from "./OpenQuickSettingButton.module.scss";
import WarningSvg from "@images/warning.svg?react";
export const OpenQuickSettingButton = (props) => { export const OpenQuickSettingButton = (props) => {
const { t } = useI18n(); const { t } = useI18n();
const variable = (typeof props.variable === "boolean") ? props.variable : null; const variable = (typeof props.variable === "boolean") ? props.variable : null;
const is_available = (typeof props.is_available === "boolean") ? props.is_available : true;
const getIndicatorLabelClassName = (base_classnames = []) => {
return clsx(...base_classnames, is_available && styles.is_available);
};
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.button_wrapper} onClick={props.onClickFunction}> <div className={styles.button_wrapper} onClick={props.onClickFunction}>
<p className={styles.button_label}>{props.label}</p> <p className={styles.button_label}>{props.label}</p>
{variable !== null && ( {variable !== null && (
props.variable === true ? ( props.variable === true ? (
<p className={clsx(styles.button_indicator_label, styles.enabled)}> <p className={getIndicatorLabelClassName([styles.button_indicator_label, styles.is_enabled])}>
{t("main_page.state_text_enabled")} {t("main_page.state_text_enabled")}
{is_available === false && (
<WarningSvg className={styles.warning_svg} />
)}
</p> </p>
) : ( ) : (
<p className={clsx(styles.button_indicator_label, styles.disabled)}> <p className={getIndicatorLabelClassName([styles.button_indicator_label, styles.is_disabled])}>
{t("main_page.state_text_disabled")} {t("main_page.state_text_disabled")}
</p> </p>
) )

View File

@@ -26,10 +26,20 @@
.button_indicator_label { .button_indicator_label {
font-size: 1rem; font-size: 1rem;
&.disabled { &.is_disabled {
color: var(--dark_600_color); color: var(--dark_600_color);
} }
&.enabled { &.is_enabled {
color: var(--primary_300_color); color: var(--primary_300_color);
&:not(.is_available) {
color: var(--warning_color);
}
} }
}
.warning_svg {
margin-left: 0.4rem;
padding-bottom: 0.1rem;
width: 1.1rem;
color: var(--warning_color);
} }