Merge branch 'ui_notification_add_link' into develop

This commit is contained in:
Sakamoto Shiina
2025-08-02 17:06:37 +09:00
9 changed files with 133 additions and 119 deletions

View File

@@ -37,7 +37,7 @@ export const PosterShowcaseWorldsContents = () => {
);
if (poster.x_post_num !== null) {
return (
<a href={`https://x.com/Shiina_12siy/status/${poster.x_post_num}`} target="_blank" rel="noreferrer" className={class_names} key={index}>
<a href={`https://x.com/Shiina_12siy/status/${poster.x_post_num}`} target="_blank" rel="noreferrer" className={class_names} key={index}>
{content}
</a>
);

View File

@@ -1,10 +1,10 @@
import { useEffect, useRef, useState, useCallback } from "react";
import { useEffect, useRef } from "react";
import { useI18n } from "@useI18n";
import { usePlugins } from "@logics_configs";
import styles from "./Plugins.module.scss";
import { PluginsControlComponent } from "./plugins_control_component/PluginsControlComponent";
import { useNotificationStatus } from "@logics_common";
import ExternalLink from "@images/external_link.svg?react";
import { HomepageLinkButton } from "@common_components";
export const Plugins = () => {
const {
@@ -125,55 +125,4 @@ const PluginDownloadContainer = () => {
})}
</div>
);
};
const HomepageLinkButton = ({ homepage_link, speed = 40 /* px/s */ }) => {
const containerRef = useRef(null);
const textRef = useRef(null);
const [inlineStyle, setInlineStyle] = useState({});
const handleMouseEnter = useCallback(() => {
const container = containerRef.current;
const text = textRef.current;
if (!container || !text) return;
const overflow = text.scrollWidth - container.clientWidth;
if (overflow > 0) {
const duration = overflow / speed;
setInlineStyle({
transform: `translateX(-${overflow}px)`,
transition: `transform ${duration}s linear`,
});
}
}, [speed]);
const handleMouseLeave = useCallback(() => {
setInlineStyle({
transform: 'translateX(0)',
transition: 'transform 0.3s ease-out',
});
}, []);
return (
<div className={styles.open_homepage_button_wrapper}>
<a
className={styles.open_homepage_button}
href={homepage_link}
target="_blank"
rel="noreferrer"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div className={styles.text_container} ref={containerRef}>
<p
className={styles.open_homepage_text}
ref={textRef}
style={inlineStyle}
>
{homepage_link}
</p>
</div>
<ExternalLink className={styles.external_link_svg} />
</a>
</div>
);
};

View File

@@ -56,45 +56,4 @@
// overflow: hidden;
// white-space: nowrap;
// text-overflow: ellipsis;
// }
.open_homepage_button_wrapper {
width: 100%;
}
.open_homepage_button {
padding: 0.6rem 0;
display: flex;
align-items: center;
gap: 1rem;
cursor: pointer;
text-decoration: none;
&:hover .external_link_svg {
color: var(--dark_200_color);
}
&:hover .open_homepage_text {
border-bottom: 0.1rem solid var(--dark_500_color);
}
}
.text_container {
position: relative;
overflow: hidden;
flex: 1; /* テキスト部分をアイコンまで伸ばす */
}
.open_homepage_text {
margin: 0; /* pタグのデフォルトマージン除去 */
font-size: 1.4rem;
color: var(--sent_400_color);
white-space: nowrap;
display: inline-block;
transform: translateX(0);
border-bottom: 0.1rem solid var(--sent_400_color);
}
.external_link_svg {
flex-shrink: 0;
width: 1.6rem;
color: var(--dark_500_color);
}
// }

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, isValidElement } from "react";
import { ToastContainer, toast, cssTransition } from "react-toastify";
import clsx from "clsx";

View File

