From 54e56f0416be6ed01e51b370a45f70095b772598 Mon Sep 17 00:00:00 2001 From: tabarra <1808295+tabarra@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:10:09 -0300 Subject: [PATCH] wip(web/actionModal): finished info tab + smaller sttuff --- panel/src/components/DateTimeCorrected.tsx | 37 +++++++++ .../src/layout/ActionModal/ActionInfoTab.tsx | 80 +++++++++++++++---- panel/src/layout/ActionModal/ActionModal.tsx | 25 +++--- 3 files changed, 118 insertions(+), 24 deletions(-) create mode 100644 panel/src/components/DateTimeCorrected.tsx diff --git a/panel/src/components/DateTimeCorrected.tsx b/panel/src/components/DateTimeCorrected.tsx new file mode 100644 index 000000000..4480eae53 --- /dev/null +++ b/panel/src/components/DateTimeCorrected.tsx @@ -0,0 +1,37 @@ +import { tsToLocaleDate, tsToLocaleDateTime } from "@/lib/utils"; +import { txToast } from "./TxToaster"; + +const clockSkewTolerance = 5 * 60; //5 minutes + +type Props = { + tsFetch: number; + tsObject: number; + serverTime: number; + className?: string; + isDateOnly?: boolean; +}; + +export default function DateTimeCorrected({ tsFetch, tsObject, serverTime, className, isDateOnly }: Props) { + const serverClockDrift = serverTime - tsFetch; //be positive if server is ahead + const howFarInThePast = serverTime - tsObject; + const localTime = tsFetch - howFarInThePast; + + const clockDriftBtnHandler = () => { + txToast.warning(`This means that the server clock is ${Math.abs(serverClockDrift)} seconds ${serverClockDrift > 0 ? 'ahead' : 'behind'} your computer time. Make sure both your computer and the server have their clocks synchronized.`); + } + const displayTime = isDateOnly + ? tsToLocaleDate(localTime, 'short') + : tsToLocaleDateTime(localTime, 'medium', 'short') + return + {displayTime} + {Math.abs(serverClockDrift) > clockSkewTolerance && ( + + )} + +} diff --git a/panel/src/layout/ActionModal/ActionInfoTab.tsx b/panel/src/layout/ActionModal/ActionInfoTab.tsx index 7bf01868b..9aabc437c 100644 --- a/panel/src/layout/ActionModal/ActionInfoTab.tsx +++ b/panel/src/layout/ActionModal/ActionInfoTab.tsx @@ -1,10 +1,11 @@ import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; -import { tsToLocaleDateTime } from "@/lib/utils"; +import { msToDuration, tsToLocaleDateTime } from "@/lib/utils"; import { useRef, useState } from "react"; import { DatabaseActionType } from "../../../../core/components/PlayerDatabase/databaseTypes"; import { useOpenPlayerModal } from "@/hooks/playerModal"; +import DateTimeCorrected from "@/components/DateTimeCorrected"; @@ -36,16 +37,53 @@ function ActionReasonBox({ actionReason }: { actionReason: string }) { type ActionInfoTabProps = { - actionId: string; action: DatabaseActionType; - setSelectedTab: (t: string) => void; - refreshModalData: () => void; + serverTime: number; + tsFetch: number; } -export default function ActionInfoTab({ actionId, action, setSelectedTab, refreshModalData }: ActionInfoTabProps) { - const actionDateTimeText = tsToLocaleDateTime(action.timestamp); +export default function ActionInfoTab({ action, serverTime, tsFetch }: ActionInfoTabProps) { const openPlayerModal = useOpenPlayerModal(); + let banExpirationText: React.ReactNode; + if (action.type === 'ban') { + if (action.expiration === false) { + banExpirationText = Never; + } else if (action.expiration > serverTime) { + const distance = msToDuration( + (serverTime - action.expiration) * 1000, + { units: ['mo', 'w', 'd', 'h', 'm'] } + ) + banExpirationText = In {distance}; + } else { + banExpirationText = ; + } + } + + let revokedText: React.ReactNode; + if (action.revocation.timestamp) { + revokedText = + By {action.revocation.author} on + ; + } else { + revokedText = No; + } + + //Player stuff + const playerDisplayName = action.playerName !== false + ? {action.playerName} + : unknown player; const targetLicenses = action.ids.filter(id => id.startsWith('license:')); const linkedPlayer = targetLicenses.length === 1 ? targetLicenses[0].split(':')[1] : false; const handleViewPlayerClick = () => { @@ -57,15 +95,33 @@ export default function ActionInfoTab({ actionId, action, setSelectedTab, refres
Date/Time
-
{actionDateTimeText}
+
+ +
+ {action.type === 'ban' && ( +
+
Expiration
+
{banExpirationText}
+
+ )} +
+
Revoked
+
{revokedText}
+
+
Admin
{action.author}
Player
-
{action.playerName}
+
{playerDisplayName}
+ >View
-
-
Status
-
FIXME:
-
diff --git a/panel/src/layout/ActionModal/ActionModal.tsx b/panel/src/layout/ActionModal/ActionModal.tsx index 15742c761..e49b7decf 100644 --- a/panel/src/layout/ActionModal/ActionModal.tsx +++ b/panel/src/layout/ActionModal/ActionModal.tsx @@ -44,6 +44,7 @@ export default function ActionModal() { const [currRefreshKey, setCurrRefreshKey] = useState(0); const [modalData, setModalData] = useState(undefined); const [modalError, setModalError] = useState(''); + const [tsFetch, setTsFetch] = useState(0); const historyGetActionApi = useBackendApi({ method: 'GET', path: `/history/action`, @@ -67,6 +68,7 @@ export default function ActionModal() { setModalError(resp.error); } else { setModalData(resp); + setTsFetch(Math.round(Date.now() / 1000)); } }, error: (error) => { @@ -92,17 +94,19 @@ export default function ActionModal() { let pageTitle: JSX.Element; if (modalData) { + const displayName = modalData.action.playerName !== false + ? {modalData.action.playerName} + : unknown player; if (modalData.action.type === 'ban') { pageTitle = <> - [BAN] - {modalData.action.id} + [{modalData.action.id}] + Banned {displayName} ; } else if (modalData.action.type === 'warn') { pageTitle = <> - [WARN] - {modalData.action.id} + [{modalData.action.id}] + Warned {displayName} ; - } else { throw new Error(`Unknown action type: ${modalData.action.type}`); } @@ -119,13 +123,16 @@ export default function ActionModal() { onOpenAutoFocus={(e) => e.preventDefault()} > - {pageTitle} + + {pageTitle} +
{modalTabs.map((tab) => ( @@ -154,10 +162,9 @@ export default function ActionModal() { ) : ( <> {selectedTab === 'Info' && } {selectedTab === 'IDs' &&