Merge branch 'supporters_page' into develop
@@ -1,6 +1,6 @@
|
|||||||
.scroll_container {
|
.scroll_container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: scroll;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import styles from "./Supporters.module.scss";
|
import styles from "./Supporters.module.scss";
|
||||||
import { SupportUsContainer } from "./support_us_container/SupportUsContainer";
|
import { SupportUsContainer } from "./support_us_container/SupportUsContainer";
|
||||||
import { SupportersContainer } from "./supporters_container/SupportersContainer";
|
import { SupportersContainer } from "./supporters_container/SupportersContainer";
|
||||||
|
import { useSupporters } from "@logics_configs";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export const Supporters = () => {
|
export const Supporters = () => {
|
||||||
|
const { asyncFetchSupportersData } = useSupporters();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
asyncFetchSupportersData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<SupportUsContainer />
|
<SupportUsContainer />
|
||||||
<SupportersContainer />
|
<div className={styles.supportersWrapper}>
|
||||||
|
<SupportersContainer />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -6,3 +6,16 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.supportersWrapper {
|
||||||
|
opacity: 0;
|
||||||
|
animation: fadeIn 0.8s ease-in-out 1.6s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,44 @@
|
|||||||
|
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
||||||
|
|
||||||
|
@keyframes revealTopImg {
|
||||||
|
0% {
|
||||||
|
clip-path: inset(0 50% 0 50%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
clip-path: inset(0 0 0 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounceIn {
|
||||||
|
0% {
|
||||||
|
transform: translateY(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: translateY(-10%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: translateY(10%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes expandWidth {
|
||||||
|
0% {
|
||||||
|
transform: scaleX(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.support_us_container {
|
.support_us_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -6,39 +47,56 @@
|
|||||||
gap: 1.4rem;
|
gap: 1.4rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.support_buttons_wrapper {
|
.support_buttons_wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2rem;
|
gap: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top_img {
|
.top_img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
animation: revealTopImg 0.8s ease forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lines_container {
|
.lines_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
gap: 3.6rem;
|
gap: 3.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line_basic, .line_fuwa, .line_mochi, .line_mogu {
|
.line_basic,
|
||||||
|
.line_fuwa,
|
||||||
|
.line_mochi,
|
||||||
|
.line_mogu {
|
||||||
width: 8.6rem;
|
width: 8.6rem;
|
||||||
height: 0.2rem;
|
height: 0.2rem;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: left center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line_basic {
|
.line_basic {
|
||||||
background-color: var(--dark_800_color);
|
background-color: var(--dark_800_color);
|
||||||
|
animation: expandWidth 1s $progress_ease 0.4s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line_fuwa {
|
.line_fuwa {
|
||||||
background-color: #5788a2;
|
background-color: #5788a2;
|
||||||
|
animation: expandWidth 1s $progress_ease 0.6s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line_mochi {
|
.line_mochi {
|
||||||
background-color: var(--received_300_color);
|
background-color: var(--received_300_color);
|
||||||
|
animation: expandWidth 1s $progress_ease 0.8s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line_mogu {
|
.line_mogu {
|
||||||
background-color: var(--dark_basic_text_color);
|
background-color: var(--dark_basic_text_color);
|
||||||
|
animation: expandWidth 1s $progress_ease 1s forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.support_us_button_wrapper {
|
.support_us_button_wrapper {
|
||||||
@@ -52,8 +110,21 @@
|
|||||||
.support_button {
|
.support_button {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 1.2rem 1.6rem;
|
padding: 1.2rem 1.6rem;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.support_button:nth-child(1) {
|
||||||
|
animation: bounceIn 0.4s ease-out 1.2s forwards;
|
||||||
|
}
|
||||||
|
.support_button:nth-child(2) {
|
||||||
|
animation: bounceIn 0.4s ease-out 1.4s forwards;
|
||||||
|
}
|
||||||
|
.support_button:nth-child(3) {
|
||||||
|
animation: bounceIn 0.4s ease-out 1.6s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.support_img {
|
.support_img {
|
||||||
&.fanbox_logo {
|
&.fanbox_logo {
|
||||||
height: 1.8rem;
|
height: 1.8rem;
|
||||||
@@ -64,8 +135,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.spiral_top::before,
|
.spiral_top::before,
|
||||||
.spiral_top::after,
|
.spiral_top::after,
|
||||||
.spiral_bottom::before,
|
.spiral_bottom::before,
|
||||||
@@ -74,6 +143,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
transition-duration: 0.3s;
|
transition-duration: 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spiral_top::before {
|
.spiral_top::before {
|
||||||
background: var(--dark_800_color);
|
background: var(--dark_800_color);
|
||||||
box-shadow: 0 0 0.4rem 0 var(--dark_800_color);
|
box-shadow: 0 0 0.4rem 0 var(--dark_800_color);
|
||||||
@@ -116,20 +186,17 @@
|
|||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.support_button:hover
|
|
||||||
.spiral_top::before {
|
.support_button:hover .spiral_top::before {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.support_button:hover
|
.support_button:hover .spiral_top::after {
|
||||||
.spiral_top::after {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.support_button:hover
|
.support_button:hover .spiral_bottom::before {
|
||||||
.spiral_bottom::before {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.support_button:hover
|
.support_button:hover .spiral_bottom::after {
|
||||||
.spiral_bottom::after {
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,9 +208,11 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vrct_supporters_title {
|
.vrct_supporters_title {
|
||||||
height: 6rem;
|
height: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vrct_supporters_desc {
|
.vrct_supporters_desc {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|||||||
@@ -1,40 +1,26 @@
|
|||||||
import styles from "./SupportersContainer.module.scss";
|
import styles from "./SupportersContainer.module.scss";
|
||||||
import { useState, useEffect } from "react";
|
|
||||||
import vrct_supporters_title from "@images/supporters/vrct_supporters_title.png";
|
|
||||||
import { SupportersWrapper } from "./supporters_wrapper/SupportersWrapper";
|
import { SupportersWrapper } from "./supporters_wrapper/SupportersWrapper";
|
||||||
import { clsx } from "clsx";
|
import { useSupporters } from "@logics_configs";
|
||||||
const SHUFFLE_INTERVAL_TIME = 20000;
|
import { supporters_images_url } from "@ui_configs";
|
||||||
|
import vrct_supporters_title from "@images/supporters/vrct_supporters_title.png";
|
||||||
|
|
||||||
export const SupportersContainer = () => {
|
export const SupportersContainer = () => {
|
||||||
|
const { currentSupportersData } = useSupporters();
|
||||||
|
|
||||||
|
if (currentSupportersData.state === "error")
|
||||||
|
return <div>Failed to retrieve data.</div>;
|
||||||
|
|
||||||
|
if (currentSupportersData.state === "pending" || currentSupportersData.data === null)
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.supporters_container}>
|
<div className={styles.supporters_container}>
|
||||||
<img className={styles.vrct_supporters_title} src={vrct_supporters_title} />
|
<div className={styles.vrct_supporters_title_wrapper}>
|
||||||
<ProgressBar />
|
<img className={styles.vrct_supporters_title} src={vrct_supporters_title}/>
|
||||||
|
<img className={styles.calc_period} src={`${supporters_images_url}/calc_period_label.png`}/>
|
||||||
|
</div>
|
||||||
<SupportersWrapper />
|
<SupportersWrapper />
|
||||||
<ProgressBar />
|
|
||||||
<p className={styles.vrct_supporters_desc_end}>{`みなさんのおかげで、みしゃ社長は布団で寝ることを許され(in開発室) しいなは喜び庭駆け回っています!!!ふわもちもぐもぐです!ありがとうございます。これからもまだまだ進化するVRCTをどうかよろしくお願いします!\nThanks to everyone, Misha has been granted the privilege of sleeping in a proper bed (in the development room), and Shiina is so happy, running around the yard! Fuwa-mochi-mogu-mogu! Thank you so much! We hope you'll continue to support the ever-evolving VRCT!`}</p>
|
<p className={styles.vrct_supporters_desc_end}>{`みなさんのおかげで、みしゃ社長は布団で寝ることを許され(in開発室) しいなは喜び庭駆け回っています!!!ふわもちもぐもぐです!ありがとうございます。これからもまだまだ進化するVRCTをどうかよろしくお願いします!\nThanks to everyone, Misha has been granted the privilege of sleeping in a proper bed (in the development room), and Shiina is so happy, running around the yard! Fuwa-mochi-mogu-mogu! Thank you so much! We hope you'll continue to support the ever-evolving VRCT!`}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ProgressBar = () => {
|
|
||||||
const [is_active, setIsActive] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsActive(true);
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
setIsActive(false);
|
|
||||||
setTimeout(() => setIsActive(true), 50);
|
|
||||||
}, SHUFFLE_INTERVAL_TIME);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={clsx(styles.progress_bar, {
|
|
||||||
[styles.progress_bar_active]: is_active,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -5,8 +5,19 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vrct_supporters_title_wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
.vrct_supporters_title {
|
.vrct_supporters_title {
|
||||||
height: 6rem;
|
height: 4.2rem;
|
||||||
|
}
|
||||||
|
.calc_period {
|
||||||
|
height: 1.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vrct_supporters_desc_end {
|
.vrct_supporters_desc_end {
|
||||||
@@ -14,13 +25,3 @@
|
|||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
color: var(--dark_300_color);
|
color: var(--dark_300_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress_bar {
|
|
||||||
height: 0.2rem;
|
|
||||||
width: 0%;
|
|
||||||
&.progress_bar_active {
|
|
||||||
transition: width 20000ms linear;
|
|
||||||
background-color: var(--primary_400_color);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,222 +6,162 @@ import { shuffleArray, randomIntMinMax, randomMinMax } from "@utils";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
useSettingBoxScrollPosition,
|
useSettingBoxScrollPosition,
|
||||||
} from "@logics_configs"
|
useSupporters,
|
||||||
|
} from "@logics_configs";
|
||||||
|
|
||||||
import json_data from "./data.json";
|
import { supporters_images_url } from "@ui_configs";
|
||||||
|
|
||||||
const target_supporting_month = "2025-01";
|
|
||||||
const calc_support_period = ["2024-10", "2024-11", "2024-12", "2025-01"];
|
|
||||||
|
|
||||||
const SHUFFLE_INTERVAL_TIME = 20000;
|
const SHUFFLE_INTERVAL_TIME = 20000;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const and_you_data = {
|
const and_you_data = {
|
||||||
supporter_id: "and_you",
|
supporter_id: "and_you",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getImagePath = (images, file_name) => {
|
|
||||||
const image_path = Object.keys(images).find((path) => path.endsWith(`${file_name}.png`));
|
|
||||||
return image_path ? images[image_path]?.default : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const image_sets = {
|
const image_sets = {
|
||||||
supporter_cards: import.meta.glob("@images/supporters/supporter_cards/*.png", { eager: true }),
|
supporter_cards: `${supporters_images_url}/supporter_cards/`,
|
||||||
chato_expressions: import.meta.glob("@images/supporters/chato_expressions/*.png", { eager: true }),
|
chato_expressions: `${supporters_images_url}/chato_expressions/`,
|
||||||
supporters_labels: import.meta.glob("@images/supporters/supporters_labels/*.png", { eager: true }),
|
supporters_labels: `${supporters_images_url}/supporters_labels/`,
|
||||||
supporters_icons: import.meta.glob("@images/supporters/supporters_icons/*.png", { eager: true }),
|
supporters_icons: `${supporters_images_url}/supporters_icons/`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSupporterCard = (plan_name) => {
|
const getSupporterCard = (plan_name) => {
|
||||||
const card_map = {
|
const card_map = {
|
||||||
"もぐもぐ_2000": "mogu_card",
|
"mogu_2000": "mogu_card",
|
||||||
"もちもち_1000": "mochi_card",
|
"mochi_1000": "mochi_card",
|
||||||
"ふわふわ_500": "fuwa_card",
|
"fuwa_500": "fuwa_card",
|
||||||
"Basic_300": "basic_card",
|
"basic_300": "basic_card",
|
||||||
};
|
};
|
||||||
return getImagePath(image_sets.supporter_cards, card_map[plan_name] || "basic_card");
|
if (!card_map[plan_name]) return `${image_sets.supporter_cards}basic_card.png`;
|
||||||
|
|
||||||
|
return `${image_sets.supporter_cards}${card_map[plan_name]}.png`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getChatoExpressionsPath = (file_name) =>
|
const getChatoExpressionsPath = (file_name) => `${image_sets.chato_expressions}${file_name}.png`;
|
||||||
getImagePath(image_sets.chato_expressions, file_name);
|
const getSupportersLabelsPath = (file_name) => `${image_sets.supporters_labels}${file_name}.png`;
|
||||||
|
const getSupportersIconsPath = (file_name) => `${image_sets.supporters_icons}${file_name}.png`;
|
||||||
|
|
||||||
const getSupportersLabelsPath = (file_name) =>
|
|
||||||
getImagePath(image_sets.supporters_labels, file_name);
|
|
||||||
|
|
||||||
const getSupportersIconsPath = (file_name) =>
|
|
||||||
getImagePath(image_sets.supporters_icons, file_name);
|
|
||||||
|
|
||||||
const chato_ex_count = Object.keys(image_sets.chato_expressions).length;
|
|
||||||
|
|
||||||
export const SupportersWrapper = () => {
|
export const SupportersWrapper = () => {
|
||||||
const { saveScrollPosition, restoreScrollPosition } = useSettingBoxScrollPosition();
|
const { saveScrollPosition, restoreScrollPosition } = useSettingBoxScrollPosition();
|
||||||
|
const { currentSupportersData } = useSupporters();
|
||||||
|
|
||||||
let credit_pending_count = 0;
|
const [json_data, setJsonData] = useState();
|
||||||
const filtered_data = json_data.filter((supporter) => {
|
const [supportersData, setSupportersData] = useState([]);
|
||||||
if (!supporter.supporter_id) return false;
|
const [chatoExpressions, setChatoExpressions] = useState([]);
|
||||||
|
|
||||||
const months = Object.keys(supporter).filter((key) => key.match(/^\d{4}-\d{2}$/));
|
useEffect(() => {
|
||||||
const has_valid_month = months.some((month) => supporter[month]);
|
setJsonData(currentSupportersData.data);
|
||||||
if (!has_valid_month) return false;
|
}, [currentSupportersData.data]);
|
||||||
|
|
||||||
const basic_300_months = months.filter((month) => supporter[month] === "Basic_300");
|
|
||||||
const has_special_plan = months.some((month) => ["ふわふわ_500", "もちもち_1000", "もぐもぐ_2000"].includes(supporter[month]));
|
|
||||||
|
|
||||||
if (basic_300_months.length === 1 && !has_special_plan) {
|
|
||||||
credit_pending_count++;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const grouped_data = {
|
|
||||||
もぐもぐ_2000: [],
|
|
||||||
もちもち_1000: [],
|
|
||||||
ふわふわ_500: [],
|
|
||||||
Basic_300: [],
|
|
||||||
empty: [],
|
|
||||||
and_you: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
filtered_data.forEach((supporter) => {
|
|
||||||
const value = supporter[target_supporting_month] || "empty";
|
|
||||||
if (grouped_data[value]) {
|
|
||||||
grouped_data[value].push(supporter);
|
|
||||||
} else {
|
|
||||||
grouped_data["empty"].push(supporter);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [supportersData, setSupportersData] = useState(() => [
|
|
||||||
...grouped_data["もぐもぐ_2000"],
|
|
||||||
...grouped_data["もちもち_1000"],
|
|
||||||
...grouped_data["ふわふわ_500"],
|
|
||||||
...grouped_data["Basic_300"],
|
|
||||||
...grouped_data["empty"],
|
|
||||||
and_you_data,
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
const [chatoExpressions, setChatoExpressions] = useState(() =>
|
const supporters_settings = currentSupportersData.data.supporters_settings;
|
||||||
supportersData.map(() =>
|
|
||||||
getChatoExpressionsPath(`chato_expression_${randomIntMinMax(1, chato_ex_count)}`)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const calc_support_period = supporters_settings.calc_support_period;
|
||||||
|
const target_supporting_month = calc_support_period.at(-1);
|
||||||
|
const chato_ex_count = supporters_settings.chato_ex_count;
|
||||||
|
const last_updated_local_date = new Date(supporters_settings.last_updated_utc_date)?.toString();
|
||||||
|
|
||||||
|
const recalcAndUpdateSupporters = useCallback(() => {
|
||||||
|
if (!json_data) return;
|
||||||
|
|
||||||
|
let credit_pending_count = 0;
|
||||||
|
const newGroupedData = {
|
||||||
|
"mogu_2000": [],
|
||||||
|
"mochi_1000": [],
|
||||||
|
"fuwa_500": [],
|
||||||
|
"basic_300": [],
|
||||||
|
"empty": [],
|
||||||
|
"and_you": [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const filtered_data = json_data.supporters_data.filter((supporter) => {
|
||||||
|
if (!supporter.supporter_id) return false;
|
||||||
|
|
||||||
|
const months = Object.keys(supporter).filter((key) => calc_support_period.includes(key));
|
||||||
|
const has_valid_month = months.some((month) => supporter[month]);
|
||||||
|
if (!has_valid_month) return false;
|
||||||
|
|
||||||
|
const basic_300_months = months.filter(
|
||||||
|
(month) => supporter[month] === "basic_300"
|
||||||
|
);
|
||||||
|
const has_special_plan = months.some((month) =>
|
||||||
|
["fuwa_500", "mochi_1000", "mogu_2000"].includes(supporter[month])
|
||||||
|
);
|
||||||
|
|
||||||
|
if (basic_300_months.length === 1 && !has_special_plan) {
|
||||||
|
credit_pending_count++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
filtered_data.forEach((supporter) => {
|
||||||
|
const value = supporter[target_supporting_month] || "empty";
|
||||||
|
if (newGroupedData[value]) {
|
||||||
|
newGroupedData[value].push(supporter);
|
||||||
|
} else {
|
||||||
|
newGroupedData["empty"].push(supporter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const shuffleSupporters = useCallback(() => {
|
|
||||||
saveScrollPosition();
|
|
||||||
const newSupportersData = [
|
const newSupportersData = [
|
||||||
...shuffleArray(grouped_data["もぐもぐ_2000"]),
|
...shuffleArray(newGroupedData["mogu_2000"]),
|
||||||
...shuffleArray(grouped_data["もちもち_1000"]),
|
...shuffleArray(newGroupedData["mochi_1000"]),
|
||||||
...shuffleArray(grouped_data["ふわふわ_500"]),
|
...shuffleArray(newGroupedData["fuwa_500"]),
|
||||||
...shuffleArray(grouped_data["Basic_300"]),
|
...shuffleArray(newGroupedData["basic_300"]),
|
||||||
...shuffleArray(grouped_data["empty"]),
|
...shuffleArray(newGroupedData["empty"]),
|
||||||
and_you_data,
|
and_you_data,
|
||||||
];
|
];
|
||||||
setSupportersData(newSupportersData);
|
|
||||||
|
|
||||||
|
setSupportersData(newSupportersData);
|
||||||
|
|
||||||
setChatoExpressions(
|
setChatoExpressions(
|
||||||
newSupportersData.map(() =>
|
newSupportersData.map(() =>
|
||||||
getChatoExpressionsPath(`chato_expression_${randomIntMinMax(1, chato_ex_count)}`)
|
getChatoExpressionsPath(
|
||||||
|
`chato_expression_${randomIntMinMax(1, chato_ex_count)}`
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
setTimeout(() => restoreScrollPosition(), 0);
|
}, [json_data]);
|
||||||
}, [grouped_data]);
|
|
||||||
|
useEffect(() => {
|
||||||
const renderImages = () => {
|
recalcAndUpdateSupporters();
|
||||||
return supportersData.map((item, index) => {
|
}, [json_data, recalcAndUpdateSupporters]);
|
||||||
const target_plan = item[target_supporting_month];
|
|
||||||
const img_src = getSupporterCard(target_plan);
|
const shuffleSupporters = useCallback(() => {
|
||||||
const is_default_icon = item.supporter_icon_id === "";
|
if (!json_data) return;
|
||||||
const is_icon_plan = ["もぐもぐ_2000", "もちもち_1000"].includes(target_plan);
|
saveScrollPosition();
|
||||||
const is_and_you = item.supporter_id === "and_you";
|
recalcAndUpdateSupporters();
|
||||||
|
setTimeout(() => restoreScrollPosition(), 0);
|
||||||
const random_delay = `${randomMinMax(0.1, 6).toFixed(1)}s`;
|
}, [json_data, recalcAndUpdateSupporters, saveScrollPosition, restoreScrollPosition]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const file_name = is_and_you ? "and_you" : `supporter_${item.supporter_id}`;
|
|
||||||
const label_img_src = getSupportersLabelsPath(file_name);
|
|
||||||
const icon_img_src = getSupportersIconsPath(`supporter_icon_${item.supporter_icon_id}`);
|
|
||||||
|
|
||||||
const supporter_label_component_classname = clsx(styles.supporter_label_component, {
|
|
||||||
[styles.is_icon_plan]: is_icon_plan,
|
|
||||||
});
|
|
||||||
|
|
||||||
const supporterLabelComponent = () => (
|
|
||||||
<div className={supporter_label_component_classname}>
|
|
||||||
{is_icon_plan && (
|
|
||||||
<div className={styles.supporter_icon_wrapper}>
|
|
||||||
{is_default_icon ? (
|
|
||||||
<img
|
|
||||||
className={styles.default_chato_expression_image}
|
|
||||||
src={chatoExpressions[index]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img className={styles.supporter_icon} src={icon_img_src} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<img className={styles.supporter_label_image} src={label_img_src} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const supporter_image_wrapper_classname = clsx(styles.supporter_image_wrapper, {
|
|
||||||
[styles.mogu_image]: target_plan === "もぐもぐ_2000",
|
|
||||||
});
|
|
||||||
|
|
||||||
return is_and_you ? (
|
|
||||||
<a href="#support_us_container" key={item.supporter_id}>
|
|
||||||
<div className={styles.supporter_image_container}>
|
|
||||||
<div
|
|
||||||
className={supporter_image_wrapper_classname}
|
|
||||||
style={{ "--delay": random_delay }}
|
|
||||||
>
|
|
||||||
<img className={styles.supporter_image} src={img_src} />
|
|
||||||
{supporterLabelComponent()}
|
|
||||||
<AndYouIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</a>
|
|
||||||
): img_src ? (
|
|
||||||
<div key={item.supporter_id} className={styles.supporter_image_container}>
|
|
||||||
<div
|
|
||||||
className={supporter_image_wrapper_classname}
|
|
||||||
style={{ "--delay": random_delay }}
|
|
||||||
>
|
|
||||||
<img className={styles.supporter_image} src={img_src} />
|
|
||||||
{supporterLabelComponent()}
|
|
||||||
</div>
|
|
||||||
<SupporterPeriodContainer settings={item}/>
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
shuffleSupporters();
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
shuffleSupporters();
|
shuffleSupporters();
|
||||||
}, SHUFFLE_INTERVAL_TIME);
|
}, SHUFFLE_INTERVAL_TIME);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, [shuffleSupporters]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.supporters_wrapper}>{renderImages()}</div>
|
<div className={styles.container}>
|
||||||
|
<ProgressBar />
|
||||||
|
<div className={styles.supporters_wrapper}>
|
||||||
|
<SupporterCardsComponent
|
||||||
|
supportersData={supportersData}
|
||||||
|
chatoExpressions={chatoExpressions}
|
||||||
|
target_supporting_month={target_supporting_month}
|
||||||
|
calc_support_period={calc_support_period}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className={styles.last_updated_local_date}>{`Last updated date:\n${last_updated_local_date}`}</p>
|
||||||
|
<ProgressBar />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const AndYouIcon = () => {
|
const AndYouIcon = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -237,29 +177,136 @@ const AndYouIcon = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SupporterPeriodContainer = ({settings}) => {
|
const SupporterCardsComponent = ({ supportersData, chatoExpressions, target_supporting_month, calc_support_period }) => {
|
||||||
|
return supportersData.map((item, index) => {
|
||||||
|
const target_plan = item[target_supporting_month];
|
||||||
|
|
||||||
|
const img_src = getSupporterCard(target_plan);
|
||||||
|
|
||||||
|
const is_and_you = item.supporter_id === "and_you";
|
||||||
|
|
||||||
|
const random_delay = `${randomMinMax(0.1, 6).toFixed(1)}s`;
|
||||||
|
|
||||||
|
const supporter_image_wrapper_classname = clsx(
|
||||||
|
styles.supporter_image_wrapper,
|
||||||
|
{
|
||||||
|
[styles.mogu_image]: target_plan === "mogu_2000",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return is_and_you ? (
|
||||||
|
<a href="#support_us_container" key={item.supporter_id}>
|
||||||
|
<div className={styles.supporter_image_container}>
|
||||||
|
<div
|
||||||
|
className={supporter_image_wrapper_classname}
|
||||||
|
style={{ "--delay": random_delay }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className={styles.supporter_image}
|
||||||
|
src={img_src}
|
||||||
|
alt="supporter"
|
||||||
|
/>
|
||||||
|
<SupporterLabelComponent
|
||||||
|
target_plan={target_plan}
|
||||||
|
item={item}
|
||||||
|
chatoExpressions={chatoExpressions}
|
||||||
|
/>
|
||||||
|
<AndYouIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
) : img_src ? (
|
||||||
|
<div key={item.supporter_id} className={styles.supporter_image_container}>
|
||||||
|
<div
|
||||||
|
className={supporter_image_wrapper_classname}
|
||||||
|
style={{ "--delay": random_delay }}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className={styles.supporter_image}
|
||||||
|
src={img_src}
|
||||||
|
alt="supporter"
|
||||||
|
/>
|
||||||
|
<SupporterLabelComponent
|
||||||
|
target_plan={target_plan}
|
||||||
|
item={item}
|
||||||
|
chato_src={chatoExpressions[index]}
|
||||||
|
index={index}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<SupporterPeriodContainer settings={item} calc_support_period={calc_support_period}/>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const SupporterLabelComponent = ({ item, target_plan, chato_src }) => {
|
||||||
|
const is_icon_plan = ["mogu_2000", "mochi_1000"].includes(
|
||||||
|
target_plan
|
||||||
|
);
|
||||||
|
|
||||||
|
const supporter_label_component_classname = clsx(
|
||||||
|
styles.supporter_label_component,
|
||||||
|
{
|
||||||
|
[styles.is_icon_plan]: is_icon_plan,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const is_and_you = item.supporter_id === "and_you";
|
||||||
|
const is_default_icon = item.supporter_icon_id === "";
|
||||||
|
|
||||||
|
const file_name = is_and_you ? "and_you" : `supporter_${item.supporter_id}`;
|
||||||
|
const label_img_src = getSupportersLabelsPath(file_name);
|
||||||
|
const icon_img_src = getSupportersIconsPath(
|
||||||
|
`supporter_icon_${item.supporter_icon_id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={supporter_label_component_classname}>
|
||||||
|
{is_icon_plan && (
|
||||||
|
<div className={styles.supporter_icon_wrapper}>
|
||||||
|
{is_default_icon ? (
|
||||||
|
<img
|
||||||
|
className={styles.default_chato_expression_image}
|
||||||
|
src={chato_src}
|
||||||
|
alt="chato expression"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<img
|
||||||
|
className={styles.supporter_icon}
|
||||||
|
src={icon_img_src}
|
||||||
|
alt="supporter icon"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<img
|
||||||
|
className={styles.supporter_label_image}
|
||||||
|
src={label_img_src}
|
||||||
|
alt="supporter label"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SupporterPeriodContainer = ({ settings, calc_support_period }) => {
|
||||||
const period_data = extractKeys(settings, calc_support_period);
|
const period_data = extractKeys(settings, calc_support_period);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.supporter_period_container}>
|
<div className={styles.supporter_period_container}>
|
||||||
{Object.entries(period_data).map(([key, item], index) => {
|
{Object.entries(period_data).map(([key, item], index) => {
|
||||||
if (item === "") return null;
|
if (item === "") return null;
|
||||||
const class_name = clsx(styles.period_box, {
|
const class_name = clsx(styles.period_box, {
|
||||||
[styles.mogu_bar]: item === "もぐもぐ_2000",
|
[styles.mogu_bar]: item === "mogu_2000",
|
||||||
[styles.mochi_bar]: item === "もちもち_1000",
|
[styles.mochi_bar]: item === "mochi_1000",
|
||||||
[styles.fuwa_bar]: item === "ふわふわ_500",
|
[styles.fuwa_bar]: item === "fuwa_500",
|
||||||
[styles.basic_bar]: item === "Basic_300",
|
[styles.basic_bar]: item === "basic_300",
|
||||||
});
|
});
|
||||||
|
|
||||||
return <div key={index} className={class_name}></div>
|
return <div key={index} className={class_name}></div>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const extractKeys = (data, keys_to_extract) => {
|
const extractKeys = (data, keys_to_extract) => {
|
||||||
const result = {};
|
const result = {};
|
||||||
for (const key of keys_to_extract) {
|
for (const key of keys_to_extract) {
|
||||||
@@ -269,3 +316,30 @@ const extractKeys = (data, keys_to_extract) => {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const ProgressBar = () => {
|
||||||
|
const [is_active, setIsActive] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setIsActive(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setIsActive(false);
|
||||||
|
setTimeout(() => setIsActive(true), 50);
|
||||||
|
}, SHUFFLE_INTERVAL_TIME);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(styles.progress_bar, {
|
||||||
|
[styles.progress_bar_active]: is_active,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.supporters_wrapper {
|
.supporters_wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -219,3 +227,21 @@
|
|||||||
background-color: var(--dark_800_color);
|
background-color: var(--dark_800_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.progress_bar {
|
||||||
|
height: 0.2rem;
|
||||||
|
width: 0%;
|
||||||
|
&.progress_bar_active {
|
||||||
|
transition: width 20000ms linear;
|
||||||
|
background-color: var(--primary_400_color);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.last_updated_local_date {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--dark_800_color);
|
||||||
|
width: 100%;
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"supporter_id": 1,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "Basic_300",
|
|
||||||
"2024-11": "Basic_300",
|
|
||||||
"2024-12": "Basic_300",
|
|
||||||
"2025-01": "Basic_300",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 2,
|
|
||||||
"supporter_icon_id": 8,
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 3,
|
|
||||||
"supporter_icon_id": 4,
|
|
||||||
"2024-10": "もちもち_1000",
|
|
||||||
"2024-11": "もちもち_1000",
|
|
||||||
"2024-12": "もちもち_1000",
|
|
||||||
"2025-01": "もちもち_1000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 4,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 5,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "ふわふわ_500",
|
|
||||||
"2024-11": "ふわふわ_500",
|
|
||||||
"2024-12": "ふわふわ_500",
|
|
||||||
"2025-01": "Basic_300",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 6,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "ふわふわ_500",
|
|
||||||
"2024-11": "ふわふわ_500",
|
|
||||||
"2024-12": "ふわふわ_500",
|
|
||||||
"2025-01": "ふわふわ_500",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 7,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "ふわふわ_500",
|
|
||||||
"2024-11": "ふわふわ_500",
|
|
||||||
"2024-12": "ふわふわ_500",
|
|
||||||
"2025-01": "ふわふわ_500",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 8,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "Basic_300",
|
|
||||||
"2024-11": "Basic_300",
|
|
||||||
"2024-12": "Basic_300",
|
|
||||||
"2025-01": "",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 9,
|
|
||||||
"supporter_icon_id": 6,
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 10,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "Basic_300",
|
|
||||||
"2024-11": "Basic_300",
|
|
||||||
"2024-12": "Basic_300",
|
|
||||||
"2025-01": "Basic_300",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 11,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 12,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 13,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "Basic_300",
|
|
||||||
"2024-11": "Basic_300",
|
|
||||||
"2024-12": "Basic_300",
|
|
||||||
"2025-01": "Basic_300",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 14,
|
|
||||||
"supporter_icon_id": 2,
|
|
||||||
"2024-10": "もちもち_1000",
|
|
||||||
"2024-11": "もちもち_1000",
|
|
||||||
"2024-12": "もちもち_1000",
|
|
||||||
"2025-01": "もちもち_1000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 15,
|
|
||||||
"supporter_icon_id": 5,
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 16,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 17,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "もちもち_1000",
|
|
||||||
"2024-11": "もちもち_1000",
|
|
||||||
"2024-12": "もちもち_1000",
|
|
||||||
"2025-01": "もちもち_1000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 18,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "Basic_300",
|
|
||||||
"2024-11": "Basic_300",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 19,
|
|
||||||
"supporter_icon_id": 1,
|
|
||||||
"2024-10": "もぐもぐ_2000",
|
|
||||||
"2024-11": "もぐもぐ_2000",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 20,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "Basic_300",
|
|
||||||
"2024-11": "Basic_300",
|
|
||||||
"2024-12": "Basic_300",
|
|
||||||
"2025-01": "ふわふわ_500",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 21,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "ふわふわ_500",
|
|
||||||
"2024-12": "ふわふわ_500",
|
|
||||||
"2025-01": "ふわふわ_500",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 22,
|
|
||||||
"supporter_icon_id": 3,
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "もぐもぐ_2000",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 23,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "ふわふわ_500",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 24,
|
|
||||||
"supporter_icon_id": 7,
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 25,
|
|
||||||
"supporter_icon_id": 9,
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "もぐもぐ_2000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 26,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "もちもち_1000",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 27,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "Basic_300",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 28,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "Basic_300",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 29,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": 30,
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"supporter_id": "",
|
|
||||||
"supporter_icon_id": "",
|
|
||||||
"2024-10": "",
|
|
||||||
"2024-11": "",
|
|
||||||
"2024-12": "",
|
|
||||||
"2025-01": "",
|
|
||||||
"2025-02": "",
|
|
||||||
"2025-03": "",
|
|
||||||
"2025-04": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
$progress_ease: cubic-bezier(0, 1, 0.75, 1);
|
||||||
|
// Duplicated
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
@@ -56,5 +56,9 @@ export { useHotkeys } from "./hotkeys/useHotkeys";
|
|||||||
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
|
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
|
||||||
export { useOscPort } from "./advanced_settings/useOscPort";
|
export { useOscPort } from "./advanced_settings/useOscPort";
|
||||||
|
|
||||||
|
|
||||||
|
export { useSupporters } from "./supporters/useSupporters";
|
||||||
|
|
||||||
|
|
||||||
export { useSettingBoxScrollPosition } from "./useSettingBoxScrollPosition";
|
export { useSettingBoxScrollPosition } from "./useSettingBoxScrollPosition";
|
||||||
export { useSoftwareVersion } from "./useSoftwareVersion";
|
export { useSoftwareVersion } from "./useSoftwareVersion";
|
||||||
29
src-ui/logics/configs/supporters/useSupporters.jsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { useStore_SupportersData } from "@store";
|
||||||
|
import { supporters_data_url } from "@ui_configs";
|
||||||
|
export const useSupporters = () => {
|
||||||
|
const { currentSupportersData, updateSupportersData, pendingSupportersData, errorSupportersData } = useStore_SupportersData();
|
||||||
|
|
||||||
|
const asyncFetchSupportersData = async () => {
|
||||||
|
if (currentSupportersData.state === "pending") return;
|
||||||
|
pendingSupportersData();
|
||||||
|
try {
|
||||||
|
const res = await fetch(supporters_data_url);
|
||||||
|
// const res = await fetch(supporters_data_url, { cache: "no-store" });
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error("Network response was not ok");
|
||||||
|
}
|
||||||
|
const data = await res.json();
|
||||||
|
updateSupportersData(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching supporters' data:", error);
|
||||||
|
errorSupportersData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
asyncFetchSupportersData,
|
||||||
|
currentSupportersData,
|
||||||
|
updateSupportersData,
|
||||||
|
pendingSupportersData,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -280,5 +280,7 @@ export const { atomInstance: Atom_OscPort, useHook: useStore_OscPort } = createA
|
|||||||
|
|
||||||
export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_IsOpenedTranslatorSelector } = createAtomWithHook(false, "IsOpenedTranslatorSelector");
|
export const { atomInstance: Atom_IsOpenedTranslatorSelector, useHook: useStore_IsOpenedTranslatorSelector } = createAtomWithHook(false, "IsOpenedTranslatorSelector");
|
||||||
|
|
||||||
|
export const { atomInstance: Atom_SupportersData, useHook: useStore_SupportersData } = createAtomWithHook(null, "SupportersData", {is_state_ok: true});
|
||||||
|
|
||||||
export const { atomInstance: Atom_VrctPosterIndex, useHook: useStore_VrctPosterIndex } = createAtomWithHook(0, "VrctPosterIndex");
|
export const { atomInstance: Atom_VrctPosterIndex, useHook: useStore_VrctPosterIndex } = createAtomWithHook(0, "VrctPosterIndex");
|
||||||
export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex");
|
export const { atomInstance: Atom_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex");
|
||||||
@@ -75,3 +75,6 @@ export const whisper_weight_type_status = [
|
|||||||
{ id: "large-v2", label: "large-v2", is_downloaded: false, progress: null },
|
{ id: "large-v2", label: "large-v2", is_downloaded: false, progress: null },
|
||||||
{ id: "large-v3", label: "large-v3", is_downloaded: false, progress: null },
|
{ id: "large-v3", label: "large-v3", is_downloaded: false, progress: null },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const supporters_data_url = "https://shiinasakamoto.github.io/vrct_supporters/assets/supporters/data.json";
|
||||||
|
export const supporters_images_url = "https://ShiinaSakamoto.github.io/vrct_supporters/assets/supporters";
|
||||||