UPDATE: New data, optimize, new name for trailblazer
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m1s

This commit is contained in:
2026-01-30 17:13:23 +07:00
parent f81fa964a5
commit c278b8bce0
55 changed files with 878 additions and 950 deletions

View File

@@ -77,10 +77,10 @@ export default async function RootLayout({
<div className="min-h-screen w-full">
<Header />
<div className="grid grid-cols-12 w-full">
<div className="hidden sm:block col-span-3 sticky top-0 self-start h-fit">
<div className="hidden sm:block md:col-span-4 lg:col-span-3 sticky top-0 self-start h-fit">
<AvatarBar />
</div>
<div className="col-span-12 sm:col-span-9 lg:col-span-9">
<div className="col-span-12 sm:col-span-8 lg:col-span-9">
<ActionBar />
{children}
</div>

View File

@@ -233,7 +233,7 @@ export default function ActionBar() {
<div className="text-center font-bold text-xl">{" / "}</div>
<ParseText
locale={locale}
text={getNameChar(locale, avatarSelected).toWellFormed()}
text={getNameChar(locale, transI18n, avatarSelected).toWellFormed()}
className={"font-bold text-xl"}
/>
{avatarSelected?.id && (

View File

@@ -5,6 +5,7 @@ import useLocaleStore from '@/stores/localeStore';
import { CharacterBasic } from '@/types';
import ParseText from '../parseText';
import Image from 'next/image';
import { useTranslations } from 'next-intl';
interface CharacterCardProps {
data: CharacterBasic
@@ -12,13 +13,14 @@ interface CharacterCardProps {
export default function CharacterCard({ data }: CharacterCardProps) {
const { locale } = useLocaleStore();
const text = getNameChar(locale, data)
const transI18n = useTranslations("DataPage");
return (
<li
className="z-10 flex flex-col items-center rounded-xl shadow-xl
<li
className="z-10 flex flex-col items-center rounded-xl shadow-xl
bg-linear-to-br from-base-300 via-base-100 to-warning/70
transform transition-transform duration-300 ease-in-out
hover:scale-105 cursor-pointer min-h-42.5 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-85"
hover:scale-105 cursor-pointer min-h-45 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-85"
>
<div
className={`w-full rounded-md bg-linear-to-br ${data.rank === "CombatPowerAvatarRarityType5"
@@ -39,7 +41,7 @@ export default function CharacterCard({ data }: CharacterCardProps) {
<Image
width={32}
height={32}
src={`/icon/${data.damageType.toLowerCase()}.webp`}
className="absolute top-0 left-0 w-6 h-6 rounded-full"
alt={data.damageType.toLowerCase()}
@@ -59,8 +61,15 @@ export default function CharacterCard({ data }: CharacterCardProps) {
<ParseText
locale={locale}
text={text}
className="mt-2 px-1 text-center text-shadow-white font-bold leading-tight text-sm sm:text-base 2xl:text-lg"
text={getNameChar(locale, transI18n, data)}
className="
w-full px-0.5
my-1
text-center font-bold text-shadow-white
leading-tight
wrap-break-word
text-sm sm:text-base 2xl:text-lg
"
/>
</li>

View File

@@ -17,7 +17,7 @@ export default function LightconeCard({ data }: LightconeCardProps) {
return (
<li className="z-10 flex flex-col items-center rounded-md shadow-lg
bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300
hover:scale-105 cursor-pointer min-h-[220px]"
hover:scale-105 cursor-pointer min-h-55"
>
<div
className={`w-full rounded-md bg-linear-to-br ${data.rank === "CombatPowerLightconeRarity5"

View File

@@ -240,7 +240,7 @@ export default function ExtraSettingBar() {
<SelectCustomImage
customSet={listAvatar.filter((it) => extraData?.multi_path?.multi_path_main?.includes(Number(it.id))).map((it) => ({
value: it.id,
label: getNameChar(locale, it),
label: getNameChar(locale, transI18n, it),
imageUrl: `/icon/${it.baseType.toLowerCase()}.webp`
}))}
excludeSet={[]}
@@ -270,7 +270,7 @@ export default function ExtraSettingBar() {
<SelectCustomImage
customSet={listAvatar.filter((it) => extraData?.multi_path?.multi_path_march_7?.includes(Number(it.id))).map((it) => ({
value: it.id,
label: getNameChar(locale, it),
label: getNameChar(locale, transI18n, it),
imageUrl: `/icon/${it.baseType.toLowerCase()}.webp`
}))}
excludeSet={[]}

View File

@@ -219,7 +219,7 @@ export default function CopyImport() {
<SelectCustomImage
customSet={listCopyAvatar.map((avatar) => ({
value: avatar.id.toString(),
label: getNameChar(locale, avatar),
label: getNameChar(locale, transI18n, avatar),
imageUrl: `https://api.hakush.in/hsr/UI/avatarshopicon/${avatar.id}.webp`
}))}
excludeSet={[]}

View File

@@ -1,10 +1,20 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import Select, { SingleValue } from 'react-select'
import Image from 'next/image'
import useLocaleStore from '@/stores/localeStore'
import ParseText from '../parseText'
import { themeColors } from '@/constant/constant'
import dynamic from "next/dynamic"
import type { SingleValue } from "react-select"
import Image from "next/image"
import useLocaleStore from "@/stores/localeStore"
import ParseText from "../parseText"
import { themeColors } from "@/constant/constant"
import type { Props as SelectProps } from "react-select"
import { JSX } from "react"
const Select = dynamic(
() => import("react-select").then(m => m.default),
{ ssr: false }
) as <Option, IsMulti extends boolean = false>(
props: SelectProps<Option, IsMulti>
) => JSX.Element
export type SelectOption = {
value: string

View File

@@ -1,9 +1,19 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import Select, { SingleValue } from 'react-select'
import dynamic from "next/dynamic"
import type { SingleValue } from "react-select"
import { replaceByParam } from '@/helper'
import useLocaleStore from '@/stores/localeStore'
import { themeColors } from '@/constant/constant'
import type { Props as SelectProps } from "react-select"
import { JSX } from "react"
const Select = dynamic(
() => import("react-select").then(m => m.default),
{ ssr: false }
) as <Option, IsMulti extends boolean = false>(
props: SelectProps<Option, IsMulti>
) => JSX.Element
export type SelectOption = {
id: string

View File

@@ -41,14 +41,15 @@ export default function ShowCaseInfo() {
html2canvas(cardRef.current!, {
scale: 2,
backgroundColor: "#000000",
logging: false,
proxy: '/api/proxy/',
logging: true,
useCORS: true,
allowTaint: false,
imageTimeout: 30000,
})
)
.then((canvas: HTMLCanvasElement) => {
const link = document.createElement("a");
link.download = `${getNameChar(locale, avatarSelected)}_showcase.png`;
link.download = `${getNameChar(locale, transI18n, avatarSelected)}_showcase.png`;
link.href = canvas.toDataURL("image/png");
link.click();
})
@@ -56,7 +57,7 @@ export default function ShowCaseInfo() {
console.log(e)
toast.error("Error generating showcase card!");
});
}, [cardRef, avatarSelected, locale]);
}, [avatarSelected, locale, transI18n]);
useEffect(() => {
@@ -605,7 +606,7 @@ export default function ShowCaseInfo() {
<div className="flex h-full flex-col justify-between">
<div>
<div className="flex flex-row items-center justify-between">
<ParseText className="text-3xl" text={getNameChar(locale, avatarSelected || undefined)} locale={locale} />
<ParseText className="text-3xl" text={getNameChar(locale, transI18n, avatarSelected || undefined)} locale={locale} />
</div>
<div className="flex flex-row items-center gap-4 mt-2">
<div className="text-2xl text-[#d8b46e]">Lv. <span className="text-white">{avatarData?.level}</span>/<span className="text-neutral-400">80</span></div>

View File

@@ -2,7 +2,7 @@
import { useTranslations } from "next-intl";
import useAvatarStore from "@/stores/avatarStore";
import { useCallback, useMemo } from "react";
import { useMemo } from "react";
import { traceButtonsInfo, traceLink } from "@/constant/traceConstant";
import useUserDataStore from "@/stores/userDataStore";
import useLocaleStore from "@/stores/localeStore";
@@ -46,27 +46,20 @@ export default function SkillsInfo() {
return avatarSkillTree?.[skillSelected || ""]?.["1"]
}, [avatarSelected, avatarSkillTree, skillSelected])
const getImageSkill = useCallback((icon: string | undefined, status: StatusAddType | undefined, provider: string = "hakushin") => {
const getImageSkill = (icon: string | undefined, status: StatusAddType | undefined) => {
if (!icon) return
let urlPrefix = "https://homdgcat.wiki/images/skillicons/avatar/";
if (provider === "hakushin") {
urlPrefix = "https://api.hakush.in/hsr/UI/skillicons/"
}
const urlPrefix = "https://api.hakush.in/hsr/UI/skillicons/";
if (icon.startsWith("SkillIcon")) {
if (provider === "homdgcat") {
return `${urlPrefix}${avatarSelected?.id}/${icon}`
} else if (provider === "hakushin") {
return `${urlPrefix}${icon.replace(".png", ".webp")}`
}
return `${urlPrefix}${icon.replace(".png", ".webp")}`
} else if (status && mappingStats[status.PropertyType]) {
return mappingStats[status.PropertyType].icon
}
else if (icon.startsWith("Icon")) {
return `https://api.hakush.in/hsr/UI/trace/${icon.replace(".png", ".webp")}`
}
}, [avatarSelected])
}
const getTraceBuffDisplay = useCallback((status: StatusAddType) => {
const getTraceBuffDisplay = (status: StatusAddType) => {
const dataDisplay = mappingStats[status.PropertyType]
if (!dataDisplay) return ""
if (dataDisplay.unit === "%") {
@@ -76,7 +69,7 @@ export default function SkillsInfo() {
return `${status.Value.toFixed(1)}${dataDisplay.unit}`
}
return `${status.Value.toFixed(0)}${dataDisplay.unit}`
}, [])
}
const dataLevelUpSkill = useMemo(() => {
const skillIds: number[] = skillInfo?.LevelUpSkillID || []
@@ -163,9 +156,6 @@ export default function SkillsInfo() {
filter: (theme === "winter" || theme === "cupcake") ? "invert(1)" : "none"
}}
className={`w-full h-full object-cover rounded-xl`}
// onError={(e) => {
// e.currentTarget.style.display = "none"
// }}
/>
{traceButtons.map((btn, index) => {
if (!avatarInfo?.SkillTrees?.[btn.id]) {
@@ -210,10 +200,6 @@ export default function SkillsInfo() {
style={{
filter: (btn.size !== "big" && btn.size !== "memory" && btn.size !== "elation") ? "brightness(0%)" : ""
}}
onError={(e) => {
e.currentTarget.onerror = null;
e.currentTarget.src = getImageSkill(avatarInfo?.SkillTrees?.[btn.id]?.["1"]?.Icon, avatarSkillTree?.[btn.id]?.["1"]?.StatusAddList[0], "homdgcat") || "";
}}
/>
{(btn.size === "big" || btn.size === "memory" || btn.size === "elation") && (
<p className="

View File

@@ -19,7 +19,6 @@ export const ThemeProvider = ({ children }: PropsWithChildren) => {
}
}, [setTheme]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const changeTheme = (nextTheme: string | null) => {
if (nextTheme) {
setTheme(nextTheme);

View File

@@ -1,8 +1,10 @@
import { listCurrentLanguage } from "@/constant/constant";
import { CharacterBasic, EventBasic, LightConeBasic, MonsterBasic } from "@/types";
import { useTranslations } from "next-intl"
type TFunc = ReturnType<typeof useTranslations>
export function getNameChar(locale: string, data: CharacterBasic | undefined): string {
export function getNameChar(locale: string, t: TFunc, data: CharacterBasic | undefined): string {
if (!data) {
return ""
}
@@ -14,10 +16,8 @@ export function getNameChar(locale: string, data: CharacterBasic | undefined): s
if (!text) {
text = data.lang.get("en") ?? "";
}
if (Number(data.id) % 2 === 0 && Number(data.id) > 8000) {
text = `Female ${data.damageType} MC`
} else if (Number(data.id) > 8000) {
text = `Male ${data.damageType} MC`
if (Number(data.id) > 8000) {
text = `${t("trailblazer")} ${t(data?.baseType?.toLowerCase() ?? "")}`;
}
return text
}

4
src/types/gloval.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.css';
declare module '*.scss';
declare module '*.module.css';
declare module '*.module.scss';