diff --git a/frontend/app/components/Session_/Player/Controls/Timeline.tsx b/frontend/app/components/Session_/Player/Controls/Timeline.tsx index 32e6d35eb..5a47b1df2 100644 --- a/frontend/app/components/Session_/Player/Controls/Timeline.tsx +++ b/frontend/app/components/Session_/Player/Controls/Timeline.tsx @@ -11,7 +11,7 @@ import TooltipContainer from './components/TooltipContainer'; import { PlayerContext } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite'; import { useStore } from 'App/mstore'; - +import { DateTime, Duration } from 'luxon'; function getTimelinePosition(value: number, scale: number) { const pos = value * scale; @@ -22,7 +22,7 @@ function getTimelinePosition(value: number, scale: number) { function Timeline(props) { const { player, store } = useContext(PlayerContext) const [wasPlaying, setWasPlaying] = useState(false) - const { notesStore } = useStore(); + const { notesStore, settingsStore } = useStore(); const { playing, time, @@ -89,20 +89,21 @@ function Timeline(props) { return props.tooltipVisible && hideTimeTooltip(); } - let timeLineTooltip; if (live) { const [time, duration] = getLiveTime(e); timeLineTooltip = { - time: duration - time, + time: Duration.fromMillis(duration - time).toFormat(`-mm:ss`), offset: e.nativeEvent.offsetX, isVisible: true, }; } else { const time = getTime(e); timeLineTooltip = { - time: time, + time: !settingsStore.isUniTs + ? Duration.fromMillis(time).toFormat(`mm:ss`) + : DateTime.fromMillis(props.startedAt + time).toFormat(`hh:mm:ss a`), offset: e.nativeEvent.offsetX, isVisible: true, }; diff --git a/frontend/app/components/Session_/Player/Controls/components/TimeTooltip.tsx b/frontend/app/components/Session_/Player/Controls/components/TimeTooltip.tsx index e1be98622..5c4d19c0d 100644 --- a/frontend/app/components/Session_/Player/Controls/components/TimeTooltip.tsx +++ b/frontend/app/components/Session_/Player/Controls/components/TimeTooltip.tsx @@ -8,26 +8,25 @@ interface Props { time: number; offset: number; isVisible: boolean; - liveTimeTravel: boolean; } function TimeTooltip({ time, offset, isVisible, - liveTimeTravel, }: Props) { - const duration = Duration.fromMillis(time).toFormat(`${liveTimeTravel ? '-' : ''}mm:ss`); return (
- {!time ? 'Loading' : duration} + {!time ? 'Loading' : time}
); } diff --git a/frontend/app/components/Session_/Subheader.js b/frontend/app/components/Session_/Subheader.js index be7679fd5..45d5540bb 100644 --- a/frontend/app/components/Session_/Subheader.js +++ b/frontend/app/components/Session_/Subheader.js @@ -11,7 +11,9 @@ import { useModal } from 'App/components/Modal'; import BugReportModal from './BugReport/BugReportModal'; import { PlayerContext } from 'App/components/Session/playerContext'; import { observer } from 'mobx-react-lite'; +import { useStore } from 'App/mstore'; import AutoplayToggle from 'Shared/AutoplayToggle'; +import { Toggler } from 'UI'; function SubHeader(props) { const { player, store } = React.useContext(PlayerContext) @@ -26,6 +28,7 @@ function SubHeader(props) { eventList: eventsList, endTime, } = store.get() + const { settingsStore } = useStore() const mappedResourceList = resourceList .filter((r) => r.isRed() || r.isYellow()) @@ -53,8 +56,19 @@ function SubHeader(props) { showModal(, { right: true }); }; + const timeStr = settingsStore.isUniTs ? 'Local Time' : 'Relative Timestamp' + const onFormatCh = (e) => { + e.stopPropagation(); + e.preventDefault(); + settingsStore.toggleTimeFormat() + } return (
+ + {!isAssist && (
+ + {timeStr} +
)} {location && (
+ const list = useMemo(() => resourceList.filter(res => !fetchList.some(ft => { if (res.url !== ft.url) { return false } if (Math.abs(res.time - ft.time) > 200) { return false } // TODO: find good epsilons @@ -228,7 +228,7 @@ function NetworkPanel() { const showDetailsModal = (item: any) => { setIsDetailsModalActive(true) showModal( - 0} />, + 0} />, { right: true, onClose: () => { @@ -366,7 +366,7 @@ function NetworkPanel() { hidden: activeTab === XHR, }, { - label: 'Time', + label: 'Duration', width: 80, dataKey: 'duration', render: renderDuration, @@ -380,4 +380,6 @@ function NetworkPanel() { ); } -export default observer(NetworkPanel); +export default connect((state: any) => ({ + startedAt: state.getIn(['sessions', 'current', 'startedAt']), +}))(observer(NetworkPanel)); diff --git a/frontend/app/components/shared/FetchDetailsModal/FetchDetailsModal.tsx b/frontend/app/components/shared/FetchDetailsModal/FetchDetailsModal.tsx index a9f90e723..be5386eab 100644 --- a/frontend/app/components/shared/FetchDetailsModal/FetchDetailsModal.tsx +++ b/frontend/app/components/shared/FetchDetailsModal/FetchDetailsModal.tsx @@ -5,9 +5,11 @@ import FetchPluginMessage from './components/FetchPluginMessage'; import { TYPES } from 'Types/session/resource'; import FetchTabs from './components/FetchTabs/FetchTabs'; import { useStore } from 'App/mstore'; +import { DateTime } from 'luxon'; interface Props { resource: any; + time?: number; rows?: any; fetchPresented?: boolean; } @@ -47,7 +49,7 @@ function FetchDetailsModal(props: Props) { return (
Network Request
- + {isXHR && !fetchPresented && } {isXHR && } diff --git a/frontend/app/components/shared/FetchDetailsModal/components/FetchBasicDetails/FetchBasicDetails.tsx b/frontend/app/components/shared/FetchDetailsModal/components/FetchBasicDetails/FetchBasicDetails.tsx index 49e16c00f..94aadfb1d 100644 --- a/frontend/app/components/shared/FetchDetailsModal/components/FetchBasicDetails/FetchBasicDetails.tsx +++ b/frontend/app/components/shared/FetchDetailsModal/components/FetchBasicDetails/FetchBasicDetails.tsx @@ -5,8 +5,9 @@ import cn from 'classnames'; interface Props { resource: any; + timestamp?: string; } -function FetchBasicDetails({ resource }: Props) { +function FetchBasicDetails({ resource, timestamp }: Props) { const _duration = parseInt(resource.duration); const text = useMemo(() => { if (resource.url.length > 50) { @@ -69,12 +70,22 @@ function FetchBasicDetails({ resource }: Props) { {!!_duration && (
-
Time
+
Duration
{_duration} ms
)} + + {timestamp && ( +
+
Time
+
+ {timestamp} +
+
+ + )}
); } diff --git a/frontend/app/mstore/notesStore.ts b/frontend/app/mstore/notesStore.ts index 1cb041049..ea52cb2e7 100644 --- a/frontend/app/mstore/notesStore.ts +++ b/frontend/app/mstore/notesStore.ts @@ -44,7 +44,7 @@ export default class NotesStore { this.loading = true try { const notes = await notesService.getNotesBySessionId(sessionId) - this.sessionNotes = notes + this.setNotes(notes) return notes; } catch (e) { console.error(e) @@ -53,6 +53,10 @@ export default class NotesStore { } } + setNotes(notes: Note[]) { + this.sessionNotes = notes + } + async addNote(sessionId: string, note: WriteNote) { this.loading = true try { diff --git a/frontend/app/mstore/settingsStore.ts b/frontend/app/mstore/settingsStore.ts index 45cb9610c..b05c9ce58 100644 --- a/frontend/app/mstore/settingsStore.ts +++ b/frontend/app/mstore/settingsStore.ts @@ -8,6 +8,7 @@ export default class SettingsStore { sessionSettings: SessionSettings = new SessionSettings() captureRateFetched: boolean = false; limits: any = null; + isUniTs = false; constructor() { makeAutoObservable(this, { @@ -15,6 +16,10 @@ export default class SettingsStore { }) } + toggleTimeFormat = () => { + this.isUniTs = !this.isUniTs + } + saveCaptureRate(data: any) { return sessionService.saveCaptureRate(data) .then(data => data.json())