@@ -0,0 +1,54 @@
import ExternalLink from "@images/external_link.svg?react";
import styles from "./HomepageLinkButton.module.scss";
import { useRef, useState, useCallback } from "react";
export const HomepageLinkButton = ({ homepage_link, speed = 40 /* px/s */ }) => {
const containerRef = useRef(null);
const textRef = useRef(null);
const [inlineStyle, setInlineStyle] = useState({});
const handleMouseEnter = useCallback(() => {
const container = containerRef.current;
const text = textRef.current;
if (!container || !text) return;
const overflow = text.scrollWidth - container.clientWidth;
if (overflow > 0) {
const duration = overflow / speed;
setInlineStyle({
transform: `translateX(-${overflow}px)`,
transition: `transform ${duration}s linear`,
});
}
}, [speed]);
const handleMouseLeave = useCallback(() => {
setInlineStyle({
transform: 'translateX(0)',
transition: 'transform 0.3s ease-out',
});
}, []);
return (
<div className={styles.open_homepage_button_wrapper}>
<a
className={styles.open_homepage_button}
href={homepage_link}
target="_blank"
rel="noreferrer"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<div className={styles.text_container} ref={containerRef}>
<p
className={styles.open_homepage_text}
ref={textRef}
style={inlineStyle}
>
{homepage_link}
</p>
</div>
<ExternalLink className={styles.external_link_svg} />
</a>
</div>
);
};

View File

@@ -0,0 +1,39 @@
.open_homepage_button_wrapper {
width: 100%;
}
.open_homepage_button {
padding: 0.6rem 0;
display: flex;
align-items: center;
gap: 1rem;
cursor: pointer;
text-decoration: none;
&:hover .external_link_svg {
color: var(--dark_200_color);
}
&:hover .open_homepage_text {
border-bottom: 0.1rem solid var(--dark_500_color);
}
}
.text_container {
position: relative;
overflow: hidden;
}
.open_homepage_text {
margin: 0; /* pタグのデフォルトマージン除去 */
font-size: 1.4rem;
color: var(--sent_400_color);
white-space: nowrap;
display: inline-block;
transform: translateX(0);
border-bottom: 0.1rem solid var(--sent_400_color);
}
.external_link_svg {
flex-shrink: 0;
width: 1.6rem;
color: var(--dark_500_color);
}

View File

@@ -1 +1,2 @@
export { Checkbox } from "./checkbox/Checkbox";
export { Checkbox } from "./checkbox/Checkbox";
export { HomepageLinkButton } from "./homepage_link_button/HomepageLinkButton";

View File

@@ -1,21 +0,0 @@
import { useStore_IsVrctAvailable } from "@store";
import { useNotificationStatus } from "@logics_common";
export const useIsVrctAvailable = () => {
const { currentIsVrctAvailable, updateIsVrctAvailable } = useStore_IsVrctAvailable();
const { showNotification_Success, showNotification_Error } = useNotificationStatus();
const handleAiModelsAvailability = (is_ai_models_available) => {
if (is_ai_models_available === false) {
updateIsVrctAvailable(false);
showNotification_Error("AI models have not been detected. Check the network connection and restart VRCT (it will download automatically, normally).", { hide_duration: null });
}
};
return {
currentIsVrctAvailable,
updateIsVrctAvailable,
handleAiModelsAvailability,
};
};

View File

@@ -0,0 +1,33 @@
import { useStore_IsVrctAvailable } from "@store";
import { useNotificationStatus } from "@logics_common";
import { HomepageLinkButton } from "@common_components";
export const useIsVrctAvailable = () => {
const { currentIsVrctAvailable, updateIsVrctAvailable } = useStore_IsVrctAvailable();
const { showNotification_Success, showNotification_Error } = useNotificationStatus();
const handleAiModelsAvailability = (is_ai_models_available) => {
if (is_ai_models_available === false) {
updateIsVrctAvailable(false);
const ErrorComponent = () => {
return (
<div>
<p>AI models have not been detected. Check the network connection and restart VRCT (it will download automatically, normally).</p>
<p>If this error occurs frequently, try the following:</p>
<HomepageLinkButton
homepage_link="https://github.com/misyaguziya/VRCT/wiki/Manual-Installation-of-AI-Model-Weights"
/>
</div>
);
};
showNotification_Error(ErrorComponent, { hide_duration: null });
}
};
return {
currentIsVrctAvailable,
updateIsVrctAvailable,
handleAiModelsAvailability,
};
};