ui fix outdated libraries, remove storybook (unused), fix player re-renders on mount

This commit is contained in:
nick-delirium 2024-10-14 12:48:08 +02:00
parent 59d6ef6bd0
commit 8024083c64
No known key found for this signature in database
GPG key ID: 93ABD695DF5FDBA0
69 changed files with 7573 additions and 14579 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

925
frontend/.yarn/releases/yarn-4.5.0.cjs vendored Executable file

File diff suppressed because one or more lines are too long

View file

@ -1,9 +1,7 @@
nodeLinker: node-modules
compressionLevel: 0
compressionLevel: 1
enableGlobalCache: true
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
nodeLinker: pnpm
yarnPath: .yarn/releases/yarn-3.2.1.cjs
yarnPath: .yarn/releases/yarn-4.5.0.cjs

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import ChatWindow from './ChatWindow';
storiesOf('Assist', module)
.add('ChatWindow', () => (
<ChatWindow userId="test@test.com" />
))

View file

@ -1,91 +1,96 @@
import React, {useEffect, useMemo} from 'react';
import {useStore} from "App/mstore";
import WidgetWrapper from "Components/Dashboard/components/WidgetWrapper/WidgetWrapper";
import {observer} from "mobx-react-lite";
import {Loader} from "UI";
import WidgetChart from "Components/Dashboard/components/WidgetChart/WidgetChart";
import LazyLoad from 'react-lazyload';
import {Card} from "antd";
import {CARD_CATEGORIES} from "Components/Dashboard/components/DashboardList/NewDashModal/ExampleCards";
import React, { useEffect, useMemo, lazy } from 'react';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
import { Loader } from 'UI';
import { Card } from 'antd';
import { CARD_CATEGORIES } from 'Components/Dashboard/components/DashboardList/NewDashModal/ExampleCards';
const CARD_TYPES_MAP = CARD_CATEGORIES.reduce((acc: any, category: any) => {
acc[category.key] = category.types;
return acc;
acc[category.key] = category.types;
return acc;
}, {});
const WidgetChart = lazy(() => import('Components/Dashboard/components/WidgetChart/WidgetChart'));
interface Props {
category?: string;
selectedList: any;
onCard: (metricId: number) => void;
query?: string;
category?: string;
selectedList: any;
onCard: (metricId: number) => void;
query?: string;
}
function CardsLibrary(props: Props) {
const {selectedList, query = ''} = props;
const {metricStore, dashboardStore} = useStore();
const { selectedList, query = '' } = props;
const { metricStore, dashboardStore } = useStore();
// const cards = useMemo(() => {
// return metricStore.filteredCards.filter((card: any) => {
// return CARD_TYPES_MAP[props.category || 'default'].includes(card.metricType);
// });
// }, [metricStore.filteredCards, props.category]);
// const cards = useMemo(() => {
// return metricStore.filteredCards.filter((card: any) => {
// return CARD_TYPES_MAP[props.category || 'default'].includes(card.metricType);
// });
// }, [metricStore.filteredCards, props.category]);
const cards = useMemo(() => {
return metricStore.filteredCards.filter((card: any) => {
return card.name.toLowerCase().includes(query.toLowerCase());
});
}, [query, metricStore.filteredCards]);
const cards = useMemo(() => {
return metricStore.filteredCards.filter((card: any) => {
return card.name.toLowerCase().includes(query.toLowerCase());
});
}, [query, metricStore.filteredCards]);
useEffect(() => {
metricStore.fetchList();
}, []);
useEffect(() => {
metricStore.fetchList();
}, []);
const onItemClick = (e: any, metricId: number) => {
e.stopPropagation();
e.preventDefault();
props.onCard(metricId);
}
const onItemClick = (e: any, metricId: number) => {
e.stopPropagation();
e.preventDefault();
props.onCard(metricId);
};
return (
<Loader loading={metricStore.isLoading}>
<div className="grid grid-cols-4 gap-4 items-start">
{cards.map((metric: any) => (
<React.Fragment key={metric.metricId}>
<div className={'relative col-span-' + metric.config.col}>
<div className="absolute inset-0 z-10 cursor-pointer" onClick={(e) => onItemClick(e, metric.metricId)} />
<LazyLoad>
<Card className='border border-transparent hover:border-indigo-50 hover:shadow-sm rounded-lg'
style={{
border: selectedList.includes(metric.metricId) ? '1px solid #1890ff' : '1px solid #f0f0f0',
}}
styles={{
header: {
padding: '4px 14px',
minHeight: '36px',
fontSize: '14px',
borderBottom: 'none'
},
body: {padding: '14px'},
cover: {
border: '2px solid #1890ff',
// border: selectedList.includes(metric.metricId) ? '2px solid #1890ff' : 'none',
}
}} title={metric.name}>
<WidgetChart
// isPreview={true}
metric={metric}
isTemplate={true}
isWidget={true}
isSaved={true}
/>
</Card>
</LazyLoad>
</div>
</React.Fragment>
))}
return (
<Loader loading={metricStore.isLoading}>
<div className="grid grid-cols-4 gap-4 items-start">
{cards.map((metric: any) => (
<React.Fragment key={metric.metricId}>
<div className={'relative col-span-' + metric.config.col}>
<div
className="absolute inset-0 z-10 cursor-pointer"
onClick={(e) => onItemClick(e, metric.metricId)}
/>
<Card
className="border border-transparent hover:border-indigo-50 hover:shadow-sm rounded-lg"
style={{
border: selectedList.includes(metric.metricId)
? '1px solid #1890ff'
: '1px solid #f0f0f0',
}}
styles={{
header: {
padding: '4px 14px',
minHeight: '36px',
fontSize: '14px',
borderBottom: 'none',
},
body: { padding: '14px' },
cover: {
border: '2px solid #1890ff',
// border: selectedList.includes(metric.metricId) ? '2px solid #1890ff' : 'none',
},
}}
title={metric.name}
>
<WidgetChart
// isPreview={true}
metric={metric}
isTemplate={true}
isWidget={true}
isSaved={true}
/>
</Card>
</div>
</Loader>
);
</React.Fragment>
))}
</div>
</Loader>
);
}
export default observer(CardsLibrary);

View file

