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:
Delirium 2024-09-13 14:37:20 +02:00 committed by GitHub
parent 5e1d895ce6
commit e196643c8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 74 additions and 68 deletions

View file

@ -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 });
}; };

View file

@ -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' });
} }
} }

View file

@ -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}

View file

@ -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="Youre 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="Youre 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">

View file

@ -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}

View file

@ -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">

View file

@ -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>

View file

@ -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>

View file

@ -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)
); );

View file

@ -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);
} }
}); });