diff --git a/src/lib/misc.ts b/src/lib/misc.ts index 1d847115b7..0cc6f34e18 100644 --- a/src/lib/misc.ts +++ b/src/lib/misc.ts @@ -21,6 +21,7 @@ import { browserHistory } from "ogsHistory"; import * as preferences from "preferences"; import { alert } from "swal_config"; import React from "react"; +import moment from "moment"; export type Timeout = ReturnType; @@ -655,6 +656,19 @@ export function getCurrentGameId() { return null; } +// needed because Game end_time and start_time are only to the nearest second +export function showSecondsResolution(duration: moment.Duration | null): string { + if (!duration) { + return "-"; + } else if (duration < moment.duration(1000)) { + return `${moment.duration(duration).asSeconds().toFixed(2)}s`; + } else if (duration < moment.duration(60000)) { + return `${moment.duration(duration).asSeconds().toFixed(1)}s`; + } else { + return moment.duration(duration).format("d:h:m:s"); + } +} + /* This code is hacked together from https://github.com/simov/slugify/blob/master/slugify.js and https://github.com/dodo/node-slug/blob/master/slug.js diff --git a/src/views/Game/GameLog.styl b/src/views/Game/GameLog.styl new file mode 100644 index 0000000000..9834e49153 --- /dev/null +++ b/src/views/Game/GameLog.styl @@ -0,0 +1,32 @@ +/* + * Copyright (C) Online-Go.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +.GameLog { + td { + padding-right: 1rem; + border-bottom: 1px solid transparent; + themed border-bottom-color shade3 + } + + .timestamp { + width: 12rem; + } + + .field { + margin-right: 1rem; + } +} diff --git a/src/views/Game/GameLog.tsx b/src/views/Game/GameLog.tsx index 0243a43e50..b52000ea60 100644 --- a/src/views/Game/GameLog.tsx +++ b/src/views/Game/GameLog.tsx @@ -62,7 +62,7 @@ export function GameLog({ goban_config, onContainsTimeout }: GameLogProps): JSX.

{_("Game Log")}

