[Refactor] Move to src-ui/views and src-ui/logics structure.

This commit is contained in:
Sakamoto Shiina
2025-11-05 11:49:48 +09:00
parent 62f7c6d534
commit db820375f1
339 changed files with 19 additions and 19 deletions

View 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>
);
};

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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);
}

View File

@@ -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>
);
};

View File

@@ -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%);
}