[Update] Config Page: Supporters: Update-able anytime.
Fetch the supporters data and images from the git repo, https://github.com/ShiinaSakamoto/vrct_supporters, via web.
@@ -1,40 +1,28 @@
|
||||
import styles from "./SupportersContainer.module.scss";
|
||||
import { useState, useEffect } from "react";
|
||||
import vrct_supporters_title from "@images/supporters/vrct_supporters_title.png";
|
||||
import { useEffect } from "react";
|
||||
import { SupportersWrapper } from "./supporters_wrapper/SupportersWrapper";
|
||||
import { clsx } from "clsx";
|
||||
const SHUFFLE_INTERVAL_TIME = 20000;
|
||||
import { useSupporters } from "@logics_configs";
|
||||
import { supporters_images_url } from "@ui_configs";
|
||||
|
||||
export const SupportersContainer = () => {
|
||||
const { currentSupportersData, asyncFetchSupportersData } = useSupporters();
|
||||
|
||||
useEffect(() => {
|
||||
asyncFetchSupportersData();
|
||||
}, []);
|
||||
|
||||
if (currentSupportersData.state === "error")
|
||||
return <div>Failed to retrieve data.</div>;
|
||||
|
||||
if (currentSupportersData.state === "pending" || currentSupportersData.data === null)
|
||||
return <div>Loading...</div>;
|
||||
|
||||
const supporters_settings = currentSupportersData.data.supporters_settings;
|
||||
return (
|
||||
<div className={styles.supporters_container}>
|
||||
<img className={styles.vrct_supporters_title} src={vrct_supporters_title} />
|
||||
<ProgressBar />
|
||||
<SupportersWrapper />
|
||||
<ProgressBar />
|
||||
<img className={styles.vrct_supporters_title} src={`${supporters_images_url}/vrct_supporters_title.png`} />
|
||||
<SupportersWrapper supporters_settings={supporters_settings}/>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -13,14 +13,4 @@
|
||||
font-size: 1.4rem;
|
||||
margin-top: 2rem;
|
||||
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 {
|
||||
useSettingBoxScrollPosition,
|
||||
} from "@logics_configs"
|
||||
useSupporters,
|
||||
} from "@logics_configs";
|
||||
|
||||
import json_data from "./data.json";
|
||||
|
||||
const target_supporting_month = "2025-01";
|
||||
const calc_support_period = ["2024-10", "2024-11", "2024-12", "2025-01"];
|
||||
import { supporters_images_url } from "@ui_configs";
|
||||
|
||||
const SHUFFLE_INTERVAL_TIME = 20000;
|
||||
|
||||
|
||||
|
||||
const and_you_data = {
|
||||
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 = {
|
||||
supporter_cards: import.meta.glob("@images/supporters/supporter_cards/*.png", { eager: true }),
|
||||
chato_expressions: import.meta.glob("@images/supporters/chato_expressions/*.png", { eager: true }),
|
||||
supporters_labels: import.meta.glob("@images/supporters/supporters_labels/*.png", { eager: true }),
|
||||
supporters_icons: import.meta.glob("@images/supporters/supporters_icons/*.png", { eager: true }),
|
||||
supporter_cards: `${supporters_images_url}/supporter_cards/`,
|
||||
chato_expressions: `${supporters_images_url}/chato_expressions/`,
|
||||
supporters_labels: `${supporters_images_url}/supporters_labels/`,
|
||||
supporters_icons: `${supporters_images_url}/supporters_icons/`,
|
||||
};
|
||||
|
||||
const getSupporterCard = (plan_name) => {
|
||||
const card_map = {
|
||||
"もぐもぐ_2000": "mogu_card",
|
||||
"もちもち_1000": "mochi_card",
|
||||
"ふわふわ_500": "fuwa_card",
|
||||
"Basic_300": "basic_card",
|
||||
"mogu_2000": "mogu_card",
|
||||
"mochi_1000": "mochi_card",
|
||||
"fuwa_500": "fuwa_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) =>
|
||||
getImagePath(image_sets.chato_expressions, file_name);
|
||||
const getChatoExpressionsPath = (file_name) => `${image_sets.chato_expressions}${file_name}.png`;
|
||||
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 = ({supporters_settings}) => {
|
||||
const { saveScrollPosition, restoreScrollPosition } = useSettingBoxScrollPosition();
|
||||
const { currentSupportersData } = useSupporters();
|
||||
|
||||
let credit_pending_count = 0;
|
||||
const filtered_data = json_data.filter((supporter) => {
|
||||
if (!supporter.supporter_id) return false;
|
||||
const [json_data, setJsonData] = useState();
|
||||
const [supportersData, setSupportersData] = useState([]);
|
||||
const [chatoExpressions, setChatoExpressions] = useState([]);
|
||||
|
||||
const months = Object.keys(supporter).filter((key) => key.match(/^\d{4}-\d{2}$/));
|
||||
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) => ["ふわふわ_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,
|
||||
]);
|
||||
useEffect(() => {
|
||||
setJsonData(currentSupportersData.data);
|
||||
}, [currentSupportersData.data]);
|
||||
|
||||
|
||||
const [chatoExpressions, setChatoExpressions] = useState(() =>
|
||||
supportersData.map(() =>
|
||||
getChatoExpressionsPath(`chato_expression_${randomIntMinMax(1, chato_ex_count)}`)
|
||||
)
|
||||
);
|
||||
const target_supporting_month = supporters_settings.target_supporting_month;
|
||||
const calc_support_period = supporters_settings.calc_support_period;
|
||||
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) =>
|
||||
key.match(/^\d{4}-\d{2}$/)
|
||||
);
|
||||
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 = [
|
||||
...shuffleArray(grouped_data["もぐもぐ_2000"]),
|
||||
...shuffleArray(grouped_data["もちもち_1000"]),
|
||||
...shuffleArray(grouped_data["ふわふわ_500"]),
|
||||
...shuffleArray(grouped_data["Basic_300"]),
|
||||
...shuffleArray(grouped_data["empty"]),
|
||||
...shuffleArray(newGroupedData["mogu_2000"]),
|
||||
...shuffleArray(newGroupedData["mochi_1000"]),
|
||||
...shuffleArray(newGroupedData["fuwa_500"]),
|
||||
...shuffleArray(newGroupedData["basic_300"]),
|
||||
...shuffleArray(newGroupedData["empty"]),
|
||||
and_you_data,
|
||||
];
|
||||
setSupportersData(newSupportersData);
|
||||
|
||||
setSupportersData(newSupportersData);
|
||||
|
||||
setChatoExpressions(
|
||||
newSupportersData.map(() =>
|
||||
getChatoExpressionsPath(`chato_expression_${randomIntMinMax(1, chato_ex_count)}`)
|
||||
getChatoExpressionsPath(
|
||||
`chato_expression_${randomIntMinMax(1, chato_ex_count)}`
|
||||
)
|
||||
)
|
||||
);
|
||||
setTimeout(() => restoreScrollPosition(), 0);
|
||||
}, [grouped_data]);
|
||||
|
||||
const renderImages = () => {
|
||||
return supportersData.map((item, index) => {
|
||||
const target_plan = item[target_supporting_month];
|
||||
const img_src = getSupporterCard(target_plan);
|
||||
const is_default_icon = item.supporter_icon_id === "";
|
||||
const is_icon_plan = ["もぐもぐ_2000", "もちもち_1000"].includes(target_plan);
|
||||
const is_and_you = item.supporter_id === "and_you";
|
||||
|
||||
const random_delay = `${randomMinMax(0.1, 6).toFixed(1)}s`;
|
||||
|
||||
|
||||
|
||||
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;
|
||||
});
|
||||
};
|
||||
|
||||
}, [json_data]);
|
||||
|
||||
useEffect(() => {
|
||||
recalcAndUpdateSupporters();
|
||||
}, [json_data, recalcAndUpdateSupporters]);
|
||||
|
||||
const shuffleSupporters = useCallback(() => {
|
||||
if (!json_data) return;
|
||||
saveScrollPosition();
|
||||
recalcAndUpdateSupporters();
|
||||
setTimeout(() => restoreScrollPosition(), 0);
|
||||
}, [json_data, recalcAndUpdateSupporters, saveScrollPosition, restoreScrollPosition]);
|
||||
|
||||
useEffect(() => {
|
||||
shuffleSupporters();
|
||||
const interval = setInterval(() => {
|
||||
shuffleSupporters();
|
||||
}, SHUFFLE_INTERVAL_TIME);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
}, [shuffleSupporters]);
|
||||
|
||||
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 = () => {
|
||||
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);
|
||||
|
||||
return (
|
||||
<div className={styles.supporter_period_container}>
|
||||
{Object.entries(period_data).map(([key, item], index) => {
|
||||
if (item === "") return null;
|
||||
const class_name = clsx(styles.period_box, {
|
||||
[styles.mogu_bar]: item === "もぐもぐ_2000",
|
||||
[styles.mochi_bar]: item === "もちもち_1000",
|
||||
[styles.fuwa_bar]: item === "ふわふわ_500",
|
||||
[styles.basic_bar]: item === "Basic_300",
|
||||
[styles.mogu_bar]: item === "mogu_2000",
|
||||
[styles.mochi_bar]: item === "mochi_1000",
|
||||
[styles.fuwa_bar]: item === "fuwa_500",
|
||||
[styles.basic_bar]: item === "basic_300",
|
||||
});
|
||||
|
||||
return <div key={index} className={class_name}></div>
|
||||
return <div key={index} className={class_name}></div>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const extractKeys = (data, keys_to_extract) => {
|
||||
const result = {};
|
||||
for (const key of keys_to_extract) {
|
||||
@@ -268,4 +315,31 @@ const extractKeys = (data, keys_to_extract) => {
|
||||
}
|
||||
}
|
||||
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 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
@@ -218,4 +226,22 @@
|
||||
&.basic_bar {
|
||||
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": ""
|
||||
}
|
||||
]
|
||||
|
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 |
@@ -56,5 +56,9 @@ export { useHotkeys } from "./hotkeys/useHotkeys";
|
||||
export { useOscIpAddress } from "./advanced_settings/useOscIpAddress";
|
||||
export { useOscPort } from "./advanced_settings/useOscPort";
|
||||
|
||||
|
||||
export { useSupporters } from "./supporters/useSupporters";
|
||||
|
||||
|
||||
export { useSettingBoxScrollPosition } from "./useSettingBoxScrollPosition";
|
||||
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_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_PosterShowcaseWorldPageIndex, useHook: useStore_PosterShowcaseWorldPageIndex } = createAtomWithHook(0, "PosterShowcaseWorldPageIndex");
|
||||
@@ -74,4 +74,7 @@ export const whisper_weight_type_status = [
|
||||
{ id: "large-v1", label: "large-v1", 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 },
|
||||
];
|
||||
];
|
||||
|
||||
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";
|
||||