diff --git a/.gitignore b/.gitignore
index 8de9cbdf9..87ca34f46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ web/build
web/node_modules
web/coverage
core
+!/web/**/*.ts
\ No newline at end of file
diff --git a/web/src/components/Timeline/ScrollPermission.ts b/web/src/components/Timeline/ScrollPermission.ts
new file mode 100644
index 000000000..c0fe084c9
--- /dev/null
+++ b/web/src/components/Timeline/ScrollPermission.ts
@@ -0,0 +1,4 @@
+export interface ScrollPermission {
+ allowed: boolean;
+ resetAfterSeeked: boolean;
+}
\ No newline at end of file
diff --git a/web/src/components/Timeline/TimelineBlockView.tsx b/web/src/components/Timeline/TimelineBlockView.tsx
index dea13f1a6..420266e52 100644
--- a/web/src/components/Timeline/TimelineBlockView.tsx
+++ b/web/src/components/Timeline/TimelineBlockView.tsx
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { useCallback } from 'preact/hooks';
-import { getColorFromTimelineEvent } from '../../utils/Timeline/timelineEventUtils';
+import { getColorFromTimelineEvent } from '../../utils/tailwind/twTimelineEventUtil';
import { TimelineEventBlock } from './TimelineEventBlock';
interface TimelineBlockViewProps {
@@ -14,7 +14,7 @@ export const TimelineBlockView = ({ block, onClick }: TimelineBlockViewProps) =>
{
+ if (secondEvent.startTime < firstEvent.endTime && secondEvent.startTime > firstEvent.startTime) {
+ return true;
+ }
+ return false;
+};
+
+export const getTimelineEventBlocksFromTimelineEvents = (events: TimelineEvent[], xOffset: number): TimelineEventBlock[] => {
+ const firstEvent = events[0];
+ const firstEventTime = longToDate(firstEvent.start_time);
+ return events
+ .map((e, index) => {
+ const startTime = longToDate(e.start_time);
+ const endTime = e.end_time ? longToDate(e.end_time) : new Date();
+ const seconds = Math.round(Math.abs(endTime.getTime() - startTime.getTime()) / 1000);
+ const positionX = Math.round(Math.abs(startTime.getTime() - firstEventTime.getTime()) / 1000 + xOffset);
+ return {
+ ...e,
+ startTime,
+ endTime,
+ width: seconds,
+ positionX,
+ index,
+ } as TimelineEventBlock;
+ })
+ .reduce((rowMap, current) => {
+ for (let i = 0; i < rowMap.length; i++) {
+ const row = rowMap[i] ?? [];
+ const lastItem = row[row.length - 1];
+ if (lastItem) {
+ const isOverlap = checkEventForOverlap(lastItem, current);
+ if (isOverlap) {
+ continue;
+ }
+ }
+ rowMap[i] = [...row, current];
+ return rowMap;
+ }
+ rowMap.push([current]);
+ return rowMap;
+ }, [] as TimelineEventBlock[][])
+ .flatMap((rows, rowPosition) => {
+ rows.forEach((eventBlock) => {
+ const OFFSET_DISTANCE_IN_PIXELS = 10;
+ eventBlock.yOffset = OFFSET_DISTANCE_IN_PIXELS * rowPosition;
+ });
+ return rows;
+ })
+ .sort((a, b) => a.startTime.getTime() - b.startTime.getTime());
+}
+
+export const findLargestYOffsetInBlocks = (blocks: TimelineEventBlock[]): number => {
+ return blocks.reduce((largestYOffset, current) => {
+ if (current.yOffset > largestYOffset) {
+ return current.yOffset
+ }
+ return largestYOffset;
+ }, 0)
+};
+
+export const getTimelineWidthFromBlocks = (blocks: TimelineEventBlock[], offset: number): number => {
+ const firstBlock = blocks[0];
+ if (firstBlock) {
+ const startTimeEpoch = firstBlock.startTime.getTime();
+ const endTimeEpoch = Date.now();
+ const timelineDurationLong = epochToLong(endTimeEpoch - startTimeEpoch);
+ return timelineDurationLong + offset * 2
+ }
+}
diff --git a/web/src/utils/dateUtil.ts b/web/src/utils/dateUtil.ts
new file mode 100644
index 000000000..51c9bcaa3
--- /dev/null
+++ b/web/src/utils/dateUtil.ts
@@ -0,0 +1,16 @@
+export const longToDate = (long: number): Date => new Date(long * 1000);
+export const epochToLong = (date: number): number => date / 1000;
+export const dateToLong = (date: Date): number => epochToLong(date.getTime());
+
+const getDateTimeYesterday = (dateTime: Date): Date => {
+ const twentyFourHoursInMilliseconds = 24 * 60 * 60 * 1000;
+ return new Date(dateTime.getTime() - twentyFourHoursInMilliseconds);
+}
+
+const getNowYesterday = (): Date => {
+ return getDateTimeYesterday(new Date());
+}
+
+export const getNowYesterdayInLong = (): number => {
+ return dateToLong(getNowYesterday());
+};
diff --git a/web/src/utils/objectUtils.ts b/web/src/utils/objectUtils.ts
new file mode 100644
index 000000000..01aab7839
--- /dev/null
+++ b/web/src/utils/objectUtils.ts
@@ -0,0 +1 @@
+export const isNullOrUndefined = (object?: unknown): boolean => object === null || object === undefined;
\ No newline at end of file
diff --git a/web/src/utils/tailwind/twTimelineEventUtil.ts b/web/src/utils/tailwind/twTimelineEventUtil.ts
new file mode 100644
index 000000000..c8bc6628b
--- /dev/null
+++ b/web/src/utils/tailwind/twTimelineEventUtil.ts
@@ -0,0 +1,15 @@
+import { TimelineEvent } from "../../components/Timeline/TimelineEvent";
+
+export const getColorFromTimelineEvent = (event: TimelineEvent) => {
+ const { label } = event;
+ if (label === 'car') {
+ return 'bg-red-400';
+ } else if (label === 'person') {
+ return 'bg-blue-400';
+ } else if (label === 'dog') {
+ return 'bg-green-400';
+ }
+
+ // unknown label
+ return 'bg-gray-400';
+};
\ No newline at end of file
diff --git a/web/src/utils/windowUtils.ts b/web/src/utils/windowUtils.ts
new file mode 100644
index 000000000..fc12b0096
--- /dev/null
+++ b/web/src/utils/windowUtils.ts
@@ -0,0 +1,3 @@
+export const convertRemToPixels = (rem: number): number => {
+ return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
+}
diff --git a/web/tailwind.config.js b/web/tailwind.config.js
index e4ecad73c..774af9f06 100644
--- a/web/tailwind.config.js
+++ b/web/tailwind.config.js
@@ -1,5 +1,5 @@
module.exports = {
- purge: ['./public/**/*.html', './src/**/*.jsx', './src/**/*.tsx'],
+ purge: ['./public/**/*.html', './src/**/*.{jsx,tsx}', './src/utils/tailwind/*.{jsx,tsx,js,ts}'],
darkMode: 'class',
theme: {
extend: {