diff --git a/frontend/app/components/Session_/Subheader.js b/frontend/app/components/Session_/Subheader.js
index dcadfad65..ce8cd0464 100644
--- a/frontend/app/components/Session_/Subheader.js
+++ b/frontend/app/components/Session_/Subheader.js
@@ -1,5 +1,5 @@
import { ShareAltOutlined } from '@ant-design/icons';
-import { Button as AntButton, Switch, Tooltip } from 'antd';
+import { Button as AntButton, Popover, Switch, Tooltip } from 'antd';
import cn from 'classnames';
import { Link2 } from 'lucide-react';
import { observer } from 'mobx-react-lite';
@@ -11,7 +11,7 @@ import { useStore } from 'App/mstore';
import { checkParam, truncateStringToFit } from 'App/utils';
import SessionTabs from 'Components/Session/Player/SharedComponents/SessionTabs';
import KeyboardHelp from 'Components/Session_/Player/Controls/components/KeyboardHelp';
-import { Icon } from 'UI';
+import WarnBadge from 'Components/Session_/WarnBadge';
import Bookmark from 'Shared/Bookmark';
@@ -20,18 +20,13 @@ import Issues from './Issues/Issues';
import QueueControls from './QueueControls';
import NotePopup from './components/NotePopup';
-const localhostWarn = (project) => project + '_localhost_warn';
const disableDevtools = 'or_devtools_uxt_toggle';
function SubHeader(props) {
- const { uxtestingStore, projectsStore, userStore, integrationsStore } = useStore();
+ const { uxtestingStore, integrationsStore, sessionStore, projectsStore } = useStore();
+ const currentSession = sessionStore.current
+ const projectId = projectsStore.siteId;
const integrations = integrationsStore.issues.list;
- const defaultLocalhostWarn = React.useMemo(() => {
- const siteId = projectsStore.siteId;
- const localhostWarnKey = localhostWarn(siteId);
- return localStorage.getItem(localhostWarnKey) !== '1';
- }, [projectsStore.siteId]);
- const [showWarningModal, setWarning] = React.useState(defaultLocalhostWarn);
const { store } = React.useContext(PlayerContext);
const { location: currentLocation = 'loading...' } = store.get();
const hasIframe = localStorage.getItem(IFRAME) === 'true';
@@ -56,15 +51,6 @@ function SubHeader(props) {
window.innerWidth - 200
);
- const showWarning =
- currentLocation &&
- /(localhost)|(127.0.0.1)|(0.0.0.0)/.test(currentLocation) &&
- showWarningModal;
- const closeWarning = () => {
- localStorage.setItem(localhostWarnKey, '1');
- setWarning(false);
- };
-
const toggleDevtools = (enabled) => {
localStorage.setItem(disableDevtools, enabled ? '0' : '1');
uxtestingStore.setHideDevtools(!enabled);
@@ -82,32 +68,11 @@ function SubHeader(props) {
: undefined,
}}
>
- {showWarning ? (
-
- Some assets may load incorrectly on localhost.
-
- Learn More
-
-
-
-
-
- ) : null}
+
@@ -120,9 +85,9 @@ function SubHeader(props) {
style={{ width: 'max-content' }}
>
-
+
- {enabledIntegration && }
+ {enabledIntegration && }
project + '_localhost_warn';
+
+const VersionComparison = {
+ Lower: -1,
+ Same: 0,
+ Higher: 1,
+};
+
+function compareVersions(
+ suppliedVersion: string,
+ currentVersion: string
+): number {
+ function parseVersion(version: string) {
+ const cleanVersion = version.split(/[-+]/)[0];
+ return cleanVersion.split('.').map(Number);
+ }
+
+ const v1 = parseVersion(suppliedVersion);
+ const v2 = parseVersion(currentVersion);
+
+ const length = Math.max(v1.length, v2.length);
+ while (v1.length < length) v1.push(0);
+ while (v2.length < length) v2.push(0);
+
+ for (let i = 0; i < length; i++) {
+ if (v1[i] < v2[i]) return VersionComparison.Lower;
+ if (v1[i] > v2[i]) return VersionComparison.Higher;
+ }
+
+ return VersionComparison.Same;
+}
+
+const WarnBadge = React.memo(
+ ({
+ currentLocation,
+ version,
+ siteId,
+ }: {
+ currentLocation: string;
+ version: string;
+ siteId: string;
+ }) => {
+ const localhostWarnSiteKey = localhostWarn(siteId);
+ const defaultLocalhostWarn =
+ localStorage.getItem(localhostWarnSiteKey) !== '1';
+ const localhostWarnActive =
+ currentLocation &&
+ defaultLocalhostWarn &&
+ /(localhost)|(127.0.0.1)|(0.0.0.0)/.test(currentLocation);
+ const trackerVersion = window.env.TRACKER_VERSION ?? '1.0.0';
+ const trackerVerDiff = compareVersions(version, trackerVersion);
+ const trackerWarnActive = trackerVerDiff !== VersionComparison.Same;
+
+ const [showLocalhostWarn, setLocalhostWarn] =
+ React.useState(localhostWarnActive);
+ const [showTrackerWarn, setTrackerWarn] = React.useState(trackerWarnActive);
+
+ const closeWarning = (type: 1 | 2) => {
+ if (type === 1) {
+ localStorage.setItem(localhostWarnSiteKey, '1');
+ setLocalhostWarn(false);
+ }
+ if (type === 2) {
+ setTrackerWarn(false);
+ }
+ };
+
+ if (!showLocalhostWarn && !showTrackerWarn) return null;
+
+ return (
+
+ {showLocalhostWarn ? (
+
+
+
+
closeWarning(1)}>
+
+
+
+ ) : null}
+ {showTrackerWarn ? (
+
+
+
+ Tracker version({version}) for this recording is{' '}
+ {trackerVerDiff === VersionComparison.Lower
+ ? 'lower '
+ : 'ahead of '}
+ the current({trackerVersion}) version.
+
+
+
+
+
closeWarning(2)}>
+
+
+
+ ) : null}
+
+ );
+ }
+);
+
+export default WarnBadge;
diff --git a/frontend/app/types/session/session.ts b/frontend/app/types/session/session.ts
index 3de179b97..f1ac1042f 100644
--- a/frontend/app/types/session/session.ts
+++ b/frontend/app/types/session/session.ts
@@ -225,6 +225,7 @@ export default class Session {
platform: ISession['platform'];
isMobileNative?: ISession['isMobileNative'];
audio?: ISession['audio'];
+ trackerVersion?: string;
fileKey: ISession['fileKey'];
durationSeconds: number;