This commit is contained in:
Kalle 2026-04-06 16:19:54 +03:00
parent a4f9b8d251
commit eda4f281f6
5 changed files with 139 additions and 121 deletions

View File

@ -65,3 +65,45 @@
.illegalIcon {
color: var(--color-error);
}
.multiBanner {
display: flex;
padding: 0;
overflow: hidden;
background-image: none;
}
.segment {
--slant: 13px;
flex: 1 1 0;
min-width: 0;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-image:
linear-gradient(
to top,
rgba(255, 255, 255, 0),
rgba(255, 255, 255, 0),
rgba(0, 0, 0, 0.6)
),
var(--stage-img);
clip-path: polygon(
var(--slant) 0,
100% 0,
calc(100% - var(--slant)) 100%,
0 100%
);
margin-inline-start: calc(var(--slant) * -1);
&:first-child {
clip-path: polygon(0 0, 100% 0, calc(100% - var(--slant)) 100%, 0 100%);
margin-inline-start: 0;
}
&:last-child {
clip-path: polygon(var(--slant) 0, 100% 0, 100% 100%, 0 100%);
}
}

View File

@ -49,7 +49,25 @@ export function MatchBanner({
);
}
function ScreenNotice({ screenLegal }: Pick<MatchBannerProps, "screenLegal">) {
export function MultiMatchBanner({ stageIds }: { stageIds: StageId[] }) {
return (
<div className={clsx(styles.banner, styles.multiBanner)}>
{stageIds.map((stageId, i) => (
<div
key={`${stageId}-${i}`}
className={styles.segment}
style={
{
"--stage-img": `url(${stageBannerImageUrl(stageId)})`,
} as React.CSSProperties
}
/>
))}
</div>
);
}
function ScreenNotice({ screenLegal }: { screenLegal: boolean }) {
const { t } = useTranslation(["weapons", "q"]);
const imgSize = 18;

View File

@ -31,7 +31,13 @@ function Score({ score }: { score: MatchBannerTopRowProps["score"] }) {
<div>
{score.alpha}-{score.bravo}
</div>
<div className={styles.sub}>Final</div>
<div className={styles.sub}>
{score.isFinal
? "Final"
: score.bestOf
? `Best of ${score.count}`
: `Play all ${score.count}`}
</div>
</div>
);
}

View File

@ -7,6 +7,7 @@ import { MatchActionTab } from "~/components/match-page/MatchActionTab";
import {
MatchBanner,
MatchBannerContainer,
MultiMatchBanner,
} from "~/components/match-page/MatchBanner";
import { MatchBannerBottomRow } from "~/components/match-page/MatchBannerBottomRow";
import { MatchBannerTopRow } from "~/components/match-page/MatchBannerTopRow";
@ -19,7 +20,6 @@ import * as Seasons from "~/features/mmr/core/Seasons";
import { SENDOUQ_BEST_OF } from "~/features/sendouq/q-constants";
import { databaseTimestampToDate } from "~/utils/dates";
import invariant from "~/utils/invariant";
import { logger } from "~/utils/logger";
import type { SendouRouteHandle } from "~/utils/remix.server";
import { SENDOUQ_RULES_PAGE } from "~/utils/urls";
import { action } from "../actions/q.match.$id.server";
@ -81,28 +81,60 @@ function SendouQMatchBanner() {
0,
);
const reportedCount = data.match.mapList.filter(
(map) => map.winnerGroupId,
).length;
const topRow = (
<MatchBannerTopRow
score={{
alpha: countScore(data.match.groupAlpha.id),
bravo: countScore(data.match.groupBravo.id),
isFinal: Boolean(data.match.isLocked),
count: SENDOUQ_BEST_OF,
bestOf: true,
}}
time={{
currentMinutes: 3,
totalMinutes: 1,
}}
/>
);
const currentMap = data.match.mapList.at(reportedCount);
const bottomRow = (
<MatchBannerBottomRow
games={data.match.mapList.map((map) => ({
mode: map.mode,
winner:
map.winnerGroupId === data.match.groupAlpha.id
? "ALPHA"
: map.winnerGroupId === data.match.groupBravo.id
? "BRAVO"
: undefined,
}))}
activeRosters={{
alpha: data.match.groupAlpha.members,
bravo: data.match.groupBravo.members,
}}
/>
);
if (data.match.isLocked) {
const playedStageIds = data.match.mapList
.filter((m) => m.winnerGroupId !== null)
.map((m) => m.stageId);
return (
<MatchBannerContainer>
{topRow}
<MultiMatchBanner stageIds={playedStageIds} />
{bottomRow}
</MatchBannerContainer>
);
}
const currentMap = data.match.currentMap;
invariant(currentMap);
return (
<MatchBannerContainer>
<MatchBannerTopRow
score={{
alpha: countScore(data.match.groupAlpha.id),
bravo: countScore(data.match.groupBravo.id),
isFinal: Boolean(data.match.isLocked),
count: SENDOUQ_BEST_OF,
bestOf: true,
}}
time={{
currentMinutes: 3,
totalMinutes: 1,
}}
/>
{topRow}
<MatchBanner
stageId={currentMap.stageId}
mode={currentMap.mode}
@ -114,26 +146,17 @@ function SendouQMatchBanner() {
5 <Vote />
</div>
</MatchBanner>
<MatchBannerBottomRow
games={data.match.mapList.map((map) => ({
mode: map.mode,
winner:
map.winnerGroupId === data.match.groupAlpha.id
? "ALPHA"
: map.winnerGroupId === data.match.groupBravo.id
? "BRAVO"
: undefined,
}))}
activeRosters={{
alpha: data.match.groupAlpha.members,
bravo: data.match.groupBravo.members,
}}
/>
{bottomRow}
</MatchBannerContainer>
);
}
function SendouQMatchTabs() {
const data = useLoaderData<typeof loader>();
const currentMap = data.match.currentMap;
invariant(currentMap);
return (
<MatchTabs tabs={["join", "rosters", "action"]}>
<MatchJoinTab
@ -151,103 +174,26 @@ function SendouQMatchTabs() {
/>
<MatchRosterTab
minMembersPerTeam={4}
canEditSubbedOut={[true, false]}
onSubbedOutChange={(teamId, subbedOut) => {
logger.info("onSubbedOutChange", { teamId, subbedOut });
}}
canEditSubbedOut={[false, false]}
teams={[
{
team: {
id: 1,
name: "me in japan",
url: "/t/me-in-japan",
},
members: [
{
id: 1,
username: "Sendou",
discordId: "123",
discordAvatar: null,
customUrl: "sendou",
},
{
id: 2,
username: "Lean",
discordId: "456",
discordAvatar: null,
customUrl: null,
},
{
id: 3,
username: "Kiver",
discordId: "789",
discordAvatar: null,
customUrl: null,
},
{
id: 4,
username: "Brian",
discordId: "012",
discordAvatar: null,
customUrl: null,
},
{
id: 9,
username: "Poppy",
discordId: "567",
discordAvatar: null,
customUrl: null,
},
],
subbedOut: [9],
team: data.match.groupAlpha.team,
members: data.match.groupAlpha.members,
},
{
team: {
id: 2,
name: "Question Mark",
url: "/t/question-mark",
},
members: [
{
id: 5,
username: "Naga",
discordId: "345",
discordAvatar: null,
customUrl: null,
},
{
id: 6,
username: "Grey",
discordId: "678",
discordAvatar: null,
customUrl: null,
},
{
id: 7,
username: "Zack",
discordId: "901",
discordAvatar: null,
customUrl: null,
},
{
id: 8,
username: "Lime",
discordId: "234",
discordAvatar: null,
customUrl: null,
},
],
team: data.match.groupBravo.team,
members: data.match.groupBravo.members,
},
]}
/>
<MatchActionTab
teams={[
{ id: 1, name: "Chimera" },
{ id: 2, name: "Koopa Clan" },
{ id: data.match.groupAlpha.id, name: "Group Alpha" },
{ id: data.match.groupBravo.id, name: "Group Bravo" },
]}
ownTeamId={1}
stageId={4}
mode="SZ"
stageId={currentMap.stageId}
mode={currentMap.mode}
withPoints={false}
/>
</MatchTabs>

View File

@ -216,9 +216,15 @@ class SendouQClass {
};
};
const reportedMapsCount = match.mapList.filter(
(map) => map.winnerGroupId,
).length;
const currentMap = match.mapList.at(reportedMapsCount);
return {
...match,
chatCode: isMatchInsider ? match.chatCode : undefined,
currentMap,
groupAlpha: this.#getAddMemberPrivateNoteMapper(notes)(
matchGroupCensorer(match.groupAlpha, isTeamAlphaMember),
),