mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-31 08:06:51 -05:00
Show friends list LIVE badge only when there is a stream to watch and link to streams
This commit is contained in:
parent
bb374145c1
commit
adce1d2099
|
|
@ -268,6 +268,7 @@
|
|||
.listLinkSubtitleRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s-1-5);
|
||||
width: 100%;
|
||||
color: var(--color-text-high);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ function TimelineHeader({
|
|||
) : null}
|
||||
{isOngoing ? (
|
||||
<span className={styles.headerScoreLive}>
|
||||
{t("q:match.timeline.live")}
|
||||
{t("q:match.timeline.ongoing")}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@ import {
|
|||
SendouMenuItem,
|
||||
SendouMenuSection,
|
||||
} from "~/components/elements/Menu";
|
||||
import { TwitchIcon } from "~/components/icons/Twitch";
|
||||
import { ListButton } from "~/components/SideNav";
|
||||
import {
|
||||
type FriendActivityBadge,
|
||||
type FriendActivityType,
|
||||
isLiveFriendActivity,
|
||||
friendActivityBadge,
|
||||
} from "~/features/friends/friends-constants";
|
||||
import { useDateTimeFormat } from "~/hooks/intl/useDateTimeFormat";
|
||||
import {
|
||||
|
|
@ -23,6 +25,11 @@ import {
|
|||
tournamentSubsPage,
|
||||
} from "~/utils/urls";
|
||||
|
||||
const ACTIVITY_BADGE_TRANSLATION_KEY = {
|
||||
MATCH: "friends:friendsList.inMatch",
|
||||
NEXT: "friends:friendsList.nextMatch",
|
||||
} as const satisfies Record<FriendActivityBadge, string>;
|
||||
|
||||
export function FriendMenu({
|
||||
discordId,
|
||||
discordAvatar,
|
||||
|
|
@ -34,6 +41,7 @@ export function FriendMenu({
|
|||
activityType,
|
||||
matchId,
|
||||
tournamentId,
|
||||
streamUrl,
|
||||
friendshipId,
|
||||
friendshipCreatedAt,
|
||||
onNavigate,
|
||||
|
|
@ -48,6 +56,7 @@ export function FriendMenu({
|
|||
activityType: FriendActivityType | null;
|
||||
matchId: number | null;
|
||||
tournamentId: number | null;
|
||||
streamUrl: string | null;
|
||||
friendshipId?: number;
|
||||
friendshipCreatedAt?: number | null;
|
||||
onNavigate?: () => void;
|
||||
|
|
@ -67,7 +76,7 @@ export function FriendMenu({
|
|||
})
|
||||
: null;
|
||||
|
||||
const isLive = isLiveFriendActivity(activityType);
|
||||
const activityBadge = friendActivityBadge(activityType);
|
||||
const activity = resolveActivity({ activityType, matchId, tournamentId });
|
||||
|
||||
return (
|
||||
|
|
@ -77,8 +86,14 @@ export function FriendMenu({
|
|||
<ListButton
|
||||
user={{ discordId, discordAvatar, customAvatarUrl }}
|
||||
subtitle={subtitle}
|
||||
badge={isLive ? t("friends:friendsList.live") : badge}
|
||||
badgeVariant={isLive ? "warning" : "default"}
|
||||
badge={
|
||||
streamUrl
|
||||
? t("friends:friendsList.live")
|
||||
: activityBadge
|
||||
? t(ACTIVITY_BADGE_TRANSLATION_KEY[activityBadge])
|
||||
: badge
|
||||
}
|
||||
badgeVariant={streamUrl ? "warning" : "default"}
|
||||
>
|
||||
{name}
|
||||
</ListButton>
|
||||
|
|
@ -88,6 +103,17 @@ export function FriendMenu({
|
|||
<SendouMenuItem href={url} icon={<User />} onAction={onNavigate}>
|
||||
{t("friends:friendsList.viewUserPage")}
|
||||
</SendouMenuItem>
|
||||
{streamUrl ? (
|
||||
<SendouMenuItem
|
||||
href={streamUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
icon={<TwitchIcon />}
|
||||
onAction={onNavigate}
|
||||
>
|
||||
{t("friends:friendsList.watchStream")}
|
||||
</SendouMenuItem>
|
||||
) : null}
|
||||
{activity?.type === "join-sendouq" ? (
|
||||
<SendouMenuItem
|
||||
icon={<Swords />}
|
||||
|
|
@ -189,7 +215,7 @@ function resolveActivity(friend: {
|
|||
}),
|
||||
} as const)
|
||||
: null;
|
||||
case "TOURNAMENT_PLAYING":
|
||||
case "TOURNAMENT_WAITING":
|
||||
return friend.tournamentId
|
||||
? ({
|
||||
type: "view-tournament",
|
||||
|
|
|
|||
|
|
@ -8,30 +8,33 @@ export const SENDOUQ_ACTIVITY_LABEL = "SendouQ";
|
|||
export type FriendActivityType =
|
||||
| "SENDOUQ_MATCH"
|
||||
| "TOURNAMENT_MATCH"
|
||||
| "TOURNAMENT_PLAYING"
|
||||
| "TOURNAMENT_WAITING"
|
||||
| "SENDOUQ"
|
||||
| "TOURNAMENT_SUB";
|
||||
|
||||
/**
|
||||
* Whether the activity represents a friend currently playing (in a live match
|
||||
* or otherwise busy in a running tournament) as opposed to looking for members.
|
||||
*/
|
||||
export function isLiveFriendActivity(type: FriendActivityType | null) {
|
||||
return (
|
||||
type === "SENDOUQ_MATCH" ||
|
||||
type === "TOURNAMENT_MATCH" ||
|
||||
type === "TOURNAMENT_PLAYING"
|
||||
);
|
||||
export type FriendActivityBadge = "MATCH" | "NEXT";
|
||||
|
||||
const ACTIVITY_BADGE: Record<FriendActivityType, FriendActivityBadge | null> = {
|
||||
SENDOUQ_MATCH: "MATCH",
|
||||
TOURNAMENT_MATCH: "MATCH",
|
||||
TOURNAMENT_WAITING: "NEXT",
|
||||
SENDOUQ: null,
|
||||
TOURNAMENT_SUB: null,
|
||||
};
|
||||
|
||||
export function friendActivityBadge(type: FriendActivityType | null) {
|
||||
if (!type) return null;
|
||||
|
||||
return ACTIVITY_BADGE[type];
|
||||
}
|
||||
|
||||
export function isInProgressFriendActivity(type: FriendActivityType | null) {
|
||||
return friendActivityBadge(type) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort value used to order friends by how interesting their activity is.
|
||||
* Looking for members ranks highest (others can act on it), then live activity,
|
||||
* then no activity.
|
||||
*/
|
||||
export function friendActivitySortValue(type: FriendActivityType | null) {
|
||||
if (type === "SENDOUQ") return 4;
|
||||
if (type === "TOURNAMENT_SUB") return 3;
|
||||
if (isLiveFriendActivity(type)) return 2;
|
||||
if (isInProgressFriendActivity(type)) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { groupExpiryStatus } from "~/features/sendouq/core/groups";
|
||||
import { SendouQ } from "~/features/sendouq/core/SendouQ.server";
|
||||
import { FULL_GROUP_SIZE } from "~/features/sendouq/q-constants";
|
||||
import { cachedStreams } from "~/features/sendouq-streams/core/streams.server";
|
||||
import { RunningTournaments } from "~/features/tournament-bracket/core/RunningTournaments.server";
|
||||
import type { TournamentTeamMemberProgressStatus } from "~/features/tournament-bracket/core/Tournament";
|
||||
import type {
|
||||
Tournament,
|
||||
TournamentTeamMemberProgressStatus,
|
||||
} from "~/features/tournament-bracket/core/Tournament";
|
||||
import { twitchUrl } from "~/utils/urls";
|
||||
import {
|
||||
type FriendActivityType,
|
||||
SENDOUQ_ACTIVITY_LABEL,
|
||||
|
|
@ -14,6 +19,8 @@ export interface FriendActivity {
|
|||
badge: string | null;
|
||||
matchId: number | null;
|
||||
tournamentId: number | null;
|
||||
/** Set when the friend's current match can be watched, making the activity show up as "LIVE". */
|
||||
streamUrl: string | null;
|
||||
}
|
||||
|
||||
const TOURNAMENT_STATUS_IS_IN_PROGRESS: Record<
|
||||
|
|
@ -25,15 +32,32 @@ const TOURNAMENT_STATUS_IS_IN_PROGRESS: Record<
|
|||
WAITING_FOR_CAST: true,
|
||||
WAITING_FOR_ROUND: true,
|
||||
WAITING_FOR_GROUPS: true,
|
||||
// to counter 2 day tournaments showing LIVE in between
|
||||
// to counter 2 day tournaments showing as in progress in between
|
||||
WAITING_FOR_BRACKET: false,
|
||||
CHECKIN: false,
|
||||
THANKS_FOR_PLAYING: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Twitch account streaming each ongoing SendouQ match, keyed by match id. Resolved
|
||||
* once per request as activity is resolved separately for every friend.
|
||||
*/
|
||||
export async function resolveSendouQMatchStreams() {
|
||||
const streams = await cachedStreams();
|
||||
|
||||
const result = new Map<number, string>();
|
||||
for (const { match, stream } of streams) {
|
||||
if (!stream.twitchUserName || result.has(match.id)) continue;
|
||||
|
||||
result.set(match.id, stream.twitchUserName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves what a friend is currently doing for display in the friends list,
|
||||
* prioritizing in-progress activity (a live SendouQ or tournament match) over
|
||||
* prioritizing in-progress activity (an ongoing SendouQ or tournament match) over
|
||||
* looking-for-members activity.
|
||||
*/
|
||||
export function resolveFriendActivity({
|
||||
|
|
@ -42,22 +66,27 @@ export function resolveFriendActivity({
|
|||
tournamentName,
|
||||
teamMemberCount,
|
||||
tournamentMinTeamSize,
|
||||
sendouQMatchStreams,
|
||||
}: {
|
||||
friendId: number;
|
||||
tournamentId: number | null;
|
||||
tournamentName: string | null;
|
||||
teamMemberCount: number | null;
|
||||
tournamentMinTeamSize: number | null;
|
||||
sendouQMatchStreams: ReadonlyMap<number, string>;
|
||||
}): FriendActivity {
|
||||
const ownGroup = SendouQ.findOwnGroup(friendId);
|
||||
|
||||
if (ownGroup?.matchId) {
|
||||
const twitchAccount = sendouQMatchStreams.get(ownGroup.matchId);
|
||||
|
||||
return {
|
||||
type: "SENDOUQ_MATCH",
|
||||
subtitle: SENDOUQ_ACTIVITY_LABEL,
|
||||
badge: null,
|
||||
matchId: ownGroup.matchId,
|
||||
tournamentId: null,
|
||||
streamUrl: twitchAccount ? twitchUrl(twitchAccount) : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +104,7 @@ export function resolveFriendActivity({
|
|||
badge: `${ownGroup.members.length}/${FULL_GROUP_SIZE}`,
|
||||
matchId: null,
|
||||
tournamentId: null,
|
||||
streamUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +115,7 @@ export function resolveFriendActivity({
|
|||
badge: `${teamMemberCount ?? 1}/${tournamentMinTeamSize ?? FULL_GROUP_SIZE}`,
|
||||
matchId: null,
|
||||
tournamentId,
|
||||
streamUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +125,7 @@ export function resolveFriendActivity({
|
|||
badge: null,
|
||||
matchId: null,
|
||||
tournamentId: null,
|
||||
streamUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -102,14 +134,79 @@ function resolveTournamentActivity(friendId: number): FriendActivity | null {
|
|||
const status = tournament.teamMemberOfProgressStatus({ id: friendId });
|
||||
if (!status || !TOURNAMENT_STATUS_IS_IN_PROGRESS[status.type]) continue;
|
||||
|
||||
const isInMatch = status.type === "MATCH";
|
||||
|
||||
return {
|
||||
type: status.type === "MATCH" ? "TOURNAMENT_MATCH" : "TOURNAMENT_PLAYING",
|
||||
type: isInMatch ? "TOURNAMENT_MATCH" : "TOURNAMENT_WAITING",
|
||||
subtitle: tournament.ctx.name,
|
||||
badge: null,
|
||||
matchId: status.type === "MATCH" ? status.matchId : null,
|
||||
matchId: isInMatch ? status.matchId : null,
|
||||
tournamentId: tournament.ctx.id,
|
||||
streamUrl: isInMatch
|
||||
? tournamentStreamUrl({
|
||||
tournament,
|
||||
friendId,
|
||||
matchId: status.matchId,
|
||||
opponentId: status.opponentId,
|
||||
})
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where the friend's ongoing tournament match can be watched, preferring the view
|
||||
* that shows the friend best: their own stream, then a teammate's stream, then the
|
||||
* official cast of the match and finally an opponent's.
|
||||
*/
|
||||
function tournamentStreamUrl({
|
||||
tournament,
|
||||
friendId,
|
||||
matchId,
|
||||
opponentId,
|
||||
}: {
|
||||
tournament: Tournament;
|
||||
friendId: number;
|
||||
matchId: number;
|
||||
opponentId: number;
|
||||
}) {
|
||||
const streamingParticipantIds = new Set(tournament.streamingParticipantIds);
|
||||
const ownTeamMembers =
|
||||
tournament.teamMemberOfByUser({ id: friendId })?.members ?? [];
|
||||
|
||||
const friendAccount = streamingTwitchAccount(
|
||||
ownTeamMembers.filter((member) => member.userId === friendId),
|
||||
streamingParticipantIds,
|
||||
);
|
||||
if (friendAccount) return twitchUrl(friendAccount);
|
||||
|
||||
const teammateAccount = streamingTwitchAccount(
|
||||
ownTeamMembers.filter((member) => member.userId !== friendId),
|
||||
streamingParticipantIds,
|
||||
);
|
||||
if (teammateAccount) return twitchUrl(teammateAccount);
|
||||
|
||||
const castAccount = tournament.ctx.castedMatchesInfo?.castedMatches.find(
|
||||
(castedMatch) => castedMatch.matchId === matchId,
|
||||
)?.twitchAccount;
|
||||
if (castAccount) return twitchUrl(castAccount);
|
||||
|
||||
const opponentAccount = streamingTwitchAccount(
|
||||
tournament.teamById(opponentId)?.members ?? [],
|
||||
streamingParticipantIds,
|
||||
);
|
||||
|
||||
return opponentAccount ? twitchUrl(opponentAccount) : null;
|
||||
}
|
||||
|
||||
function streamingTwitchAccount(
|
||||
players: Array<{ userId: number; streamTwitch: string | null }>,
|
||||
streamingParticipantIds: ReadonlySet<number>,
|
||||
) {
|
||||
return players.find(
|
||||
(player) =>
|
||||
streamingParticipantIds.has(player.userId) && player.streamTwitch,
|
||||
)?.streamTwitch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,19 +3,27 @@ import { requireUser } from "~/features/auth/core/user.server";
|
|||
import { userPage } from "~/utils/urls";
|
||||
import * as FriendRepository from "../FriendRepository.server";
|
||||
import { friendActivitySortValue } from "../friends-constants";
|
||||
import { resolveFriendActivity } from "../friends-utils.server";
|
||||
import {
|
||||
resolveFriendActivity,
|
||||
resolveSendouQMatchStreams,
|
||||
} from "../friends-utils.server";
|
||||
|
||||
export type FriendsLoaderData = typeof loader;
|
||||
|
||||
export const loader = async () => {
|
||||
const user = requireUser();
|
||||
|
||||
const [friendsWithActivity, pendingRequests, incomingRequests] =
|
||||
await Promise.all([
|
||||
FriendRepository.findByUserIdWithActivity(user.id),
|
||||
FriendRepository.findPendingSentRequests(user.id),
|
||||
FriendRepository.findPendingReceivedRequests(user.id),
|
||||
]);
|
||||
const [
|
||||
friendsWithActivity,
|
||||
pendingRequests,
|
||||
incomingRequests,
|
||||
streamedSendouQMatches,
|
||||
] = await Promise.all([
|
||||
FriendRepository.findByUserIdWithActivity(user.id),
|
||||
FriendRepository.findPendingSentRequests(user.id),
|
||||
FriendRepository.findPendingReceivedRequests(user.id),
|
||||
resolveSendouQMatchStreams(),
|
||||
]);
|
||||
|
||||
const unique = R.uniqueBy(friendsWithActivity, (f) => f.id);
|
||||
|
||||
|
|
@ -29,6 +37,7 @@ export const loader = async () => {
|
|||
tournamentName: friend.tournamentName,
|
||||
teamMemberCount: friend.teamMemberCount,
|
||||
tournamentMinTeamSize: friend.tournamentMinTeamSize,
|
||||
sendouQMatchStreams: streamedSendouQMatches,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -47,6 +56,7 @@ export const loader = async () => {
|
|||
activityType: activity.type,
|
||||
matchId: activity.matchId,
|
||||
tournamentId: activity.tournamentId ?? friend.tournamentId,
|
||||
streamUrl: activity.streamUrl,
|
||||
friendshipCreatedAt: friend.friendshipCreatedAt,
|
||||
};
|
||||
}),
|
||||
|
|
@ -64,6 +74,7 @@ export const loader = async () => {
|
|||
tournamentName: tm.tournamentName,
|
||||
teamMemberCount: tm.teamMemberCount,
|
||||
tournamentMinTeamSize: tm.tournamentMinTeamSize,
|
||||
sendouQMatchStreams: streamedSendouQMatches,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -81,6 +92,7 @@ export const loader = async () => {
|
|||
activityType: activity.type,
|
||||
matchId: activity.matchId,
|
||||
tournamentId: activity.tournamentId ?? tm.tournamentId,
|
||||
streamUrl: activity.streamUrl,
|
||||
};
|
||||
}),
|
||||
[(tm) => friendActivitySortValue(tm.activityType), "desc"],
|
||||
|
|
|
|||
|
|
@ -14,11 +14,12 @@ import {
|
|||
import * as FriendRepository from "~/features/friends/FriendRepository.server";
|
||||
import {
|
||||
type FriendActivityType,
|
||||
isLiveFriendActivity,
|
||||
isInProgressFriendActivity,
|
||||
} from "~/features/friends/friends-constants";
|
||||
import {
|
||||
type FriendActivity,
|
||||
resolveFriendActivity,
|
||||
resolveSendouQMatchStreams,
|
||||
} from "~/features/friends/friends-utils.server";
|
||||
import * as ShowcaseTournaments from "~/features/front-page/core/ShowcaseTournaments.server";
|
||||
import * as LiveStreamRepository from "~/features/live-streams/LiveStreamRepository.server";
|
||||
|
|
@ -60,6 +61,7 @@ export type SidebarFriend = {
|
|||
activityType: FriendActivityType | null;
|
||||
matchId: number | null;
|
||||
tournamentId: number | null;
|
||||
streamUrl: string | null;
|
||||
};
|
||||
|
||||
const MAX_EVENTS_VISIBLE = 5;
|
||||
|
|
@ -86,12 +88,14 @@ export async function resolveSidebarData(userId: number | null) {
|
|||
friendsWithActivity,
|
||||
savedTournaments,
|
||||
incomingFriendRequestIds,
|
||||
streamedSendouQMatches,
|
||||
] = await Promise.all([
|
||||
ShowcaseTournaments.categorizedTournamentsByUserId(userId),
|
||||
ScrimPostRepository.findUserScrims(userId),
|
||||
FriendRepository.findByUserIdWithActivity(userId),
|
||||
SavedCalendarEventRepository.findAllUpcomingByUserId(userId),
|
||||
FriendRepository.findPendingReceivedRequestIds(userId),
|
||||
resolveSendouQMatchStreams(),
|
||||
]);
|
||||
|
||||
const seenTournamentIds = new Set<number>();
|
||||
|
|
@ -119,7 +123,7 @@ export async function resolveSidebarData(userId: number | null) {
|
|||
.sort((a, b) => a.startsAt - b.startsAt)
|
||||
.slice(0, MAX_EVENTS_VISIBLE);
|
||||
|
||||
const friends = resolveFriends(friendsWithActivity);
|
||||
const friends = resolveFriends(friendsWithActivity, streamedSendouQMatches);
|
||||
|
||||
const savedTournamentIds = savedTournaments.map((t) => t.id);
|
||||
|
||||
|
|
@ -277,7 +281,20 @@ type FriendWithActivity = Awaited<
|
|||
ReturnType<typeof FriendRepository.findByUserIdWithActivity>
|
||||
>[number];
|
||||
|
||||
function resolveFriends(friendsWithActivity: FriendWithActivity[]) {
|
||||
function resolveFriends(
|
||||
friendsWithActivity: FriendWithActivity[],
|
||||
streamedSendouQMatches: ReadonlyMap<number, string>,
|
||||
) {
|
||||
const activityForRow = (row: FriendWithActivity) =>
|
||||
resolveFriendActivity({
|
||||
friendId: row.id,
|
||||
tournamentId: row.tournamentId,
|
||||
tournamentName: row.tournamentName,
|
||||
teamMemberCount: row.teamMemberCount,
|
||||
tournamentMinTeamSize: row.tournamentMinTeamSize,
|
||||
sendouQMatchStreams: streamedSendouQMatches,
|
||||
});
|
||||
|
||||
const unique = R.uniqueBy(friendsWithActivity, (f) => f.id);
|
||||
const friendRows = unique.filter((f) => f.friendshipId !== null);
|
||||
const teamMemberRows = unique.filter((f) => f.friendshipId === null);
|
||||
|
|
@ -297,7 +314,7 @@ function resolveFriends(friendsWithActivity: FriendWithActivity[]) {
|
|||
|
||||
const sidebarFriend = rowToSidebarFriend(friend, activity);
|
||||
|
||||
if (isLiveFriendActivity(activity.type)) {
|
||||
if (isInProgressFriendActivity(activity.type)) {
|
||||
activeFriends.push(sidebarFriend);
|
||||
} else if (activity.type === "SENDOUQ") {
|
||||
sendouqFriends.push(sidebarFriend);
|
||||
|
|
@ -360,16 +377,6 @@ function resolveFriends(friendsWithActivity: FriendWithActivity[]) {
|
|||
return result.slice(0, MAX_FRIENDS_VISIBLE);
|
||||
}
|
||||
|
||||
function activityForRow(row: FriendWithActivity): FriendActivity {
|
||||
return resolveFriendActivity({
|
||||
friendId: row.id,
|
||||
tournamentId: row.tournamentId,
|
||||
tournamentName: row.tournamentName,
|
||||
teamMemberCount: row.teamMemberCount,
|
||||
tournamentMinTeamSize: row.tournamentMinTeamSize,
|
||||
});
|
||||
}
|
||||
|
||||
function rowToSidebarFriend(
|
||||
row: FriendWithActivity,
|
||||
activity: FriendActivity | null,
|
||||
|
|
@ -386,6 +393,7 @@ function rowToSidebarFriend(
|
|||
activityType: activity?.type ?? null,
|
||||
matchId: activity?.matchId ?? null,
|
||||
tournamentId: activity?.tournamentId ?? row.tournamentId,
|
||||
streamUrl: activity?.streamUrl ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1049,6 +1049,7 @@ export class Tournament {
|
|||
type: "MATCH",
|
||||
matchId: match.id,
|
||||
opponent: otherTeam.name,
|
||||
opponentId: otherTeam.id,
|
||||
} as const;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "View tournament",
|
||||
"friendsList.viewMatch": "View match",
|
||||
"friendsList.live": "Live",
|
||||
"friendsList.inMatch": "Match",
|
||||
"friendsList.nextMatch": "Next",
|
||||
"friendsList.watchStream": "Watch stream",
|
||||
"friendsList.joinSendouQ": "Join SendouQ",
|
||||
"friendsList.deleteFriend": "Delete friend",
|
||||
"friendsList.deleteConfirm": "Delete {{name}} as a friend?",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "Loss",
|
||||
"match.timeline.out": "Out",
|
||||
"match.timeline.in": "In",
|
||||
"match.timeline.live": "LIVE",
|
||||
"match.timeline.ongoing": "ONGOING",
|
||||
"match.timeline.picked": "Picked",
|
||||
"match.timeline.explainer.picked": "Map picked by this team",
|
||||
"match.timeline.explainer.pick": "Map or mode picked",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "",
|
||||
"friendsList.viewMatch": "",
|
||||
"friendsList.live": "",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "",
|
||||
"friendsList.deleteFriend": "",
|
||||
"friendsList.deleteConfirm": "",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "",
|
||||
"match.timeline.out": "",
|
||||
"match.timeline.in": "",
|
||||
"match.timeline.live": "",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "",
|
||||
"match.timeline.explainer.picked": "",
|
||||
"match.timeline.explainer.pick": "",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
"friendsList.viewTournament": "查看赛事",
|
||||
"friendsList.viewMatch": "查看对局",
|
||||
"friendsList.live": "直播中",
|
||||
"friendsList.inMatch": "",
|
||||
"friendsList.nextMatch": "",
|
||||
"friendsList.watchStream": "",
|
||||
"friendsList.joinSendouQ": "加入 SendouQ",
|
||||
"friendsList.deleteFriend": "删除好友",
|
||||
"friendsList.deleteConfirm": "确定要删除好友 {{name}} 吗?",
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
"match.timeline.loss": "负",
|
||||
"match.timeline.out": "下场",
|
||||
"match.timeline.in": "上场",
|
||||
"match.timeline.live": "直播中",
|
||||
"match.timeline.ongoing": "",
|
||||
"match.timeline.picked": "已选",
|
||||
"match.timeline.explainer.picked": "此场地由该队伍选择",
|
||||
"match.timeline.explainer.pick": "已选场地或模式",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user