UI patch 4 (#2572)
* ui: spot related fixes * ui: remove random logs * ui: fix insights mapping * ui: hide doc link for spot stuff * ui: force worker for hls
This commit is contained in:
parent
5e1d895ce6
commit
e196643c8f
10 changed files with 74 additions and 68 deletions
|
|
@ -16,7 +16,6 @@ function WidgetOptions(props: Props) {
|
||||||
const metric: any = metricStore.instance;
|
const metric: any = metricStore.instance;
|
||||||
|
|
||||||
const handleChange = (value: any) => {
|
const handleChange = (value: any) => {
|
||||||
console.log(`selected ${value}`);
|
|
||||||
metric.update({ metricFormat: value });
|
metric.update({ metricFormat: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,6 @@ function EventsBlock(props: IProps) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (scroller.current) {
|
if (scroller.current) {
|
||||||
if (!mouseOver) {
|
if (!mouseOver) {
|
||||||
console.log('scrolling to index', currentTimeEventIndex, scroller.current);
|
|
||||||
scroller.current.scrollToIndex(currentTimeEventIndex, { align: 'center' });
|
scroller.current.scrollToIndex(currentTimeEventIndex, { align: 'center' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ function SpotNetwork({ panelHeight, onClose }: { panelHeight: number, onClose: (
|
||||||
resourceListNow={[]}
|
resourceListNow={[]}
|
||||||
websocketList={[]}
|
websocketList={[]}
|
||||||
websocketListNow={[]}
|
websocketListNow={[]}
|
||||||
|
isSpot
|
||||||
/* @ts-ignore */
|
/* @ts-ignore */
|
||||||
player={{ jump: (t) => spotPlayerStore.setTime(t) }}
|
player={{ jump: (t) => spotPlayerStore.setTime(t) }}
|
||||||
activeOutsideIndex={index}
|
activeOutsideIndex={index}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
import { InfoCircleOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { Alert, Button } from 'antd';
|
||||||
import Hls from 'hls.js';
|
import Hls from 'hls.js';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Alert, Button} from 'antd';
|
|
||||||
import {PlayCircleOutlined, InfoCircleOutlined} from '@ant-design/icons';
|
|
||||||
import spotPlayerStore from '../spotPlayerStore';
|
import spotPlayerStore from '../spotPlayerStore';
|
||||||
|
|
||||||
const base64toblob = (str: string) => {
|
const base64toblob = (str: string) => {
|
||||||
|
|
@ -33,7 +34,9 @@ function SpotVideoContainer({
|
||||||
checkReady: () => Promise<boolean>;
|
checkReady: () => Promise<boolean>;
|
||||||
}) {
|
}) {
|
||||||
const [prevIsProcessing, setPrevIsProcessing] = React.useState(false);
|
const [prevIsProcessing, setPrevIsProcessing] = React.useState(false);
|
||||||
const [processingState, setProcessingState] = React.useState<ProcessingState>(ProcessingState.Unchecked);
|
const [processingState, setProcessingState] = React.useState<ProcessingState>(
|
||||||
|
ProcessingState.Unchecked
|
||||||
|
);
|
||||||
const [isLoaded, setLoaded] = React.useState(false);
|
const [isLoaded, setLoaded] = React.useState(false);
|
||||||
const videoRef = React.useRef<HTMLVideoElement>(null);
|
const videoRef = React.useRef<HTMLVideoElement>(null);
|
||||||
const playbackTime = React.useRef(0);
|
const playbackTime = React.useRef(0);
|
||||||
|
|
@ -69,13 +72,15 @@ function SpotVideoContainer({
|
||||||
clearInterval(int);
|
clearInterval(int);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 5000)
|
}, 5000);
|
||||||
} else {
|
} else {
|
||||||
setProcessingState(ProcessingState.Ready);
|
setProcessingState(ProcessingState.Ready);
|
||||||
}
|
}
|
||||||
import('hls.js').then(({ default: Hls }) => {
|
import('hls.js').then(({ default: Hls }) => {
|
||||||
if (Hls.isSupported() && videoRef.current) {
|
if (Hls.isSupported() && videoRef.current) {
|
||||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
const isSafari = /^((?!chrome|android).)*safari/i.test(
|
||||||
|
navigator.userAgent
|
||||||
|
);
|
||||||
if (isSafari) {
|
if (isSafari) {
|
||||||
setLoaded(true);
|
setLoaded(true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -85,9 +90,7 @@ function SpotVideoContainer({
|
||||||
}
|
}
|
||||||
if (streamFile) {
|
if (streamFile) {
|
||||||
const hls = new Hls({
|
const hls = new Hls({
|
||||||
// not needed for small videos (we have 3 min limit and 720 quality with half kbps)
|
enableWorker: true,
|
||||||
enableWorker: false,
|
|
||||||
// = 1MB, should be enough
|
|
||||||
maxBufferSize: 1000 * 1000,
|
maxBufferSize: 1000 * 1000,
|
||||||
});
|
});
|
||||||
const url = URL.createObjectURL(base64toblob(streamFile));
|
const url = URL.createObjectURL(base64toblob(streamFile));
|
||||||
|
|
@ -124,7 +127,11 @@ function SpotVideoContainer({
|
||||||
};
|
};
|
||||||
check();
|
check();
|
||||||
}
|
}
|
||||||
} else if (streamFile && videoRef.current && videoRef.current.canPlayType('application/vnd.apple.mpegurl')) {
|
} else if (
|
||||||
|
streamFile &&
|
||||||
|
videoRef.current &&
|
||||||
|
videoRef.current.canPlayType('application/vnd.apple.mpegurl')
|
||||||
|
) {
|
||||||
setLoaded(true);
|
setLoaded(true);
|
||||||
videoRef.current.src = URL.createObjectURL(base64toblob(streamFile));
|
videoRef.current.src = URL.createObjectURL(base64toblob(streamFile));
|
||||||
startPlaying();
|
startPlaying();
|
||||||
|
|
@ -185,40 +192,44 @@ function SpotVideoContainer({
|
||||||
}
|
}
|
||||||
}, [spotPlayerStore.playbackRate]);
|
}, [spotPlayerStore.playbackRate]);
|
||||||
|
|
||||||
const reloadPage = () => { window.location.reload(); };
|
const reloadPage = () => {
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="absolute z-20 left-2/4 -top-6" style={{ transform: 'translate(-50%, 0)' }}>
|
<div
|
||||||
{processingState === ProcessingState.Processing ? (
|
className="absolute z-20 left-2/4 -top-6"
|
||||||
<Alert
|
style={{ transform: 'translate(-50%, 0)' }}
|
||||||
className='trimIsProcessing rounded-lg shadow-sm border-indigo-500 bg-indigo-50'
|
>
|
||||||
message="You’re viewing the original recording. Processed Spot will be available here shortly."
|
{processingState === ProcessingState.Processing ? (
|
||||||
showIcon
|
<Alert
|
||||||
type="info"
|
className="trimIsProcessing rounded-lg shadow-sm border-indigo-500 bg-indigo-50"
|
||||||
icon={<InfoCircleOutlined style={{ color: '#394dfe' }} />}
|
message="You’re viewing the original recording. Processed Spot will be available here shortly."
|
||||||
/>
|
showIcon
|
||||||
) : prevIsProcessing ? (
|
type="info"
|
||||||
<Alert
|
icon={<InfoCircleOutlined style={{ color: '#394dfe' }} />}
|
||||||
className='trimIsReady rounded-lg shadow-sm border-0'
|
/>
|
||||||
message="Your processed Spot is ready!"
|
) : prevIsProcessing ? (
|
||||||
showIcon
|
<Alert
|
||||||
type="success"
|
className="trimIsReady rounded-lg shadow-sm border-0"
|
||||||
action={
|
message="Your processed Spot is ready!"
|
||||||
<Button
|
showIcon
|
||||||
size="small"
|
type="success"
|
||||||
type="default"
|
action={
|
||||||
icon={<PlayCircleOutlined />}
|
<Button
|
||||||
onClick={reloadPage}
|
size="small"
|
||||||
className='ml-2'
|
type="default"
|
||||||
>
|
icon={<PlayCircleOutlined />}
|
||||||
Play Now
|
onClick={reloadPage}
|
||||||
</Button>
|
className="ml-2"
|
||||||
}
|
>
|
||||||
/>
|
Play Now
|
||||||
) : null}
|
</Button>
|
||||||
</div>
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
{!isLoaded && (
|
{!isLoaded && (
|
||||||
<div className="relative w-full h-full flex flex-col items-center justify-center bg-white/50">
|
<div className="relative w-full h-full flex flex-col items-center justify-center bg-white/50">
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,7 @@ interface Props {
|
||||||
panelHeight: number;
|
panelHeight: number;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
activeOutsideIndex?: number;
|
activeOutsideIndex?: number;
|
||||||
|
isSpot?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NetworkPanelComp = observer(
|
export const NetworkPanelComp = observer(
|
||||||
|
|
@ -331,6 +332,7 @@ export const NetworkPanelComp = observer(
|
||||||
zoomEndTs,
|
zoomEndTs,
|
||||||
onClose,
|
onClose,
|
||||||
activeOutsideIndex,
|
activeOutsideIndex,
|
||||||
|
isSpot,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { showModal } = useModal();
|
const { showModal } = useModal();
|
||||||
const [sortBy, setSortBy] = useState('time');
|
const [sortBy, setSortBy] = useState('time');
|
||||||
|
|
@ -500,6 +502,7 @@ export const NetworkPanelComp = observer(
|
||||||
setIsDetailsModalActive(true);
|
setIsDetailsModalActive(true);
|
||||||
showModal(
|
showModal(
|
||||||
<FetchDetailsModal
|
<FetchDetailsModal
|
||||||
|
isSpot={isSpot}
|
||||||
time={item.time + startedAt}
|
time={item.time + startedAt}
|
||||||
resource={item}
|
resource={item}
|
||||||
rows={filteredList}
|
rows={filteredList}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@ interface Props {
|
||||||
time?: number;
|
time?: number;
|
||||||
rows?: any;
|
rows?: any;
|
||||||
fetchPresented?: boolean;
|
fetchPresented?: boolean;
|
||||||
|
isSpot?: boolean;
|
||||||
}
|
}
|
||||||
function FetchDetailsModal(props: Props) {
|
function FetchDetailsModal(props: Props) {
|
||||||
const { rows = [], fetchPresented = false } = props;
|
const { rows = [], fetchPresented = false, isSpot } = props;
|
||||||
const [resource, setResource] = useState(props.resource);
|
const [resource, setResource] = useState(props.resource);
|
||||||
const [first, setFirst] = useState(false);
|
const [first, setFirst] = useState(false);
|
||||||
const [last, setLast] = useState(false);
|
const [last, setLast] = useState(false);
|
||||||
|
|
@ -57,7 +58,7 @@ function FetchDetailsModal(props: Props) {
|
||||||
<h5 className="mb-4 text-2xl ">Network Request</h5>
|
<h5 className="mb-4 text-2xl ">Network Request</h5>
|
||||||
<FetchBasicDetails resource={resource} timestamp={props.time ? DateTime.fromMillis(props.time).setZone(timezone.value).toFormat(`hh:mm:ss a`) : undefined} />
|
<FetchBasicDetails resource={resource} timestamp={props.time ? DateTime.fromMillis(props.time).setZone(timezone.value).toFormat(`hh:mm:ss a`) : undefined} />
|
||||||
|
|
||||||
{isXHR && <FetchTabs resource={resource} />}
|
{isXHR && <FetchTabs isSpot={isSpot} resource={resource} />}
|
||||||
|
|
||||||
{rows && rows.length > 0 && (
|
{rows && rows.length > 0 && (
|
||||||
<div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">
|
<div className="flex justify-between absolute bottom-0 left-0 right-0 p-3 border-t bg-white">
|
||||||
|
|
|
||||||
|
|
@ -24,23 +24,16 @@ function FetchBasicDetails({ resource, timestamp }: Props) {
|
||||||
<div className="flex items-start py-1">
|
<div className="flex items-start py-1">
|
||||||
<div className="font-medium w-36">Name</div>
|
<div className="font-medium w-36">Name</div>
|
||||||
<Tag
|
<Tag
|
||||||
className="text-base max-w-96 rounded-lg text-clip bg-indigo-50 whitespace-nowrap overflow-hidden text-clip cursor-pointer word-break"
|
className="text-base max-w-80 rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer word-break"
|
||||||
bordered={false}>
|
bordered={false}>
|
||||||
<CopyText content={resource.url}>{resource.url}</CopyText>
|
<CopyText content={resource.url}>{resource.url}</CopyText>
|
||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center py-1">
|
|
||||||
<div className="font-medium w-36">Type</div>
|
|
||||||
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip" bordered={false}>
|
|
||||||
{resource.type}
|
|
||||||
</Tag>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{resource.method && (
|
{resource.method && (
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<div className="font-medium w-36">Request Method</div>
|
<div className="font-medium w-36">Request Method</div>
|
||||||
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip"
|
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis"
|
||||||
bordered={false}>
|
bordered={false}>
|
||||||
{resource.method}
|
{resource.method}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|
@ -53,7 +46,7 @@ function FetchBasicDetails({ resource, timestamp }: Props) {
|
||||||
<Tag
|
<Tag
|
||||||
bordered={false}
|
bordered={false}
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip flex items-center',
|
'text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis flex items-center',
|
||||||
{ 'error color-red': !resource.success }
|
{ 'error color-red': !resource.success }
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -64,7 +57,7 @@ function FetchBasicDetails({ resource, timestamp }: Props) {
|
||||||
|
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<div className="font-medium w-36">Type</div>
|
<div className="font-medium w-36">Type</div>
|
||||||
<Tag className="text-base capitalize rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip"
|
<Tag className="text-base capitalize rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis"
|
||||||
bordered={false}>
|
bordered={false}>
|
||||||
{resource.type}
|
{resource.type}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|
@ -73,7 +66,7 @@ function FetchBasicDetails({ resource, timestamp }: Props) {
|
||||||
{!!resource.decodedBodySize && (
|
{!!resource.decodedBodySize && (
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<div className="font-medium w-36">Size</div>
|
<div className="font-medium w-36">Size</div>
|
||||||
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip"
|
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis"
|
||||||
bordered={false}>
|
bordered={false}>
|
||||||
{formatBytes(resource.decodedBodySize)}
|
{formatBytes(resource.decodedBodySize)}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|
@ -84,7 +77,7 @@ function FetchBasicDetails({ resource, timestamp }: Props) {
|
||||||
{!!_duration && (
|
{!!_duration && (
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<div className="font-medium w-36">Duration</div>
|
<div className="font-medium w-36">Duration</div>
|
||||||
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip"
|
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis"
|
||||||
bordered={false}>
|
bordered={false}>
|
||||||
{_duration} ms
|
{_duration} ms
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|
@ -94,7 +87,7 @@ function FetchBasicDetails({ resource, timestamp }: Props) {
|
||||||
{timestamp && (
|
{timestamp && (
|
||||||
<div className="flex items-center py-1">
|
<div className="flex items-center py-1">
|
||||||
<div className="font-medium w-36">Time</div>
|
<div className="font-medium w-36">Time</div>
|
||||||
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-clip"
|
<Tag className="text-base rounded-lg bg-indigo-50 whitespace-nowrap overflow-hidden text-ellipsis"
|
||||||
bordered={false}>
|
bordered={false}>
|
||||||
{timestamp}
|
{timestamp}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,9 @@ function parseRequestResponse(
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
resource: { request: string, response: string };
|
resource: { request: string, response: string };
|
||||||
|
isSpot?: boolean;
|
||||||
}
|
}
|
||||||
function FetchTabs({ resource }: Props) {
|
function FetchTabs({ resource, isSpot }: Props) {
|
||||||
const [activeTab, setActiveTab] = useState(HEADERS);
|
const [activeTab, setActiveTab] = useState(HEADERS);
|
||||||
const onTabClick = (tab: string) => setActiveTab(tab);
|
const onTabClick = (tab: string) => setActiveTab(tab);
|
||||||
const [jsonRequest, setJsonRequest] = useState<Object | null>(null);
|
const [jsonRequest, setJsonRequest] = useState<Object | null>(null);
|
||||||
|
|
@ -84,7 +85,6 @@ function FetchTabs({ resource }: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { request, response } = resource;
|
const { request, response } = resource;
|
||||||
console.log(resource, request, response)
|
|
||||||
parseRequestResponse(
|
parseRequestResponse(
|
||||||
request,
|
request,
|
||||||
setRequestHeaders,
|
setRequestHeaders,
|
||||||
|
|
@ -119,7 +119,7 @@ function FetchTabs({ resource }: Props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
show={!jsonRequest && !stringRequest}
|
show={!isSpot && !jsonRequest && !stringRequest}
|
||||||
// animatedIcon="no-results"
|
// animatedIcon="no-results"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -151,7 +151,7 @@ function FetchTabs({ resource }: Props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
show={!jsonResponse && !stringResponse}
|
show={!isSpot && !jsonResponse && !stringResponse}
|
||||||
// animatedIcon="no-results"
|
// animatedIcon="no-results"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,7 @@ export default class Widget {
|
||||||
} else if (this.metricType === INSIGHTS) {
|
} else if (this.metricType === INSIGHTS) {
|
||||||
_data['issues'] = data
|
_data['issues'] = data
|
||||||
.filter((i: any) => i.change > 0 || i.change < 0)
|
.filter((i: any) => i.change > 0 || i.change < 0)
|
||||||
x .map(
|
.map(
|
||||||
(i: any) =>
|
(i: any) =>
|
||||||
new InsightIssue(i.category, i.name, i.ratio, i.oldValue, i.value, i.change, i.isNew)
|
new InsightIssue(i.category, i.name, i.ratio, i.oldValue, i.value, i.change, i.isNew)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,14 @@ function Settings({ goBack }: { goBack: () => void }) {
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
chrome.storage.local.get("settings", (data: any) => {
|
chrome.storage.local.get("settings", (data: any) => {
|
||||||
if (data.settings) {
|
if (data.settings) {
|
||||||
|
const ingest = data.settings.ingestPoint || "https://app.openreplay.com";
|
||||||
const devToolsEnabled =
|
const devToolsEnabled =
|
||||||
data.settings.consoleLogs && data.settings.networkLogs;
|
data.settings.consoleLogs && data.settings.networkLogs;
|
||||||
setOpenInNewTab(data.settings.openInNewTab ?? false);
|
setOpenInNewTab(data.settings.openInNewTab ?? false);
|
||||||
setIncludeDevTools(devToolsEnabled);
|
setIncludeDevTools(devToolsEnabled);
|
||||||
setShowIngest(data.settings.showIngest ?? true);
|
setIngest(ingest);
|
||||||
setIngest(data.settings.ingestPoint || "https://app.openreplay.com");
|
setTempIngest(ingest);
|
||||||
setTempIngest(
|
setShowIngest(ingest !== "https://app.openreplay.com");
|
||||||
data.settings.ingestPoint || "https://app.openreplay.com",
|
|
||||||
);
|
|
||||||
setEditIngest(!data.settings.ingestPoint);
|
setEditIngest(!data.settings.ingestPoint);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue