VoD allow 1v1/2v2/3v3 tournament casts (#2625)
Some checks are pending
E2E Tests / e2e (push) Waiting to run
Tests and checks on push / run-checks-and-tests (push) Waiting to run
Updates translation progress / update-translation-progress-issue (push) Waiting to run

This commit is contained in:
Kalle 2025-11-08 13:55:11 +02:00 committed by GitHub
parent 9296319d23
commit bca14d5713
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 233 additions and 86 deletions

View File

@ -169,7 +169,9 @@ function Match({
const { t } = useTranslation(["game-misc", "weapons"]);
const weapon = match.weapons.length === 1 ? match.weapons[0] : null;
const weapons = match.weapons.length === 8 ? match.weapons : null;
const weapons = match.weapons.length > 1 ? match.weapons : null;
const teamSize = weapons ? weapons.length / 2 : 0;
return (
<div className={styles.match}>
@ -198,7 +200,7 @@ function Match({
{weapons ? (
<div className="stack horizontal md">
<div className={styles.matchWeapons}>
{weapons.slice(0, 4).map((weapon, i) => {
{weapons.slice(0, teamSize).map((weapon, i) => {
return (
<WeaponImage
key={i}
@ -211,8 +213,8 @@ function Match({
})}
</div>
<div className={styles.matchWeapons}>
{weapons.slice(4).map((weapon, i) => {
const adjustedI = i + 4;
{weapons.slice(teamSize).map((weapon, i) => {
const adjustedI = i + teamSize;
return (
<WeaponImage
key={i}

View File

@ -69,8 +69,14 @@ export default function NewVodPage() {
: {
video: {
type: "TOURNAMENT",
teamSize: 4,
matches: [
{ mode: "SZ", stageId: 1, startsAt: "", weapons: [] },
{
mode: "SZ",
stageId: 1,
startsAt: "",
weapons: [],
},
],
pov: { type: "USER" } as VodFormFields["video"]["pov"],
},
@ -124,13 +130,57 @@ function FormFields() {
required
/>
{videoType !== "CAST" ? <PovFormField /> : null}
{videoType === "CAST" ? <TeamSizeField /> : <PovFormField />}
<MatchesFormfield videoType={videoType} />
</>
);
}
function TeamSizeField() {
const { t } = useTranslation(["vods"]);
const { setValue } = useFormContext<VodFormFields>();
const matches = useWatch<VodFormFields>({
name: "video.matches",
}) as VodFormFields["video"]["matches"];
return (
<Controller
control={useFormContext<VodFormFields>().control}
name="video.teamSize"
render={({ field: { onChange, value } }) => {
return (
<div>
<Label required htmlFor="teamSize">
{t("vods:forms.title.teamSize")}
</Label>
<select
id="teamSize"
value={value ?? 4}
onChange={(e) => {
const newTeamSize = Number(e.target.value);
onChange(newTeamSize);
if (matches && Array.isArray(matches)) {
matches.forEach((_, idx) => {
setValue(`video.matches.${idx}.weapons`, []);
});
}
}}
required
>
<option value={1}>{t("vods:teamSize.1v1")}</option>
<option value={2}>{t("vods:teamSize.2v2")}</option>
<option value={3}>{t("vods:teamSize.3v3")}</option>
<option value={4}>{t("vods:teamSize.4v4")}</option>
</select>
</div>
);
}}
/>
);
}
function PovFormField() {
const { t } = useTranslation(["vods", "calendar"]);
const methods = useFormContext<VodFormFields>();
@ -244,7 +294,12 @@ function MatchesFormfield({
})}
<AddFieldButton
onClick={() => {
append({ mode: "SZ", stageId: 1, startsAt: "", weapons: [] });
append({
mode: "SZ",
stageId: 1,
startsAt: "",
weapons: [],
});
}}
/>
{rootError && (
@ -304,26 +359,66 @@ function MatchesFieldset({
/>
</div>
<Controller
control={useFormContext<VodFormFields>().control}
name={`video.matches.${idx}.weapons`}
render={({ field: { onChange, value } }) => {
return (
<div>
{videoType === "CAST" ? (
<div>
<Label required>{t("vods:forms.title.weaponsTeamOne")}</Label>
<WeaponsField idx={idx} videoType={videoType} />
</div>
);
}
function WeaponsField({
idx,
videoType,
}: {
idx: number;
videoType: Tables["Video"]["type"];
}) {
const { t } = useTranslation(["vods"]);
const watchedTeamSize = useWatch<VodFormFields>({
name: "video.teamSize",
});
const teamSize = typeof watchedTeamSize === "number" ? watchedTeamSize : 4;
return (
<Controller
control={useFormContext<VodFormFields>().control}
name={`video.matches.${idx}.weapons`}
render={({ field: { onChange, value } }) => {
return (
<div>
{videoType === "CAST" ? (
<div>
<Label required>{t("vods:forms.title.weaponsTeamOne")}</Label>
<div className="stack sm">
{new Array(teamSize).fill(null).map((_, i) => {
return (
<WeaponSelect
key={i}
isRequired
testId={`player-${i}-weapon`}
value={value[i] ?? null}
onChange={(weaponId) => {
const weapons = [...value];
weapons[i] = weaponId;
onChange(weapons);
}}
/>
);
})}
</div>
<div className="mt-4">
<Label required>{t("vods:forms.title.weaponsTeamTwo")}</Label>
<div className="stack sm">
{new Array(4).fill(null).map((_, i) => {
{new Array(teamSize).fill(null).map((_, i) => {
const adjustedI = i + teamSize;
return (
<WeaponSelect
key={i}
isRequired
testId={`player-${i}-weapon`}
value={value[i] ?? null}
testId={`player-${adjustedI}-weapon`}
value={value[adjustedI] ?? null}
onChange={(weaponId) => {
const weapons = [...value];
weapons[i] = weaponId;
weapons[adjustedI] = weaponId;
onChange(weapons);
}}
@ -331,44 +426,20 @@ function MatchesFieldset({
);
})}
</div>
<div className="mt-4">
<Label required>
{t("vods:forms.title.weaponsTeamTwo")}
</Label>
<div className="stack sm">
{new Array(4).fill(null).map((_, i) => {
const adjustedI = i + 4;
return (
<WeaponSelect
key={i}
isRequired
testId={`player-${adjustedI}-weapon`}
value={value[adjustedI] ?? null}
onChange={(weaponId) => {
const weapons = [...value];
weapons[adjustedI] = weaponId;
onChange(weapons);
}}
/>
);
})}
</div>
</div>
</div>
) : (
<WeaponSelect
label={t("vods:forms.title.weapon")}
isRequired
testId={`match-${idx}-weapon`}
value={value[0] ?? null}
onChange={(weaponId) => onChange([weaponId])}
/>
)}
</div>
);
}}
/>
</div>
</div>
) : (
<WeaponSelect
label={t("vods:forms.title.weapon")}
isRequired
testId={`match-${idx}-weapon`}
value={value[0] ?? null}
onChange={(weaponId) => onChange([weaponId])}
/>
)}
</div>
);
}}
/>
);
}

View File

@ -63,24 +63,18 @@ export const videoSchema = z.preprocess(
}),
])
.optional(),
teamSize: z.number().int().min(1).max(4).optional(),
matches: z.array(videoMatchSchema),
})
.refine((data) => {
if (
data.type === "CAST" &&
data.matches.some((match) => match.weapons.length !== 8)
) {
return false;
if (data.type === "CAST") {
const teamSize = data.teamSize ?? 4;
return data.matches.every(
(match) => match.weapons.length === teamSize * 2,
);
}
if (
data.type !== "CAST" &&
data.matches.some((match) => match.weapons.length !== 1)
) {
return false;
}
return true;
return data.matches.every((match) => match.weapons.length === 1);
}),
);

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "Hvordan kan jeg uploade kunst, som jeg har bestilt",
"a9": "Du kan kun uploade kunst, som du selv har lavet. Det skal også være Splatoon-relateret. For bestilte kunstværker, så henvend dig til kunstneren, så De kan uploade det til hjemmesiden. De kan også knytte kunstværket til din profil, så det bliver vist på din profil",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Våben (Hold 1)",
"forms.title.weaponsTeamTwo": "Våben (Hold 2)",
"forms.title.weapon": "Våben",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Tilføj kamp",
"forms.action.deleteMatch": "Slet kamp",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "",
"a9": "",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Waffen (Team 1)",
"forms.title.weaponsTeamTwo": "Waffem (Team 2)",
"forms.title.weapon": "Waffe",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Match hinzufügen",
"forms.action.deleteMatch": "Match löschen",
"noVods": "",

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Weapons (Team 1)",
"forms.title.weaponsTeamTwo": "Weapons (Team 2)",
"forms.title.weapon": "Weapon",
"forms.title.teamSize": "Team Size",
"teamSize.1v1": "1v1",
"teamSize.2v2": "2v2",
"teamSize.3v3": "3v3",
"teamSize.4v4": "4v4",
"forms.action.addMatch": "Add match",
"forms.action.deleteMatch": "Delete match",
"noVods": "No videos found matching this filter. Are we missing something? See the FAQ page on information about how to gain VoD uploading permissions.",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "¿Cómo puedo subir arte que he comisionado?",
"a9": "Solo puedes agregar arte que tú has creado. También tiene que tener relación con Splatoon. Para piezas comisionadas, por favor pide al artista que suba el arte. El artista puede enlazar tu perfil y así se podrá mostrar en tu perfil.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Armas (Equipo 1)",
"forms.title.weaponsTeamTwo": "Armas (Equipo 2)",
"forms.title.weapon": "Arma",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Agregar partido",
"forms.action.deleteMatch": "Borrar partido",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "¿Cómo puedo subir arte que he comisionado?",
"a9": "Solo puedes agregar arte que tú has creado. También tiene que tener relación con Splatoon. Para piezas comisionadas, por favor pide al artista que suba el arte. El artista puede enlazar tu perfil y así se podrá mostrar en tu perfil.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Armas (Equipo 1)",
"forms.title.weaponsTeamTwo": "Armas (Equipo 2)",
"forms.title.weapon": "Arma",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Agregar partido",
"forms.action.deleteMatch": "Borrar partido",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "",
"a9": "",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Armes (Équipe 1)",
"forms.title.weaponsTeamTwo": "Armes (Équipe 2)",
"forms.title.weapon": "Arme",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Ajouter un match",
"forms.action.deleteMatch": "Effacer un match",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "Trouvez votre page de joueur depuis la page Top 500. Postez le lien vers le channel helpdesk sur notre Discord en indiquant que vous souhaitez qu'il soit lié. Note: Le lien n'est pas possible si vous n'avez pas terminé une saison dans le Top 500.",
"q9": "Comment puis-je publier une œuvre que j'ai commandé ?",
"a9": "Vous pouvez seulement publier une œuvre que vous avez réalisé. Il doit également y avoir un rapport avec Splatoon. Veuillez demander à lartiste de la télécharger lui-même sur le site pour une œuvre commandée. Ils sont capables de lier votre profil d'utilisateur à leur œuvre et cela la fera apparaître sur votre profil.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Armes (Équipe 1)",
"forms.title.weaponsTeamTwo": "Armes (Équipe 2)",
"forms.title.weapon": "Arme",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Ajouter un match",
"forms.action.deleteMatch": "Effacer un match",
"noVods": "Aucune vidéo a été trouvé. Est-ce qu'il nous manque quelque chose ? Consultez la page FAQ pour savoir comment obtenir les autorisations de téléchargement VoD.",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "איך אני יכול להעלות ציור שאני ביקשתי?",
"a9": "ניתן להעלות ציור שהכנת בעצמך. הוא צריך להיות קשור ל-Splatoon. בשביל עבודה שביקשת נא לבקש מהאומן להעלות את הציור בכוחות עצמם לאתר. הם יכולים לקשר את הפרופיל שלך מה שיגרום לו להופיע שם.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "נשקים (צוות 1)",
"forms.title.weaponsTeamTwo": "נשקים (צוות 2)",
"forms.title.weapon": "נשקים",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "הוספת קרב",
"forms.action.deleteMatch": "מחיקת קרב",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "Cerca la tua pagina profilo sulla pagina Ricerca Top. Posta il link sull'helpdesk nel nostro Discord dicendo di volerla associata. L'associazione non è possibile se non hai concluso una stagione in Top 500.",
"q9": "Come faccio a caricare un'opera che ho commissionato?",
"a9": "Puoi caricare solo art che hai creato tu stesso/a. Deve anche essere relativa a Splatoon. Per opere commissionate, si prega di chiedere all'artista originale di caricarle. Possono collegare il tuo profilo e fare in modo che esse vengano mostrate lì.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Armi (Squadra 1)",
"forms.title.weaponsTeamTwo": "Armi (Squadra 2)",
"forms.title.weapon": "Arma",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Aggiungi partita",
"forms.action.deleteMatch": "Elimina partita",
"noVods": "Nessun video trovato con questo filtro. Ci stiamo dimenticando di qualcosa? Consulta il FAQ per informazioni su come ottenere il permesso per caricare VoD",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "依頼した作品はどうやってアップロードできますか?",
"a9": "自分で作った作品のみアップロードできます(もちろんスプラトゥーン関連でないといけません)。依頼した作品の場合はその作品の作者からアップロードするように伝えてください。そうすることによって自分のプロファイルに作品が表示されます。",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "ブキ (チーム 1)",
"forms.title.weaponsTeamTwo": "ブキ (チーム 2)",
"forms.title.weapon": "ブキ",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "対戦を追加",
"forms.action.deleteMatch": "対戦を削除",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "",
"a9": "",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "",
"forms.title.weaponsTeamTwo": "",
"forms.title.weapon": "",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "",
"forms.action.deleteMatch": "",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "",
"a9": "",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "",
"forms.title.weaponsTeamTwo": "",
"forms.title.weapon": "",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "",
"forms.action.deleteMatch": "",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "",
"a9": "",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "",
"forms.title.weaponsTeamTwo": "",
"forms.title.weapon": "",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "",
"forms.action.deleteMatch": "",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "Como posso fazer o upload de uma arte da qual fiz a comissão?",
"a9": "Você só pode fazer upload de arte que você mesmo(a) fez. Ela também tem que ser relacionada ao Splatoon. Para trabalhos comissionados, por favor peça o artista para fazer o upload no site. Eles são capazes de conectar a arte ao seu perfil de usuário e isso fará que ela apareça no seu perfil.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Armas (Time 1)",
"forms.title.weaponsTeamTwo": "Armas (Time 2)",
"forms.title.weapon": "Arma",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Adicionar partida",
"forms.action.deleteMatch": "Excluir partida",
"noVods": "",

View File

@ -17,6 +17,6 @@
"a8": "Найдите вашу пользовательскую страницу в секции Топ по режиму. Отправьте сообщение в helpdesk канал на Discord сервере sendou.ink с просьбой привязать эту ссылку к вашему профилю. Обратите внимание, что это можно сделать только если вы закончили сезон в Топ 500.",
"q9": "Как разместить арт, который я заказал?",
"a9": "Вы можете разместить только арт, созданный непосредственно вами. Он также должен быть по тематике Splatoon. Для артов по заказу попросите художника-автора загрузить их на свою страницу. После того, как художник загрузит работу и отметит вас в ней, арт появится на вашем профиле.",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "Оружие (Команда 1)",
"forms.title.weaponsTeamTwo": "Оружие (Команда 2)",
"forms.title.weapon": "Оружие",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "Добавить матч",
"forms.action.deleteMatch": "Удалить матч",
"noVods": "Видео, совпадающие с фильтрами, не найдены. Может, мы что-то упустили? Посмотрите страницу FAQ с информацией о получении прав на загрузку VoD.",

View File

@ -17,6 +17,6 @@
"a8": "",
"q9": "我应该如何上传委托他人创作的艺术作品?",
"a9": "您只能上传您自己创作的艺术作品。并且需要与斯普拉遁相关。对于委托他人创作的作品,请联系作者亲自上传。他们可以将作品链接到您的个人信息,并展示在您的个人信息页。",
"q10:": "",
"q10": "",
"a10": ""
}

View File

@ -20,6 +20,11 @@
"forms.title.weaponsTeamOne": "武器编成队伍1",
"forms.title.weaponsTeamTwo": "武器编成队伍2",
"forms.title.weapon": "武器",
"forms.title.teamSize": "",
"teamSize.1v1": "",
"teamSize.2v2": "",
"teamSize.3v3": "",
"teamSize.4v4": "",
"forms.action.addMatch": "添加比赛",
"forms.action.deleteMatch": "删除比赛",
"noVods": "",