{log.length > 0 ? ( <> - +
diff --git a/src/views/Game/GameLogModal.styl b/src/views/Game/GameLogModal.styl index 22d8e389dc..bda14b9711 100644 --- a/src/views/Game/GameLogModal.styl +++ b/src/views/Game/GameLogModal.styl @@ -19,18 +19,4 @@ width: 90vw; min-width: 90vw; max-width: 90vw; - - td { - padding-right: 1rem; - border-bottom: 1px solid transparent; - themed border-bottom-color shade3 - } - - .timestamp { - width: 12rem; - } - - .field { - margin-right: 1rem; - } } diff --git a/src/views/Game/GameLogModal.tsx b/src/views/Game/GameLogModal.tsx index ad45f5facd..21cea13f67 100644 --- a/src/views/Game/GameLogModal.tsx +++ b/src/views/Game/GameLogModal.tsx @@ -55,7 +55,6 @@ export class GameLogModal extends Modal) { - console.log(log); this.setState({ log }); } diff --git a/src/views/Game/GameTimings.tsx b/src/views/Game/GameTimings.tsx index 22d19ea17b..fac69dd436 100644 --- a/src/views/Game/GameTimings.tsx +++ b/src/views/Game/GameTimings.tsx @@ -22,6 +22,7 @@ import * as React from "react"; import { AdHocPackedMove, GobanMovesArray } from "goban"; import { useUser } from "hooks"; +import { showSecondsResolution } from "misc"; interface GameTimingProperties { moves: GobanMovesArray; @@ -31,6 +32,7 @@ interface GameTimingProperties { handicap: number; black_id: number; white_id: number; + onFinalActionCalculated?: (final_action_timing: moment.Duration) => void; } export function GameTimings(props: GameTimingProperties): JSX.Element { @@ -47,22 +49,22 @@ export function GameTimings(props: GameTimingProperties): JSX.Element { {moment.duration(duration).format()} ); - // needed because end_time and start_time are only to the nearest second - const show_seconds_resolution = (duration: moment.Duration): string => { - if (duration < moment.duration(1000)) { - return `${moment.duration(duration).asSeconds().toFixed(2)}s`; - } else if (duration < moment.duration(60000)) { - return `${moment.duration(duration).asSeconds().toFixed(1)}s`; - } else { - return moment.duration(duration).format("d:h:m:s"); - } - }; - const game_elapsed: ReturnType = moment.duration(0); // running total const black_elapsed: ReturnType = moment.duration(0); const white_elapsed: ReturnType = moment.duration(0); const move_elapsed: Array> = []; // the time elapsed up to each move + // Publish final action timing after game_elapsed is calculated (during render!) + React.useEffect(() => { + if (props.end_time && props.onFinalActionCalculated) { + props.onFinalActionCalculated( + moment + .duration(props.end_time - props.start_time, "seconds") + .subtract(game_elapsed), + ); + } + }, [props.start_time, props.end_time, props.onFinalActionCalculated]); + let non_handicap_moves = [...props.moves]; let handicap_moves: any[] = []; let handicap_move_offset = 0; @@ -244,7 +246,7 @@ export function GameTimings(props: GameTimingProperties): JSX.Element {
Final action:
{props.end_time && - show_seconds_resolution( + showSecondsResolution( moment .duration(props.end_time - props.start_time, "seconds") .subtract(game_elapsed), diff --git a/src/views/ReportsCenter/ReportedGame.tsx b/src/views/ReportsCenter/ReportedGame.tsx index 7d2a6a25a3..7b5b2e14fb 100644 --- a/src/views/ReportsCenter/ReportedGame.tsx +++ b/src/views/ReportsCenter/ReportedGame.tsx @@ -16,13 +16,14 @@ */ import * as React from "react"; +import * as moment from "moment"; import { useRefresh } from "hooks"; import { _, pgettext } from "translate"; import { Link } from "react-router-dom"; import { MiniGoban } from "MiniGoban"; import { alert } from "swal_config"; import { post, get } from "requests"; -import { errorAlerter } from "misc"; +import { errorAlerter, showSecondsResolution } from "misc"; import { doAnnul } from "moderation"; import { @@ -33,6 +34,7 @@ import { GobanContext, useCurrentMove, game_control, + GameLog, } from "Game"; import { GobanRenderer } from "goban"; import { Resizable } from "Resizable"; @@ -40,7 +42,6 @@ import { Resizable } from "Resizable"; import { Player } from "Player"; import { useUser } from "hooks"; import { shortTimeControl } from "TimeControl"; -import { GameLog } from "../Game/GameLog"; export function ReportedGame({ game_id, @@ -61,6 +62,7 @@ export function ReportedGame({ const [game, setGame] = React.useState(null); const [_aiReviewUuid, setAiReviewUuid] = React.useState(null); const [annulled, setAnnulled] = React.useState(false); + const [finalActionTime, setFinalActionTime] = React.useState(null); const [timedOutPlayer, setTimedOutPlayer] = React.useState(null); const user = useUser(); @@ -178,30 +180,39 @@ export function ReportedGame({
White: {game && }
Game Phase: {goban.engine.phase}
{(goban.engine.phase === "finished" || null) && ( -
- {_("Game Outcome:") + ` ${winner} (`} - - {` ) ${pgettext("use like: they won 'by' this much", "by")} `} - {goban.engine.outcome} - {annulled ? _(" annulled") : ""} -
- )} - {timedOutPlayer && ( -
- {_("Player timed out:")} - - {timedOutPlayer === reported_by - ? pgettext( - "A note of surprise telling a moderator that the person who timed out is the reporter", - " (reporter!)", - ) - : pgettext( - "A label next to a player name telling a moderator that a they are the one who was reported", - " (reported)", - )} -
- )} + <> +
+ {_("Game Outcome:") + ` ${winner} (`} + + {` ) ${pgettext( + "use like: they won 'by' this much", + "by", + )} `} + {goban.engine.outcome} + {annulled ? _(" annulled") : ""} +
+
+ {_("The last event took: ") + + showSecondsResolution(finalActionTime)} +
+ {timedOutPlayer && ( +
+ {_("Player timed out:")} + + {timedOutPlayer === reported_by + ? pgettext( + "A note of surprise telling a moderator that the person who timed out is the reporter", + " (reporter!)", + ) + : pgettext( + "A label next to a player name telling a moderator that a they are the one who was reported", + " (the reported player)", + )} +
+ )} + + )} {user.is_moderator && ( <> {goban.engine.phase === "finished" ? ( @@ -249,7 +260,6 @@ export function ReportedGame({ hidden={false} /> )} -