perf: Various minor timeline performance improvements (#1952)
- Closes #1930
This commit is contained in:
@@ -483,7 +483,6 @@ export class AdvancedCameraCardTimelineCore extends LitElement {
|
|||||||
if (panMode === 'seek') {
|
if (panMode === 'seek') {
|
||||||
newResults = results
|
newResults = results
|
||||||
.clone()
|
.clone()
|
||||||
.resetSelectedResult()
|
|
||||||
.selectBestResult(
|
.selectBestResult(
|
||||||
(mediaArray) => findBestMediaIndex(mediaArray, targetTime, view?.camera),
|
(mediaArray) => findBestMediaIndex(mediaArray, targetTime, view?.camera),
|
||||||
{
|
{
|
||||||
@@ -494,7 +493,6 @@ export class AdvancedCameraCardTimelineCore extends LitElement {
|
|||||||
} else if (panMode === 'seek-in-camera') {
|
} else if (panMode === 'seek-in-camera') {
|
||||||
newResults = results
|
newResults = results
|
||||||
.clone()
|
.clone()
|
||||||
.resetSelectedResult()
|
|
||||||
.selectBestResult((mediaArray) => findBestMediaIndex(mediaArray, targetTime), {
|
.selectBestResult((mediaArray) => findBestMediaIndex(mediaArray, targetTime), {
|
||||||
cameraID: view.camera,
|
cameraID: view.camera,
|
||||||
})
|
})
|
||||||
@@ -683,6 +681,9 @@ export class AdvancedCameraCardTimelineCore extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const mediaQuery = this._createMediaQueries(queryType);
|
const mediaQuery = this._createMediaQueries(queryType);
|
||||||
|
if (!mediaQuery || this._alreadyHasAcceptableMediaQuery(mediaQuery)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await this.viewManagerEpoch?.manager.setViewByParametersWithExistingQuery({
|
await this.viewManagerEpoch?.manager.setViewByParametersWithExistingQuery({
|
||||||
params: {
|
params: {
|
||||||
@@ -1088,7 +1089,7 @@ export class AdvancedCameraCardTimelineCore extends LitElement {
|
|||||||
!!this.cameraManager &&
|
!!this.cameraManager &&
|
||||||
!!currentQueries &&
|
!!currentQueries &&
|
||||||
!!currentResultTimestamp &&
|
!!currentResultTimestamp &&
|
||||||
isEqual(currentQueries, freshMediaQuery.getQueries()) &&
|
!!view?.query?.isSupersetOf(freshMediaQuery) &&
|
||||||
this.cameraManager.areMediaQueriesResultsFresh<MediaQuery>(
|
this.cameraManager.areMediaQueriesResultsFresh<MediaQuery>(
|
||||||
currentQueries,
|
currentQueries,
|
||||||
currentResultTimestamp,
|
currentResultTimestamp,
|
||||||
@@ -1179,7 +1180,9 @@ export class AdvancedCameraCardTimelineCore extends LitElement {
|
|||||||
this._timelineSource &&
|
this._timelineSource &&
|
||||||
this._refTimeline.value &&
|
this._refTimeline.value &&
|
||||||
this.timelineConfig &&
|
this.timelineConfig &&
|
||||||
(changedProperties.has('timelineConfig') || changedProperties.has('cameraIDs'))
|
(!this._timeline ||
|
||||||
|
changedProperties.has('timelineConfig') ||
|
||||||
|
changedProperties.has('cameraIDs'))
|
||||||
) {
|
) {
|
||||||
if (this._timeline) {
|
if (this._timeline) {
|
||||||
this._destroy();
|
this._destroy();
|
||||||
|
|||||||
@@ -59,6 +59,44 @@ class MediaQueriesBase<T extends MediaQuery> {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isSupersetOf(that: MediaQueries): boolean {
|
||||||
|
// Queries are typically a single item, so quadratic complexity here is
|
||||||
|
// likely still a lot better than going to the network for a new set of
|
||||||
|
// query results.
|
||||||
|
for (const thatQuery of that.getQueries() ?? []) {
|
||||||
|
let haveMatch = false;
|
||||||
|
for (const thisQuery of this._queries ?? []) {
|
||||||
|
// Compare the query except the times, and then separately compare the
|
||||||
|
// times taking into account whether source time is larger than target
|
||||||
|
// time.
|
||||||
|
if (
|
||||||
|
isEqual(
|
||||||
|
{
|
||||||
|
...thisQuery,
|
||||||
|
end: null,
|
||||||
|
start: null,
|
||||||
|
},
|
||||||
|
{ ...thatQuery, end: null, start: null },
|
||||||
|
) &&
|
||||||
|
((!thisQuery.start && !thatQuery.start) ||
|
||||||
|
(thisQuery.start &&
|
||||||
|
thatQuery.start &&
|
||||||
|
thisQuery.start <= thatQuery.start)) &&
|
||||||
|
((!thisQuery.end && !thatQuery.end) ||
|
||||||
|
(thisQuery.end && thatQuery.end && thisQuery.end >= thatQuery.end))
|
||||||
|
) {
|
||||||
|
haveMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!haveMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventMediaQueries extends MediaQueriesBase<EventQuery> {
|
export class EventMediaQueries extends MediaQueriesBase<EventQuery> {
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ describe('dispatchViewContextChangeEvent', () => {
|
|||||||
expect(results.selectIndex(0)).toEqual(results);
|
expect(results.selectIndex(0)).toEqual(results);
|
||||||
expect(results.getSelectedResult()).toBeNull();
|
expect(results.getSelectedResult()).toBeNull();
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
expect(results.selectResultIfFound((_media: ViewMedia) => true)).toEqual(results);
|
expect(results.selectResultIfFound((_media: ViewMedia) => true)).toEqual(results);
|
||||||
expect(results.getSelectedResult()).toBeNull();
|
expect(results.getSelectedResult()).toBeNull();
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
expect(results.selectBestResult((_media: ViewMedia[]) => null)).toEqual(results);
|
expect(results.selectBestResult((_media: ViewMedia[]) => null)).toEqual(results);
|
||||||
expect(results.getSelectedResult()).toBeNull();
|
expect(results.getSelectedResult()).toBeNull();
|
||||||
expect(results.getMultipleSelectedResults()).toEqual([]);
|
expect(results.getMultipleSelectedResults()).toEqual([]);
|
||||||
|
|||||||
@@ -96,6 +96,170 @@ describe('EventMediaQueries', () => {
|
|||||||
newCameraIDs,
|
newCameraIDs,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('should determine when queries are a superset', () => {
|
||||||
|
it('should return true with itself', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_1)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true with an identical but shorter query', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T01:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-07T23:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false with an identical but longer query', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-06T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false with a non-matching query', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['DIFFERENT']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true with a matching query where the source has multiple', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['kitchen']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false with a matching query where the target has multiple', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['kitchen']),
|
||||||
|
start: new Date('2025-03-07T00:00:00.000Z'),
|
||||||
|
end: new Date('2025-03-08T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when queries do not have start or end', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when target has no queries', () => {
|
||||||
|
const queries_1 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const queries_2 = new EventMediaQueries();
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false when source has no queries', () => {
|
||||||
|
const queries_1 = new EventMediaQueries();
|
||||||
|
const queries_2 = new EventMediaQueries([
|
||||||
|
{
|
||||||
|
type: QueryType.Event,
|
||||||
|
cameraIDs: new Set(['office']),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(queries_1.isSupersetOf(queries_2)).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('RecordingMediaQueries', () => {
|
describe('RecordingMediaQueries', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user