@ -33,7 +33,7 @@ import InsightsCard from 'App/components/Dashboard/Widgets/CustomMetricsWidgets/
import SankeyChart from 'Shared/Insights/SankeyChart';
import CohortCard from '../../Widgets/CustomMetricsWidgets/CohortCard';
import SessionsBy from "Components/Dashboard/Widgets/CustomMetricsWidgets/SessionsBy";
import { Empty } from 'antd';
import { useInView } from "react-intersection-observer";
interface Props {
metric: any;
@ -43,6 +43,10 @@ interface Props {
}
function WidgetChart(props: Props) {
const { ref, inView } = useInView({
triggerOnce: true,
rootMargin: "200px 0px",
});
const {isSaved = false, metric, isTemplate} = props;
const {dashboardStore, metricStore, sessionStore} = useStore();
const _metric: any = metricStore.instance;
@ -105,6 +109,7 @@ function WidgetChart(props: Props) {
const debounceRequest: any = React.useCallback(debounce(fetchMetricChartData, 500), []);
const loadPage = () => {
if (!inView) return;
if (prevMetricRef.current && prevMetricRef.current.name !== metric.name) {
prevMetricRef.current = metric;
return;
@ -117,7 +122,18 @@ function WidgetChart(props: Props) {
useEffect(() => {
_metric.updateKey('page', 1);
loadPage();
}, [drillDownPeriod, period, depsString, metric.metricType, metric.metricOf, metric.viewType, metric.metricValue, metric.startType, metric.metricFormat]);
}, [
drillDownPeriod,
period,
depsString,
metric.metricType,
metric.metricOf,
metric.viewType,
metric.metricValue,
metric.startType,
metric.metricFormat,
inView,
]);
useEffect(loadPage, [_metric.page]);
@ -254,9 +270,11 @@ function WidgetChart(props: Props) {
return <div>Unknown metric type</div>;
};
return (
<div ref={ref}>
<Loader loading={loading} style={{height: `240px`}}>
<div style={{minHeight: 240}}>{renderChart()}</div>
</Loader>
</div>
);
}

View file

@ -1,11 +0,0 @@
import React from 'react';
import MetricTypeDropdown from './';
export default {
title: 'Dashboad/Cards/Form/MetricTypeDropdown',
component: MetricTypeDropdown,
};
const Template = (args: any) => <MetricTypeDropdown {...args} />;
export const Simple = Template.bind({});

View file

@ -1,8 +1,7 @@
import React, { useRef } from 'react';
import React, { useRef, lazy } from 'react';
import cn from 'classnames';
import { ItemMenu, TextEllipsis } from 'UI';
import { useDrag, useDrop } from 'react-dnd';
import WidgetChart from '../WidgetChart';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import { withRouter, RouteComponentProps } from 'react-router-dom';
@ -11,9 +10,10 @@ import TemplateOverlay from './TemplateOverlay';
import AlertButton from './AlertButton';
import stl from './widgetWrapper.module.css';
import { FilterKey } from 'App/types/filter/filterType';
import LazyLoad from 'react-lazyload';
import { TIMESERIES } from "App/constants/card";
const WidgetChart = lazy(() => import('Components/Dashboard/components/WidgetChart'));
interface Props {
className?: string;
widget?: any;
@ -165,7 +165,6 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
)}
</div>
<LazyLoad offset={!isTemplate ? 100 : 600}>
<div className="px-4" onClick={onChartClick}>
<WidgetChart
isPreview={isPreview}
@ -174,7 +173,6 @@ function WidgetWrapper(props: Props & RouteComponentProps) {
isSaved={isSaved}
/>
</div>
</LazyLoad>
</div>
);
}

View file

@ -1,8 +1,7 @@
import React, { useRef } from 'react';
import React, { useRef, lazy } from 'react';
import cn from 'classnames';
import { Card, Tooltip, Button } from 'antd';
import { Card, Tooltip } from 'antd';
import { useDrag, useDrop } from 'react-dnd';
import WidgetChart from '../WidgetChart';
import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import { withRouter, RouteComponentProps } from 'react-router-dom';
@ -10,11 +9,12 @@ import { withSiteId, dashboardMetricDetails } from 'App/routes';
import TemplateOverlay from './TemplateOverlay';
import stl from './widgetWrapper.module.css';
import { FilterKey } from 'App/types/filter/filterType';
import LazyLoad from 'react-lazyload';
import { TIMESERIES } from 'App/constants/card';
import CardMenu from 'Components/Dashboard/components/WidgetWrapper/CardMenu';
import AlertButton from 'Components/Dashboard/components/WidgetWrapper/AlertButton';
const WidgetChart = lazy(() => import('Components/Dashboard/components/WidgetChart'));
interface Props {
className?: string;
widget?: any;
@ -146,7 +146,6 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) {
{addOverlay && <TemplateOverlay onClick={onChartClick} isTemplate={isTemplate} />}
<LazyLoad offset={!isTemplate ? 100 : 600}>
<div className="px-4" onClick={onChartClick}>
<WidgetChart
isPreview={isPreview}
@ -156,7 +155,6 @@ function WidgetWrapperNew(props: Props & RouteComponentProps) {
isSaved={isSaved}
/>
</div>
</LazyLoad>
</Card>
);
}

View file

@ -1,55 +0,0 @@
import { storiesOf } from '@storybook/react';
import Funnel from 'Types/funnel'
import FunnelIssue from 'Types/funnelIssue'
import FunnelList from './FunnelList';
import FunnelItem from './FunnelItem';
import IssueItem from './IssueItem';
import FunnelGraph from './FunnelGraph';
import FunnelHeader from './FunnelHeader';
import FunnelOverview from './FunnelOverview';
import IssueFilter from './IssueFilter';
const funnel = Funnel({
title: 'Sessions from france',
users: 308,
steps: 10,
sessionsCount: 200,
criticalIssues: 5,
missedConversions: 30
})
const list = [funnel, funnel, funnel];
const funnelIssue = FunnelIssue({
type: 'Error',
error: 'Unchecked runtime lasterror the message port closed before a response was received.',
affectedUsers: 132,
conversionImpact: 30,
lostConversions: 200,
})
storiesOf('Funnels', module)
.add('Funnel List', () => (
<FunnelList list={list} />
))
.add('Funnel Item', () => (
<FunnelItem funnel={funnel} />
))
.add('Funnel Header', () => (
<FunnelHeader />
))
.add('Funnel Header', () => (
<FunnelHeader funnel={funnel} />
))
.add('Issue Item', () => (
<IssueItem issue={funnelIssue} />
))
.add('Funnel graph', () => (
<FunnelGraph />
))
.add('Funnel Overview', () => (
<FunnelOverview funnel={funnel} />
))
.add('Funnel IssueFilter', () => (
<IssueFilter />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Onboarding from '.';
storiesOf('Onboarding', module)
.add('Pure', () => (
<Onboarding />
))

View file

@ -68,14 +68,16 @@ function Player(props: IProps) {
const isReady = playerContext.store.get().ready;
const screenWrapper = React.useRef<HTMLDivElement>(null);
const bottomBlockIsActive = !fullscreen && bottomBlock !== NONE;
const [isAttached, setAttached] = React.useState(false);
const isAttached = React.useRef(false);
React.useEffect(() => {
updateLastPlayedSession(sessionId);
const parentElement = findDOMNode(screenWrapper.current) as HTMLDivElement | null; //TODO: good architecture
if (parentElement && !isAttached) {
playerContext.player.attach(parentElement);
setAttached(true);
if (isReady && !isAttached.current) {
const parentElement = findDOMNode(screenWrapper.current) as HTMLDivElement | null; //TODO: good architecture
if (parentElement) {
playerContext.player.attach(parentElement);
isAttached.current = true;
}
}
}, [isReady]);

View file

@ -1,116 +0,0 @@
import { storiesOf } from '@storybook/react';
import { List } from 'immutable';
import PageInsightsPanel from './';
const list = [
{
"alertId": 2,
"projectId": 1,
"name": "new alert",
"description": null,
"active": true,
"threshold": 240,
"detectionMethod": "threshold",
"query": {
"left": "avgPageLoad",
"right": 1.0,
"operator": ">="
},
"createdAt": 1591893324078,
"options": {
"message": [
{
"type": "slack",
"value": "51"
},
],
"LastNotification": 1592929583000,
"renotifyInterval": 120
}
},
{
"alertId": 14,
"projectId": 1,
"name": "alert 19.06",
"description": null,
"active": true,
"threshold": 30,
"detectionMethod": "threshold",
"query": {
"left": "avgPageLoad",
"right": 3000.0,
"operator": ">="
},
"createdAt": 1592579750935,
"options": {
"message": [
{
"type": "slack",
"value": "51"
}
],
"renotifyInterval": 120
}
},
{
"alertId": 15,
"projectId": 1,
"name": "notify every 60min",
"description": null,
"active": true,
"threshold": 30,
"detectionMethod": "threshold",
"query": {
"left": "avgPageLoad",
"right": 1.0,
"operator": ">="
},
"createdAt": 1592848779604,
"options": {
"message": [
{
"type": "slack",
"value": "51"
},
],
"LastNotification": 1599135058000,
"renotifyInterval": 60
}
},
{
"alertId": 21,
"projectId": 1,
"name": "always notify",
"description": null,
"active": true,
"threshold": 30,
"detectionMethod": "threshold",
"query": {
"left": "avgPageLoad",
"right": 1.0,
"operator": ">="
},
"createdAt": 1592849011350,
"options": {
"message": [
{
"type": "slack",
"value": "51"
}
],
"LastNotification": 1599135058000,
"renotifyInterval": 10
}
}
]
const notifications = List([
{ title: 'test', type: 'change', createdAt: 1591893324078, description: 'Lorem ipusm'},
{ title: 'test', type: 'threshold', createdAt: 1591893324078, description: 'Lorem ipusm'},
{ title: 'test', type: 'threshold', createdAt: 1591893324078, description: 'Lorem ipusm'},
{ title: 'test', type: 'threshold', createdAt: 1591893324078, description: 'Lorem ipusm'},
])
storiesOf('PageInsights', module)
.add('Panel', () => (
<PageInsightsPanel />
))

View file

@ -4,7 +4,6 @@ import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
import { fileNameFormat } from 'App/utils';
import { toast } from 'react-toastify';
import { forceVisible } from 'react-lazyload';
const TEXT_GENERATING = 'Generating report...';
const TEXT_SUCCESS = 'Report successfully generated';
@ -38,7 +37,6 @@ export default function withReport<P extends Props>(WrappedComponent: React.Comp
};
const renderPromise = async (): Promise<any> => {
forceVisible();
setRendering(true);
toast.info(TEXT_GENERATING, {
autoClose: false,

View file

@ -0,0 +1,183 @@
/**
* https://github.com/react-component/picker/blob/master/src/generate/luxon.ts
* we don't need entire lib so I'm only using this part for antd datepicker
* */
import { DateTime, Info } from 'luxon';
const weekDayFormatMap = {
zh_CN: 'narrow',
zh_TW: 'narrow',
};
const weekDayLengthMap = {
en_US: 2,
en_GB: 2,
};
/**
* Normalizes part of a moment format string that should
* not be escaped to a luxon compatible format string.
*
* @param part string
* @returns string
*/
const normalizeFormatPart = (part: string): string =>
part
.replace(/Y/g, 'y')
.replace(/D/g, 'd')
.replace(/gg/g, 'kk')
.replace(/Q/g, 'q')
.replace(/([Ww])o/g, 'WW')
.replace(/A/g, 'a');
/**
* Normalizes a moment compatible format string to a luxon compatible format string
*
* @param format string
* @returns string
*/
const normalizeFormat = (format: string): string =>
format
// moment escapes strings contained in brackets
.split(/[[\]]/)
.map((part, index) => {
const shouldEscape = index % 2 > 0;
return shouldEscape ? part : normalizeFormatPart(part);
})
// luxon escapes strings contained in single quotes
.join("'");
/**
* Normalizes language tags used to luxon compatible
* language tags by replacing underscores with hyphen-minus.
*
* @param locale string
* @returns string
*/
const normalizeLocale = (locale: string): string => locale.replace(/_/g, '-');
const generateConfig: GenerateConfig<DateTime> = {
// get
getNow: () => DateTime.local(),
getFixedDate: (string) => DateTime.fromFormat(string, 'yyyy-MM-dd'),
getEndDate: (date) => date.endOf('month'),
getWeekDay: (date) => date.weekday,
getYear: (date) => date.year,
getMonth: (date) => date.month - 1, // getMonth should return 0-11, luxon month returns 1-12
getDate: (date) => date.day,
getHour: (date) => date.hour,
getMinute: (date) => date.minute,
getSecond: (date) => date.second,
getMillisecond: (date) => date.millisecond,
// set
addYear: (date, diff) => date.plus({ year: diff }),
addMonth: (date, diff) => date.plus({ month: diff }),
addDate: (date, diff) => date.plus({ day: diff }),
setYear: (date, year) => date.set({ year }),
setMonth: (date, month) => date.set({ month: month + 1 }), // setMonth month argument is 0-11, luxon months are 1-12
setDate: (date, day) => date.set({ day }),
setHour: (date, hour) => date.set({ hour }),
setMinute: (date, minute) => date.set({ minute }),
setSecond: (date, second) => date.set({ second }),
setMillisecond: (date, milliseconds) => date.set({ millisecond: milliseconds }),
// Compare
isAfter: (date1, date2) => date1 > date2,
isValidate: (date) => date.isValid,
locale: {
getWeekFirstDate: (locale, date) => date.setLocale(normalizeLocale(locale)).startOf('week'),
getWeekFirstDay: (locale) =>
DateTime.local().setLocale(normalizeLocale(locale)).startOf('week').weekday,
getWeek: (locale, date) => date.setLocale(normalizeLocale(locale)).weekNumber,
getShortWeekDays: (locale) => {
const weekdays = Info.weekdays(weekDayFormatMap[locale] || 'short', {
locale: normalizeLocale(locale),
});
const shifted = weekdays.map((weekday) => weekday.slice(0, weekDayLengthMap[locale]));
// getShortWeekDays should return weekday labels starting from Sunday.
// luxon returns them starting from Monday, so we have to shift the results.
shifted.unshift(shifted.pop() as string);
return shifted;
},
getShortMonths: (locale) => Info.months('short', { locale: normalizeLocale(locale) }),
format: (locale, date, format) => {
if (!date || !date.isValid) {
return null;
}
return date.setLocale(normalizeLocale(locale)).toFormat(normalizeFormat(format));
},
parse: (locale, text, formats) => {
for (let i = 0; i < formats.length; i += 1) {
const normalizedFormat = normalizeFormat(formats[i]);
const date = DateTime.fromFormat(text, normalizedFormat, {
locale: normalizeLocale(locale),
});
if (date.isValid) {
return date;
}
}
return null;
},
},
};
export default generateConfig;
export type GenerateConfig<DateType> = {
// Get
getWeekDay: (value: DateType) => number;
getMillisecond: (value: DateType) => number;
getSecond: (value: DateType) => number;
getMinute: (value: DateType) => number;
getHour: (value: DateType) => number;
getDate: (value: DateType) => number;
getMonth: (value: DateType) => number;
getYear: (value: DateType) => number;
getNow: () => DateType;
getFixedDate: (fixed: string) => DateType;
getEndDate: (value: DateType) => DateType;
// Set
addYear: (value: DateType, diff: number) => DateType;
addMonth: (value: DateType, diff: number) => DateType;
addDate: (value: DateType, diff: number) => DateType;
setYear: (value: DateType, year: number) => DateType;
setMonth: (value: DateType, month: number) => DateType;
setDate: (value: DateType, date: number) => DateType;
setHour: (value: DateType, hour: number) => DateType;
setMinute: (value: DateType, minute: number) => DateType;
setSecond: (value: DateType, second: number) => DateType;
setMillisecond: (value: DateType, millisecond: number) => DateType;
// Compare
isAfter: (date1: DateType, date2: DateType) => boolean;
isValidate: (date: DateType) => boolean;
locale: {
getWeekFirstDay: (locale: string) => number;
getWeekFirstDate: (locale: string, value: DateType) => DateType;
getWeek: (locale: string, value: DateType) => number;
format: (locale: string, date: DateType, format: string) => string;
/** Should only return validate date instance */
parse: (locale: string, text: string, formats: string[]) => DateType | null;
/** A proxy for getting locale with moment or other locale library */
getShortWeekDays?: (locale: string) => string[];
/** A proxy for getting locale with moment or other locale library */
getShortMonths?: (locale: string) => string[];
};
};

View file

@ -2,7 +2,7 @@
import { DatePicker } from 'antd';
import { PickerTimeProps } from 'antd/es/time-picker';
import React from 'react';
import luxonGenerateConfig from 'rc-picker/lib/generate/luxon';
import luxonGenerateConfig from './config';
const CustomPicker = DatePicker.generatePicker<DateTime>(luxonGenerateConfig);

View file

@ -2,7 +2,7 @@ import { Button } from 'antd';
import React from 'react';
import DateRangePicker from '@wojtekmaj/react-daterange-picker';
import '@wojtekmaj/react-daterange-picker/dist/DateRangePicker.css';
import 'react-calendar/dist/Calendar.css';
import './ReactCalendar.css';
import { TimePicker } from 'App/components/shared/DatePicker';
import {

View file

@ -0,0 +1,155 @@
.react-calendar {
width: 350px;
max-width: 100%;
background: white;
border: 1px solid #a0a096;
font-family: 'Arial', 'Helvetica', sans-serif;
line-height: 1.125em;
}
.react-calendar--doubleView {
width: 700px;
}
.react-calendar--doubleView .react-calendar__viewContainer {
display: flex;
margin: -0.5em;
}
.react-calendar--doubleView .react-calendar__viewContainer > * {
width: 50%;
margin: 0.5em;
}
.react-calendar,
.react-calendar *,
.react-calendar *:before,
.react-calendar *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.react-calendar button {
margin: 0;
border: 0;
outline: none;
}
.react-calendar button:enabled:hover {
cursor: pointer;
}
.react-calendar__navigation {
display: flex;
height: 44px;
margin-bottom: 1em;
}
.react-calendar__navigation button {
min-width: 44px;
background: none;
}
.react-calendar__navigation button:disabled {
background-color: #f0f0f0;
}
.react-calendar__navigation button:enabled:hover,
.react-calendar__navigation button:enabled:focus {
background-color: #e6e6e6;
}
.react-calendar__month-view__weekdays {
text-align: center;
text-transform: uppercase;
font: inherit;
font-size: 0.75em;
font-weight: bold;
}
.react-calendar__month-view__weekdays__weekday {
padding: 0.5em;
}
.react-calendar__month-view__weekNumbers .react-calendar__tile {
display: flex;
align-items: center;
justify-content: center;
font: inherit;
font-size: 0.75em;
font-weight: bold;
}
.react-calendar__month-view__days__day--weekend {
color: #d10000;
}
.react-calendar__month-view__days__day--neighboringMonth,
.react-calendar__decade-view__years__year--neighboringDecade,
.react-calendar__century-view__decades__decade--neighboringCentury {
color: #757575;
}
.react-calendar__year-view .react-calendar__tile,
.react-calendar__decade-view .react-calendar__tile,
.react-calendar__century-view .react-calendar__tile {
padding: 2em 0.5em;
}
.react-calendar__tile {
max-width: 100%;
padding: 10px 6.6667px;
background: none;
text-align: center;
font: inherit;
font-size: 0.833em;
}
.react-calendar__tile:disabled {
background-color: #f0f0f0;
color: #ababab;
}
.react-calendar__month-view__days__day--neighboringMonth:disabled,
.react-calendar__decade-view__years__year--neighboringDecade:disabled,
.react-calendar__century-view__decades__decade--neighboringCentury:disabled {
color: #cdcdcd;
}
.react-calendar__tile:enabled:hover,
.react-calendar__tile:enabled:focus {
background-color: #e6e6e6;
}
.react-calendar__tile--now {
background: #ffff76;
}
.react-calendar__tile--now:enabled:hover,
.react-calendar__tile--now:enabled:focus {
background: #ffffa9;
}
.react-calendar__tile--hasActive {
background: #76baff;
}
.react-calendar__tile--hasActive:enabled:hover,
.react-calendar__tile--hasActive:enabled:focus {
background: #a9d4ff;
}
.react-calendar__tile--active {
background: #006edc;
color: white;
}
.react-calendar__tile--active:enabled:hover,
.react-calendar__tile--active:enabled:focus {
background: #1087ff;
}
.react-calendar--selectRange .react-calendar__tile--hover {
background-color: #e6e6e6;
}

View file

@ -1,44 +0,0 @@
import React from 'react';
import { Story, Meta } from '@storybook/react';
import GettingStartedModal, { Props } from './GettingStartedModal';
import { Step } from './StepList';
const list: Step[] = [
{
title: '🕵️ Install OpenReplay',
status: 'pending',
description: 'Install OpenReplay on your website or mobile app.',
icon: 'tools',
},
{
title: '🕵️ Identify Users',
status: 'pending',
description: 'Identify users across devices and sessions.',
icon: 'users',
},
{
title: '🕵️ Integrations',
status: 'completed',
description: 'Identify users across devices and sessions.',
icon: 'users',
},
{
title: '🕵️ Invite Team Members',
status: 'ignored',
description: 'Identify users across devices and sessions.',
icon: 'users',
},
];
export default {
title: 'GettingStarted',
component: GettingStartedModal,
} as Meta;
const Template: Story<Props> = (args) => <GettingStartedModal {...args} />;
export const Default = Template.bind({});
Default.args = {
list,
};

View file

@ -1,28 +0,0 @@
import SankeyChart, { SankeyChartData } from './SankeyChart';
import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';
const data: SankeyChartData = {
nodes: [
{ name: 'Home Page' },
{ name: 'Dashboard' },
{ name: 'Preferences' },
{ name: 'Billing' },
],
links: [
{ source: 0, target: 1, value: 100 },
{ source: 1, target: 2, value: 50 },
{ source: 1, target: 3, value: 50 },
{ source: 2, target: 3, value: 10 },
],
};
export default {
title: 'Dashboad/Cards/SankeyChart',
component: SankeyChart,
} as ComponentMeta<typeof SankeyChart>;
const Template: ComponentStory<typeof SankeyChart> = (args: any) => <SankeyChart {...args} />;
export const Simple = Template.bind({});
Simple.args = { data };

View file

@ -1,23 +0,0 @@
import { storiesOf } from '@storybook/react';
import ScatterChart from './ScatterChart';
const data01 = [
{ x: 100, y: 200, z: 200 },
{ x: 120, y: 100, z: 260 },
{ x: 170, y: 300, z: 400 },
{ x: 140, y: 250, z: 280 },
{ x: 150, y: 400, z: 500 },
{ x: 110, y: 280, z: 200 },
];
const data02 = [
{ x: 200, y: 260, z: 240 },
{ x: 240, y: 290, z: 220 },
{ x: 190, y: 290, z: 250 },
{ x: 198, y: 250, z: 210 },
{ x: 180, y: 280, z: 260 },
{ x: 210, y: 220, z: 230 },
];
storiesOf('ScatterChart', module).add('Pure', () => (
<ScatterChart dataFirst={data01} dataSecond={data02} />
));

View file

@ -1,21 +1,10 @@
import { issues_types, types } from 'Types/session/issue';
import { Segmented } from 'antd';
import { Angry, CircleAlert, HandIcon, Skull, WifiOff } from 'lucide-react';
import { Angry, CircleAlert, Skull, WifiOff } from 'lucide-react';
import { observer } from 'mobx-react-lite';
import React from 'react';
import { useStore } from 'App/mstore';
interface Tag {
name: string;
type: string;
icon: string;
}
interface StateProps {
}
type Props = StateProps;
const tagIcons = {
[types.ALL]: undefined,
[types.JS_EXCEPTION]: <CircleAlert size={14} />,
@ -25,7 +14,7 @@ const tagIcons = {
[types.TAP_RAGE]: <Angry size={14} />,
} as Record<string, any>;
const SessionTags: React.FC<Props> = () => {
const SessionTags = () => {
const { projectsStore, sessionStore, searchStore } = useStore();
const total = sessionStore.total;
const platform = projectsStore.active?.platform || '';

View file

@ -1 +1 @@
export { default, TagItem } from './SessionTags';
export { default } from './SessionTags';

View file

@ -1,32 +0,0 @@
import { storiesOf } from '@storybook/react';
import ResultTimings from './ResultTimings';
const timing = [
{ "name": "connectStart", "value": 1602181968963 },
{ "name": "navigationStart", "value": 1602181965923 },
{ "name": "loadEventEnd", "value": 1602181993795 },
{ "name": "domLoading", "value": 1602181971233 },
{ "name": "secureConnectionStart", "value": 1602181969010 },
{ "name": "fetchStart", "value": 1602181965976 },
{ "name": "domContentLoadedEventStart", "value": 1602181980930 },
{ "name": "responseStart", "value": 1602181970432 },
{ "name": "responseEnd", "value": 1602181970763 },
{ "name": "domInteractive", "value": 1602181980930 },
{ "name": "domainLookupEnd", "value": 1602181968963 },
{ "name": "redirectStart", "value": 0 },
{ "name": "requestStart", "value": 1602181969932 },
{ "name": "unloadEventEnd", "value": 0 },
{ "name": "unloadEventStart", "value": 0 },
{ "name": "domComplete", "value": 1602181993794 },
{ "name": "domainLookupStart", "value": 1602181968871 },
{ "name": "loadEventStart", "value": 1602181993794 },
{ "name": "domContentLoadedEventEnd", "value": 1602181982868 },
{ "name": "redirectEnd", "value": 0 },
{ "name": "connectEnd", "value": 1602181969783 }
];
storiesOf('Shared', module)
.add('ResultTimings', () => (
<ResultTimings timing={timing} />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import BackLink from '.';
storiesOf('BackLink', module)
.add('Pure', () => (
<BackLink />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Button from '.';
storiesOf('Button', module)
.add('Pure', () => (
<Button />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Checkbox from '.';
storiesOf('Checkbox', module)
.add('Pure', () => (
<Checkbox />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import CircularLoader from '.';
storiesOf('CircularLoader', module)
.add('Pure', () => (
<CircularLoader />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import CloseButton from '.';
storiesOf('CloseButton', module)
.add('Pure', () => (
<CloseButton />
))

View file

@ -1,14 +0,0 @@
import { storiesOf } from '@storybook/react';
import Icon from '.';
storiesOf('Icon', module)
.add('Pure', () => (
<Icon />
))
.add('Icon', () => (
<Icon name="close" />
))
.add('Icon Size 16', () => (
<Icon name="close" size="16" />
))

View file

@ -1,20 +0,0 @@
import { storiesOf } from '@storybook/react';
import IconButton from '.';
storiesOf('IconButton', module)
.add('Pure', () => (
<IconButton />
))
.add('Icon', () => (
<IconButton icon="cog" />
))
.add('Icon & Label', () => (
<IconButton icon="cog" label="Button" />
))
.add('Plain', () => (
<IconButton icon="cog" label="Button" plain />
))
.add('Primary', () => (
<IconButton icon="cog" label="Button" primary />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import ItemMenu from '.';
storiesOf('ItemMenu', module)
.add('Pure', () => (
<ItemMenu />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Label from '.';
storiesOf('Label', module)
.add('Pure', () => (
<Label />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Link from '.';
storiesOf('Link', module)
.add('Pure', () => (
<Link />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import LinkStyledInput from '.';
storiesOf('LinkStyledInput', module)
.add('Pure', () => (
<LinkStyledInput />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Loader from '.';
storiesOf('Loader', module)
.add('Pure', () => (
<Loader />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Message from '.';
storiesOf('Message', module)
.add('Pure', () => (
<Message />
))

View file

@ -1,14 +0,0 @@
import { storiesOf } from '@storybook/react';
import NoContent from '.';
storiesOf('NoContent', module)
.add('Pure', () => (
<NoContent />
))
.add('Text and icon', () => (
<NoContent icon subtext="this is subtext to be displayed."/>
))
.add('Empty Content', () => (
<NoContent empty icon subtext="this is subtext to be displayed."/>
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Notification from '.';
storiesOf('Notification', module)
.add('Pure', () => (
<Notification />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import PopMenu from '.';
storiesOf('PopMenu', module)
.add('Pure', () => (
<PopMenu />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import SegmentSelection from '.';
storiesOf('SegmentSelection', module)
.add('Pure', () => (
<SegmentSelection />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import SlideModal from '.';
storiesOf('SlideModal', module)
.add('Pure', () => (
<SlideModal />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import SplitButton from './SplitButton';
storiesOf('SplitButton', module)
.add('Pure', () => (
<SplitButton label="Issues" primary icon="plus" />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Tabs from '.';
storiesOf('Tabs', module)
.add('Pure', () => (
<Tabs />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import TagBadge from '.';
storiesOf('TagBadge', module)
.add('Pure', () => (
<TagBadge />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import TagInput from '.';
storiesOf('TagInput', module)
.add('Pure', () => (
<TagInput />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import TagList from '.';
storiesOf('TagList', module)
.add('Pure', () => (
<TagList />
))

View file

@ -1,23 +0,0 @@
import { storiesOf } from '@storybook/react';
import TextEllipsis from '.';
storiesOf('TextEllipsis', module)
.add('Pure', () => (
<TextEllipsis />
))
.add('Normal Text', () => (
<TextEllipsis popupProps={{ wide: 'very'}}>
{'this is test'}
</TextEllipsis>
))
.add('Inverted', () => (
<TextEllipsis popupProps={{ wide: 'very', inverted: true }}>
{'this is test'}
</TextEllipsis>
))
.add('Bigger Text', () => (
<TextEllipsis popupProps={{ wide: 'very'}}>
<div style={{width: '200px', }}>{'this is the biggest text in the application to test the popup content. this is the biggest text in the application to test the popup content.'}</div>
</TextEllipsis>
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import TextLabel from '.';
storiesOf('TextLabel', module)
.add('Pure', () => (
<TextLabel />
))

View file

@ -1,8 +0,0 @@
import { storiesOf } from '@storybook/react';
import Toggler from '.';
storiesOf('Toggler', module)
.add('Pure', () => (
<Toggler />
))

View file

@ -1,90 +0,0 @@
import { storiesOf } from '@storybook/react';
import SideMenuItem from './SideMenuItem';
import { Avatar, ErrorItem, ErrorFrame, ErrorDetails, TimelinePointer } from 'UI';
import Error from 'Types/session/error';
import ErrorStackModel from 'Types/session/errorStack';
const errorStack = ErrorStackModel(
{
"url": "https://staging.openreplay.com/app-1cac32a.js",
"args": [],
"func": "FilterModal._this.onFilterClick",
"line": 75,
"column": 100,
"context": [
[ 70, " });" ],
[ 71, " } else {" ],
[ 72, " props.fetchSession(props.sessionId).then(() => {" ],
[ 73, " const { session } = this.props;" ],
[ 74, " if (!session.sessionId) return; // shouldn't be. On first load component constructed twise somewhy" ],
[ 75, " initPlayer(session, props.jwt);" ],
[ 76, " });" ],
[ 77, " }" ],
[ 78, " }" ],
[ 79, "" ],
[ 80, " componentDidUpdate(prevProps) {" ]
]
}
);
const errors = [
Error({
"sessionId": 2315691667741445,
"messageId": 220546,
"timestamp": 1585335179312,
"errorId": "1_5c3b207b20a36c08c408c4990b9f5cbc",
"projectId": 1,
"source": "js_exception",
"name": "TypeError",
"message": "Cannot read property '0' of undefined",
"payload": {
"mode": "stack",
"stack": [
{
"url": "https://staging.openreplay.com/app-1cac32a.js",
"args": [],
"func": "FilterModal._this.onFilterClick",
"line": 3233,
"column": 62,
"context": []
},
{
"url": "https://staging.openreplay.com/app-1cac32a.js",
"args": [],
"func": "onClick",
"line": 3342,
"column": 25,
"context": []
},
]
},
"status": "unresolved",
"parentErrorId": null
})
]
storiesOf('UI Components', module)
.add('SideMenuItem', () => (
<SideMenuItem title="Menu Label" />
))
.add('SideMenuItem active', () => (
<SideMenuItem title="Menu Label" active />
))
.add('Avatar', () => (
<Avatar />
))
.add('ErrorItem', () => (
<ErrorItem error={errors[0]} />
))
.add('ErrorFrame', () => (
<ErrorFrame stack={errorStack} />
))
.add('ErrorDetails', () => (
<div className="p-4 bg-white">
<ErrorDetails error={errors[0]} />
</div>
))
.add('Timeline POinter', () => (
<TimelinePointer />
))

View file

@ -101,7 +101,7 @@ class SearchStore {
}
edit(instance: Partial<Search>) {
this.instance = new Search(Object.assign(this.instance.toData(), instance));
this.instance = new Search(Object.assign({ ...this.instance }, instance));
this.currentPage = 1;
}

View file

@ -87,7 +87,7 @@ class SearchStoreLive {
}
edit(instance: Partial<Search>) {
this.instance = new Search(Object.assign(this.instance.toData(), instance));
this.instance = new Search(Object.assign({ ...this.instance }, instance));
this.currentPage = 1;
}

View file

@ -105,8 +105,8 @@ export default class Screen {
attach(parentElement: HTMLElement) {
if (this.parentElement) {
this.parentElement = null;
console.warn('BaseScreen: reattaching the screen.');
console.error('!!! web/Screen.ts#108: Tried to reattach the parent element.');
return;
}
parentElement.appendChild(this.screen);

View file

@ -34,11 +34,11 @@ export default class StylesManager {
this.linkLoadingCount++;
this.setLoading(true);
const addSkipAndResolve = (e: any) => {
this.skipCSSLinks.push(value); // watch out
this.skipCSSLinks.push(value);
logger.error('skip node', e)
resolve()
}
timeoutId = setTimeout(addSkipAndResolve, 4000);
timeoutId = setTimeout(() => addSkipAndResolve('by timeout'), 5000);
node.onload = () => {
const doc = this.screen.document;
@ -55,6 +55,7 @@ export default class StylesManager {
this.linkLoadingCount--;
if (this.linkLoadingCount === 0) {
this.setLoading(false)
this.linkLoadPromises = [];
}
});
this.linkLoadPromises.push(promise);

View file

@ -12,10 +12,8 @@
"gen:constants": "node ./scripts/constants.js",
"gen:icons": "node ./scripts/icons.js",
"gen:colors": "node ./scripts/colors.js",
"storybook": "start-storybook -p 6006",
"flow": "flow",
"gen:static": "yarn gen:icons && yarn gen:colors",
"build-storybook": "build-storybook",
"test:ci": "jest --maxWorkers=1 --no-cache --coverage",
"test": "jest --watch",
"cy:open": "cypress open",
@ -52,9 +50,9 @@
"lucide-react": "^0.396.0",
"luxon": "^3.5.0",
"microdiff": "^1.4.0",
"mobx": "^6.3.8",
"mobx": "^6.13.3",
"mobx-persist-store": "^1.1.5",
"mobx-react-lite": "^3.1.6",
"mobx-react-lite": "^4.0.7",
"peerjs": "1.3.2",
"prismjs": "^1.29.0",
"rc-time-picker": "^3.7.3",
@ -66,7 +64,7 @@
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
"react-google-recaptcha": "^2.1.0",
"react-lazyload": "^3.2.0",
"react-intersection-observer": "^9.13.1",
"react-merge-refs": "^2.0.1",
"react-router": "^5.3.3",
"react-router-dom": "^5.3.3",
@ -77,7 +75,7 @@
"recharts": "^2.12.7",
"socket.io-client": "^4.4.1",
"syncod": "^0.0.1",
"virtua": "^0.33.4"
"virtua": "^0.35.1"
},
"devDependencies": {
"@babel/cli": "^7.23.0",
@ -96,15 +94,6 @@
"@babel/runtime": "^7.23.2",
"@jest/globals": "^29.7.0",
"@openreplay/sourcemap-uploader": "^3.0.8",
"@storybook/addon-actions": "^6.5.12",
"@storybook/addon-docs": "^6.5.12",
"@storybook/addon-essentials": "^6.5.12",
"@storybook/addon-interactions": "^6.5.12",
"@storybook/addon-links": "^6.5.12",
"@storybook/builder-webpack5": "^6.5.12",
"@storybook/manager-webpack5": "^6.5.12",
"@storybook/react": "^6.5.12",
"@storybook/testing-library": "^0.0.13",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/luxon": "^3.4.2",
"@types/prismjs": "^1",
@ -161,5 +150,5 @@
"engines": {
"node": ">=10.14.1"
},
"packageManager": "yarn@3.2.1"
"packageManager": "yarn@4.5.0"
}

File diff suppressed because it is too large Load diff

View file

@ -81,7 +81,6 @@ up to date with every new library you use.
| react-dom | MIT | JavaScript |
| react-google-recaptcha | MIT | JavaScript |
| react-json-view | MIT | JavaScript |
| react-lazyload | MIT | JavaScript |
| react-redux | MIT | JavaScript |
| react-router | MIT | JavaScript |
| react-router-dom | MIT | JavaScript |
@ -122,4 +121,5 @@ up to date with every new library you use.
| @wojtekmaj/react-daterange-picker | MIT | JavaScript |
| prismjs | MIT | JavaScript |
| virtua | MIT | JavaScript |
| babel-plugin-prismjs | MIT | JavaScript |
| babel-plugin-prismjs | MIT | JavaScript |
| react-intersection-observer | MIT | JavaScript |

View file

@ -1 +1 @@
nodeLinker: node-modules
nodeLinker: pnpm

View file

@ -335,17 +335,25 @@ export default class App {
this.revID = this.options.revID
this.localStorage = this.options.localStorage ?? window.localStorage
this.sessionStorage = this.options.sessionStorage ?? window.sessionStorage
this.sanitizer = new Sanitizer(this, options)
this.nodes = new Nodes(this.options.node_id, Boolean(options.angularMode))
this.observer = new Observer(this, options)
this.sanitizer = new Sanitizer({ app: this, options })
this.nodes = new Nodes({
node_id: this.options.node_id,
angularMode: Boolean(options.angularMode),
})
this.observer = new Observer({ app: this, options })
this.ticker = new Ticker(this)
this.ticker.attach(() => this.commit())
this.debug = new Logger(this.options.__debug__)
this.session = new Session(this, this.options)
this.attributeSender = new AttributeSender(this, Boolean(this.options.disableStringDict))
this.session = new Session({ app: this, options: this.options })
this.attributeSender = new AttributeSender({
app: this,
isDictDisabled: Boolean(this.options.disableStringDict || this.options.crossdomain?.enabled),
})
this.featureFlags = new FeatureFlags(this)
this.tagWatcher = new TagWatcher(this.sessionStorage, this.debug.error, (tag) => {
this.send(TagTrigger(tag) as Message)
this.tagWatcher = new TagWatcher({
sessionStorage: this.sessionStorage,
errLog: this.debug.error,
onTag: (tag) => this.send(TagTrigger(tag) as Message),
})
this.session.attachUpdateCallback(({ userID, metadata }) => {
if (userID != null) {
@ -895,12 +903,12 @@ export default class App {
const createListener = () =>
target
? createEventListener(target, type, listener, useCapture, this.options.angularMode)
: null
? createEventListener(target, type, listener, useCapture, this.options.angularMode)
: null
const deleteListener = () =>
target
? deleteEventListener(target, type, listener, useCapture, this.options.angularMode)
: null
? deleteEventListener(target, type, listener, useCapture, this.options.angularMode)
: null
this.attachStartCallback(createListener, useSafe)
this.attachStopCallback(deleteListener, useSafe)

View file

@ -9,11 +9,13 @@ export default class Nodes {
private readonly nodeCallbacks: Array<NodeCallback> = []
private readonly elementListeners: Map<number, Array<ElementListener>> = new Map()
private nextNodeId = 0
private readonly node_id: string
private readonly angularMode: boolean
constructor(
private readonly node_id: string,
private readonly angularMode: boolean,
) {}
constructor(params: { node_id: string; angularMode: boolean }) {
this.node_id = params.node_id
this.angularMode = params.angularMode
}
syntheticMode(frameOrder: number) {
const maxSafeNumber = Number.MAX_SAFE_INTEGER
@ -36,7 +38,12 @@ export default class Nodes {
this.nodes.forEach((node) => cb(node))
}
attachNodeListener = (node: Node, type: string, listener: EventListener, useCapture = true): void => {
attachNodeListener = (
node: Node,
type: string,
listener: EventListener,
useCapture = true,
): void => {
const id = this.getID(node)
if (id === undefined) {
return

View file

@ -22,14 +22,16 @@ const attachShadowNativeFn = IN_BROWSER ? Element.prototype.attachShadow : () =>
export default class TopObserver extends Observer {
private readonly options: Options
private readonly iframeOffsets: IFrameOffsets = new IFrameOffsets()
readonly app: App
constructor(app: App, options: Partial<Options>) {
super(app, true)
constructor(params: { app: App; options: Partial<Options> }) {
super(params.app, true)
this.app = params.app
this.options = Object.assign(
{
captureIFrames: true,
},
options,
params.options,
)
// IFrames

View file

@ -23,17 +23,16 @@ export default class Sanitizer {
private readonly obscured: Set<number> = new Set()
private readonly hidden: Set<number> = new Set()
private readonly options: Options
private readonly app: App
constructor(
private readonly app: App,
options: Partial<Options>,
) {
constructor(params: { app: App; options?: Partial<Options> }) {
this.app = params.app
this.options = Object.assign(
{
obscureTextEmails: true,
obscureTextNumbers: false,
},
options,
params.options,
)
}

View file

@ -35,11 +35,12 @@ export default class Session {
private tabId: string
public userInfo: UserInfo
private token: string | undefined
private readonly app: App
private readonly options: Options
constructor(
private readonly app: App,
private readonly options: Options,
) {
constructor(params: { app: App; options: Options }) {
this.app = params.app
this.options = params.options
this.createTabId()
}

View file

@ -17,11 +17,12 @@ export class StringDictionary {
export default class AttributeSender {
private dict = new StringDictionary()
constructor(
private readonly app: App,
private readonly isDictDisabled: boolean,
) {}
private readonly app: App
private readonly isDictDisabled: boolean
constructor(options: { app: App; isDictDisabled: boolean }) {
this.app = options.app
this.isDictDisabled = options.isDictDisabled
}
public sendSetAttribute(id: number, name: string, value: string) {
if (this.isDictDisabled) {

View file

@ -4,12 +4,19 @@ class TagWatcher {
intervals: Record<string, ReturnType<typeof setInterval>> = {}
tags: { id: number; selector: string }[] = []
observer: IntersectionObserver
private readonly sessionStorage: Storage
private readonly errLog: (args: any[]) => void
private readonly onTag: (tag: number) => void
constructor(params: {
sessionStorage: Storage
errLog: (args: any[]) => void
onTag: (tag: number) => void
}) {
this.sessionStorage = params.sessionStorage
this.errLog = params.errLog
this.onTag = params.onTag
constructor(
private readonly sessionStorage: Storage,
private readonly errLog: (args: any[]) => void,
private readonly onTag: (tag: number) => void,
) {
const tags: { id: number; selector: string }[] = JSON.parse(
sessionStorage.getItem(WATCHED_TAGS_KEY) ?? '[]',
)