From 41aefc99d0cdf4834d713d9475a598c22ba29628 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Fri, 19 Aug 2022 14:15:09 -0400 Subject: [PATCH 1/5] refactoring sorting --- .../components/TrackDetailsPanel.vue | 2 +- client/dive-common/components/Viewer.vue | 27 +++++++++++- client/src/BaseAnnotationStore.ts | 35 +++++++++++++-- client/src/BaseFilterControls.ts | 25 ++++++++--- client/src/CameraStore.ts | 43 +++++++++++++++---- client/src/TrackFilterControls.spec.ts | 19 +++++++- client/src/TrackStore.spec.ts | 6 ++- client/src/components/GroupItem.vue | 2 +- client/src/components/TrackList.vue | 7 +-- client/src/use/useEventChart.ts | 12 ++++-- client/src/use/useLineChart.ts | 2 +- 11 files changed, 147 insertions(+), 33 deletions(-) diff --git a/client/dive-common/components/TrackDetailsPanel.vue b/client/dive-common/components/TrackDetailsPanel.vue index c8d866eb7..87f562145 100644 --- a/client/dive-common/components/TrackDetailsPanel.vue +++ b/client/dive-common/components/TrackDetailsPanel.vue @@ -298,7 +298,7 @@ export default defineComponent({ {{ editingGroup.id }} { cameraStore.removeGroups(id); }; + const setTrackType = (id: AnnotationId, newType: string, + confidenceVal?: number, currentType?: string) => { + cameraStore.setTrackType(id, newType, confidenceVal, currentType); + }; + const removeTypes = (id: AnnotationId, types: string[]) => cameraStore.removeTypes(id, types); + const getTracksMerged = (id: AnnotationId) => cameraStore.getTracksMerged(id); const groupFilters = new GroupFilterControls({ sorted: cameraStore.sortedGroups, markChangesPending, remove: removeGroups, + setType: setTrackType, + removeTypes, }); // This context for removal @@ -180,6 +188,8 @@ export default defineComponent({ markChangesPending, lookupGroups: cameraStore.lookupGroups, groupFilterControls: groupFilters, + setType: setTrackType, + removeTypes, }); clientSettingsSetup(trackFilters.allTypes); @@ -251,6 +261,7 @@ export default defineComponent({ enabledTracks: trackFilters.enabledAnnotations, selectedTrackIds: allSelectedIds, typeStyling: trackStyleManager.typeStyling, + getTracksMerged, }); const { eventChartData: groupChartData } = useEventChart({ @@ -262,6 +273,7 @@ export default defineComponent({ } return []; }), + getTracksMerged, }); async function trackSplit(trackId: AnnotationId | null, frame: number) { @@ -538,6 +550,11 @@ export default defineComponent({ const trackStore = cameraStore.camMap.value.get(camera)?.trackStore; const groupStore = cameraStore.camMap.value.get(camera)?.groupStore; if (trackStore && groupStore) { + // We can start sorting if our total tracks are less than 20000 + // If greater we do one sort at the end instead to speed loading. + if (tracks.length < 20000) { + trackStore.setEnableSorting(); + } for (let j = 0; j < tracks.length; j += 1) { if (j % 4000 === 0) { /* Every N tracks, yeild some cycles for other scheduled tasks */ @@ -558,13 +575,19 @@ export default defineComponent({ } } } - progress.loaded = true; - cameraStore.camMap.value.forEach((_cam, key) => { + cameraStore.camMap.value.forEach((cam, key) => { + const { trackStore } = cam; + // Enable Sorting after loading is complete if it isn't enabled already + if (trackStore) { + trackStore.setEnableSorting(); + } + if (!multiCamList.value.includes(key)) { cameraStore.removeCamera(key); removeSaveCamera(key); } }); + progress.loaded = true; // If multiCam add Tools and remove group Tools if (cameraStore.camMap.value.size > 1) { context.unregister({ diff --git a/client/src/BaseAnnotationStore.ts b/client/src/BaseAnnotationStore.ts index a2b90d56a..aae8c87d6 100644 --- a/client/src/BaseAnnotationStore.ts +++ b/client/src/BaseAnnotationStore.ts @@ -2,7 +2,7 @@ import { ref, Ref, computed } from '@vue/composition-api'; import IntervalTree from '@flatten-js/interval-tree'; import type Track from './track'; import type Group from './Group'; -import type { AnnotationId, NotifierFuncParams } from './BaseAnnotation'; +import type { AnnotationId, ConfidencePair, NotifierFuncParams } from './BaseAnnotation'; export type MarkChangesPending = ({ action, @@ -21,6 +21,15 @@ export interface InsertArgs { afterId?: AnnotationId; } +//A subset of Track so copies of full track aren't passed around +export interface SortedAnnotation { + id: AnnotationId; + begin: number; + end: number; + confidencePairs: ConfidencePair[]; + getType: (index?: number) => string; +} + function isTrack(value: Track | Group): value is Track { return (value as Track).features !== undefined; } @@ -55,12 +64,14 @@ export default abstract class BaseAnnotationStore { */ annotationIds: Ref; - sorted: Ref[]>; + sorted: Ref; cameraName: string; private canary: Ref; + enableSorting: Ref; //Sorting is false to start with + constructor({ markChangesPending, cameraName }: { markChangesPending: MarkChangesPending; cameraName: string }) { this.markChangesPending = markChangesPending; @@ -69,10 +80,24 @@ export default abstract class BaseAnnotationStore { this.annotationIds = ref([]); this.intervalTree = new IntervalTree(); this.canary = ref(0); + this.enableSorting = ref(false); this.sorted = computed(() => { this.depend(); + // Prevent sorting when loading data + if (!this.enableSorting.value) { + return []; + } return this.annotationIds.value - .map((trackId) => this.get(trackId)) + .map((trackId) => { + const track = this.get(trackId); + return { + begin: track.begin, + end: track.end, + id: track.id, + confidencePairs: track.confidencePairs, + getType: (index?: number) => ((track.confidencePairs[index || 0][0]) || 'unknown'), + }; + }) .sort((a, b) => a.begin - b.begin); }); } @@ -87,6 +112,10 @@ export default abstract class BaseAnnotationStore { return this.canary.value; } + setEnableSorting() { + this.enableSorting.value = true; + } + get(annotationId: AnnotationId) { const value = this.annotationMap.get(annotationId); if (value === undefined) { diff --git a/client/src/BaseFilterControls.ts b/client/src/BaseFilterControls.ts index 0e0a3ca64..b52ae1f01 100644 --- a/client/src/BaseFilterControls.ts +++ b/client/src/BaseFilterControls.ts @@ -1,7 +1,8 @@ import { ref, computed, Ref, watch, } from '@vue/composition-api'; -import type { AnnotationId } from './BaseAnnotation'; +import type { AnnotationId, ConfidencePair } from './BaseAnnotation'; +import { SortedAnnotation } from './BaseAnnotationStore'; import type Group from './Group'; import type Track from './track'; import { updateSubset } from './utils'; @@ -13,7 +14,7 @@ export const DefaultConfidence = 0.1; * or function. */ export interface AnnotationWithContext { - annotation: Readonly>; + annotation: Readonly; context: { // confidencePair index within annotation that makes this annotation a positive filter result confidencePairIndex: number; @@ -21,9 +22,12 @@ export interface AnnotationWithContext { } export interface FilterControlsParams { - sorted: Ref; + sorted: Ref; markChangesPending: () => void; remove: (id: AnnotationId) => void; + setType: (id: AnnotationId, newType: string, + confidenceVal?: number, currentType?: string) => void; + removeTypes: (id: AnnotationId, types: string[]) => ConfidencePair[]; } export type TrackWithContext = AnnotationWithContext; @@ -59,10 +63,15 @@ export default abstract class BaseFilterControls { private markChangesPending: () => void; /* Hold a reference to the annotationStore */ - sorted: Readonly>; + sorted: Readonly>; remove: (id: AnnotationId) => void; + setType: (id: AnnotationId, newType: string, + confidenceVal?: number, currentType?: string) => void; + + removeTypes: (id: AnnotationId, types: string[]) => ConfidencePair[]; + constructor(params: FilterControlsParams) { this.checkedIDs = ref(params.sorted.value.map((t) => t.id)); @@ -74,6 +83,10 @@ export default abstract class BaseFilterControls { this.remove = params.remove; + this.setType = params.setType; + + this.removeTypes = params.removeTypes; + this.markChangesPending = params.markChangesPending; this.allTypes = computed(() => { @@ -165,7 +178,7 @@ export default abstract class BaseFilterControls { for (let i = 0; i < annotation.confidencePairs.length; i += 1) { const [name, confidenceVal] = annotation.confidencePairs[i]; if (name === currentType) { - annotation.setType(newType, confidenceVal, currentType); + this.setType(annotation.id, newType, confidenceVal, currentType); break; } } @@ -184,7 +197,7 @@ export default abstract class BaseFilterControls { const filteredType = filtered.annotation.getType(filtered.context.confidencePairIndex); if (filteredType && types.includes(filteredType[0])) { //Remove the type from the annotation if multiple types exist - const newConfidencePairs = filtered.annotation.removeTypes(types); + const newConfidencePairs = this.removeTypes(filtered.annotation.id, types); if (newConfidencePairs.length === 0) { this.remove(filtered.annotation.id); } diff --git a/client/src/CameraStore.ts b/client/src/CameraStore.ts index d81b06176..88709d823 100644 --- a/client/src/CameraStore.ts +++ b/client/src/CameraStore.ts @@ -4,8 +4,8 @@ import { import { cloneDeep, uniq } from 'lodash'; import type Track from './track'; import type Group from './Group'; -import { AnnotationId } from './BaseAnnotation'; -import { MarkChangesPending } from './BaseAnnotationStore'; +import { AnnotationId, ConfidencePair } from './BaseAnnotation'; +import { MarkChangesPending, SortedAnnotation } from './BaseAnnotationStore'; import GroupStore from './GroupStore'; import TrackStore from './TrackStore'; @@ -20,9 +20,9 @@ export default class CameraStore { markChangesPending: MarkChangesPending; - sortedTracks: Ref; + sortedTracks: Ref; - sortedGroups: Ref; + sortedGroups: Ref; defaultGroup: [string, number]; @@ -45,10 +45,10 @@ export default class CameraStore { * This allows the full range begin/end for the track across multiple cameras to * be displayed. */ - return uniq(idList).map((id) => this.getTracksMerged(id)); + return uniq(idList).map((id) => this.getTracksMergedForSorted(id)); }); this.sortedGroups = computed(() => { - let list: Group[] = []; + let list: SortedAnnotation[] = []; this.camMap.value.forEach((camera) => { list = list.concat(camera.groupStore.sorted.value); }); @@ -140,6 +140,17 @@ export default class CameraStore { return track; } + getTracksMergedForSorted(trackId: Readonly): SortedAnnotation { + const track = this.getTracksMerged(trackId); + return { + id: track.id, + confidencePairs: track.confidencePairs, + begin: track.begin, + end: track.end, + getType: (index?: number) => (track.confidencePairs[index || 0][0] || 'unknown'), + }; + } + addCamera(cameraName: string) { if (this.camMap.value.get(cameraName) === undefined) { this.camMap.value.set(cameraName, { @@ -224,11 +235,11 @@ export default class CameraStore { } // Update all cameras to have the same track type - setTrackType(id: AnnotationId, type: string) { + setTrackType(id: AnnotationId, newType: string, confidenceVal?: number, currentType?: string) { this.camMap.value.forEach((camera) => { const track = camera.trackStore.getPossible(id); if (track !== undefined) { - track.setType(type); + track.setType(newType, confidenceVal, currentType); } }); } @@ -239,11 +250,25 @@ export default class CameraStore { for (let i = 0; i < annotation.confidencePairs.length; i += 1) { const [name, confidenceVal] = annotation.confidencePairs[i]; if (name === currentType) { - annotation.setType(newType, confidenceVal, currentType); + const track = camera.trackStore.get(annotation.id); + if (track) { + track.setType(newType, confidenceVal, currentType); + } break; } } }); }); } + + removeTypes(id: AnnotationId, types: string[]) { + let resultingTypes: ConfidencePair[] = []; + this.camMap.value.forEach((camera) => { + const track = camera.trackStore.getPossible(id); + if (track !== undefined) { + resultingTypes = track.removeTypes(types); + } + }); + return resultingTypes; + } } diff --git a/client/src/TrackFilterControls.spec.ts b/client/src/TrackFilterControls.spec.ts index 89d9b94ce..b0fe4fa26 100644 --- a/client/src/TrackFilterControls.spec.ts +++ b/client/src/TrackFilterControls.spec.ts @@ -43,11 +43,17 @@ function makeCameraStore() { trackStore.insert(t0); trackStore.insert(t1); trackStore.insert(t2); + trackStore.setEnableSorting(); } return cameraStore; } function makeGroupFilterControls(store: CameraStore) { + const setTrackType = (id: AnnotationId, newType: string, + confidenceVal?: number, currentType?: string) => { + store.setTrackType(id, newType, confidenceVal, currentType); + }; + const removeTypes = (id: AnnotationId, types: string[]) => store.removeTypes(id, types); const remove = (id: AnnotationId) => { store.removeGroups(id); }; @@ -55,21 +61,32 @@ function makeGroupFilterControls(store: CameraStore) { sorted: store.sortedGroups, remove, markChangesPending, + setType: setTrackType, + removeTypes, }); } function makeTrackFilterControls() { const cameraStore = makeCameraStore(); const groupFilterControls = makeGroupFilterControls(cameraStore); + const setTrackType = (id: AnnotationId, newType: string, + confidenceVal?: number, currentType?: string) => { + cameraStore.setTrackType(id, newType, confidenceVal, currentType); + }; + const removeTypes = (id: AnnotationId, types: string[]) => cameraStore.removeTypes(id, types); + const remove = (id: AnnotationId) => { cameraStore.removeTracks(id); }; + return new TrackFilterControls({ sorted: cameraStore.sortedTracks, remove, markChangesPending, groupFilterControls, lookupGroups: cameraStore.lookupGroups, + setType: setTrackType, + removeTypes, }); } @@ -79,7 +96,7 @@ describe('useAnnotationFilters', () => { tf.setConfidenceFilters({ baz: 0.1, bar: 0.2, default: 0.1 }); tf.updateTypeName({ currentType: 'foo', newType: 'baz' }); expect(tf.allTypes.value).toEqual(['baz', 'bar']); - expect(tf.filteredAnnotations.value.filter(({ annotation }) => annotation.getType()[0] === 'baz').length).toBe(2); + expect(tf.filteredAnnotations.value.filter(({ annotation }) => annotation.getType() === 'baz').length).toBe(2); tf.updateTypeName({ currentType: 'baz', newType: 'newtype' }); await Vue.nextTick(); // must wait a tick for confidence to settle when newtype is added. expect(tf.allTypes.value).toEqual(['newtype', 'bar']); diff --git a/client/src/TrackStore.spec.ts b/client/src/TrackStore.spec.ts index 023f25a2f..4385a08a8 100644 --- a/client/src/TrackStore.spec.ts +++ b/client/src/TrackStore.spec.ts @@ -8,16 +8,17 @@ Vue.use(CompositionApi); describe('TrackStore', () => { it('can add and remove tracks', () => { const ts = new TrackStore({ markChangesPending: () => null, cameraName: 'singleCam' }); + ts.setEnableSorting(); const t0 = ts.add(20, 'foo', undefined, ts.getNewId()); const t1 = ts.add(10, 'foo', undefined, ts.getNewId()); expect(Array.from(ts.annotationMap.keys()).length).toBe(2); - expect(ts.sorted.value[0].trackId).toBe(1); + expect(ts.sorted.value[0].id).toBe(1); expect(ts.intervalTree.search([10, 10]).length).toBe(1); expect(ts.intervalTree.search([10, 20]).length).toBe(2); ts.remove(t1.id); expect(Array.from(ts.annotationMap.keys()).length).toBe(1); - expect(ts.sorted.value[0].trackId).toBe(0); + expect(ts.sorted.value[0].id).toBe(0); expect(ts.intervalTree.search([10, 10]).length).toBe(0); expect(ts.intervalTree.search([10, 20]).length).toBe(1); @@ -57,6 +58,7 @@ describe('TrackStore', () => { it('updates a reactive list when member tracks change', async () => { const ts = new TrackStore({ markChangesPending: () => null, cameraName: 'singleCam' }); + ts.setEnableSorting(); const track = ts.add(0, 'foo', undefined, ts.getNewId()); let called = false; diff --git a/client/src/components/GroupItem.vue b/client/src/components/GroupItem.vue index 304736647..bd104a39a 100644 --- a/client/src/components/GroupItem.vue +++ b/client/src/components/GroupItem.vue @@ -104,7 +104,7 @@ export default defineComponent({ { if (item.checkedTrackIds.includes(item.filteredTrack.annotation.id)) { if (count < limit) { - text.push(item.filteredTrack.annotation.trackId.toString()); + text.push(item.filteredTrack.annotation.id.toString()); } tracksDisplayed.push(item.filteredTrack.annotation.id); count += 1; diff --git a/client/src/use/useEventChart.ts b/client/src/use/useEventChart.ts index 9533223c5..438b48513 100644 --- a/client/src/use/useEventChart.ts +++ b/client/src/use/useEventChart.ts @@ -10,6 +10,7 @@ interface EventChartParams { enabledTracks: Readonly>[]>>; selectedTrackIds: Ref; typeStyling: Ref; + getTracksMerged: (id: AnnotationId) => Track; } export interface EventChartData { @@ -23,7 +24,7 @@ export interface EventChartData { } export default function useEventChart({ - enabledTracks, selectedTrackIds, typeStyling, + enabledTracks, selectedTrackIds, typeStyling, getTracksMerged, }: EventChartParams) { const eventChartData = computed(() => { const values = [] as EventChartData[]; @@ -34,9 +35,12 @@ export default function useEventChart({ const { annotation: track } = filtered; const { confidencePairs } = track; let markers: [number, boolean][] = []; - if ('featureIndex' in track) { - markers = track.featureIndex.map((i) => ( - [i, track.features[i].interpolate || false])); + if (selectedTrackIds.value.includes(filtered.annotation.id)) { + const mergedTrack = getTracksMerged(filtered.annotation.id); + if ('featureIndex' in mergedTrack) { + markers = mergedTrack.featureIndex.map((i) => ( + [i, mergedTrack.features[i].interpolate || false])); + } } if (confidencePairs.length) { const trackType = track.getType(filtered.context.confidencePairIndex)[0]; diff --git a/client/src/use/useLineChart.ts b/client/src/use/useLineChart.ts index 7c7d0fa36..091b28d6c 100644 --- a/client/src/use/useLineChart.ts +++ b/client/src/use/useLineChart.ts @@ -48,7 +48,7 @@ export default function useLineChart({ const ibegin = track.begin; const iend = track.end > track.begin ? track.end : track.begin + 1; [totalArr[ibegin], totalArr[iend]] = updateHistogram(ibegin, iend, totalArr); - const trackType = track.getType(filtered.context.confidencePairIndex)[0]; + const trackType = track.getType(filtered.context.confidencePairIndex); const typeArr = histograms.get(trackType) as number[]; [typeArr[ibegin], typeArr[iend]] = updateHistogram(ibegin, iend, typeArr); }); From 08cf7d1833be6d5a9f04e427797738b2d98e0433 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Fri, 19 Aug 2022 15:20:19 -0400 Subject: [PATCH 2/5] fixing issues --- client/src/CameraStore.ts | 11 +++++++++++ client/src/components/GroupList.vue | 3 ++- client/src/provides.ts | 10 ++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/client/src/CameraStore.ts b/client/src/CameraStore.ts index 88709d823..2f6cf9c5e 100644 --- a/client/src/CameraStore.ts +++ b/client/src/CameraStore.ts @@ -271,4 +271,15 @@ export default class CameraStore { }); return resultingTypes; } + + getGroupMemebers(id: AnnotationId) { + let members = {}; + this.camMap.value.forEach((camera) => { + const group = camera.groupStore.get(id); + if (group !== undefined) { + members = group.members; + } + }); + return members; + } } diff --git a/client/src/components/GroupList.vue b/client/src/components/GroupList.vue index 323725c1a..e2d647cd9 100644 --- a/client/src/components/GroupList.vue +++ b/client/src/components/GroupList.vue @@ -81,13 +81,14 @@ export default defineComponent({ function getItemProps(item: typeof virtualListItems.value[number]) { const confidencePair = item.filteredGroup.annotation.getType(); + const members = cameraStore.getGroupMemebers(item.filteredGroup.annotation.id); return { group: item.filteredGroup.annotation, color: typeStylingRef.value.color(confidencePair[0]), selected: item.filteredGroup.annotation.id === selectedId.value, selectedTrackId: selectedTrack.value, secondarySelected: (selectedTrack.value !== null) - ? selectedTrack.value in item.filteredGroup.annotation.members + ? selectedTrack.value in members : false, inputValue: item.checkedTrackIds.includes(item.filteredGroup.annotation.id), }; diff --git a/client/src/provides.ts b/client/src/provides.ts index ab51053a1..0f42e3fee 100644 --- a/client/src/provides.ts +++ b/client/src/provides.ts @@ -250,11 +250,18 @@ const markChangesPending = () => { }; */ function dummyState(): State { const cameraStore = new CameraStore({ markChangesPending }); + const setTrackType = (id: AnnotationId, newType: string, + confidenceVal?: number, currentType?: string) => { + cameraStore.setTrackType(id, newType, confidenceVal, currentType); + }; + const removeTypes = (id: AnnotationId, types: string[]) => cameraStore.removeTypes(id, types); const groupFilterControls = new GroupFilterControls( { sorted: cameraStore.sortedGroups, remove: cameraStore.removeGroups, markChangesPending, + setType: setTrackType, + removeTypes, }, ); const trackFilterControls = new TrackFilterControls({ @@ -263,6 +270,9 @@ function dummyState(): State { markChangesPending, groupFilterControls, lookupGroups: cameraStore.lookupGroups, + setType: setTrackType, + removeTypes, + }); return { annotatorPreferences: ref({ trackTails: { before: 20, after: 10 } }), From a291c46216c05b004b35346caf6773f1c79ad866 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Fri, 19 Aug 2022 15:32:35 -0400 Subject: [PATCH 3/5] fix electron build --- client/dive-common/use/useModeManager.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/dive-common/use/useModeManager.ts b/client/dive-common/use/useModeManager.ts index f0ff54498..e3c63fdd0 100644 --- a/client/dive-common/use/useModeManager.ts +++ b/client/dive-common/use/useModeManager.ts @@ -10,12 +10,12 @@ import { AggregateMediaController } from 'vue-media-annotator/components/annotat import Recipe from 'vue-media-annotator/recipe'; import type { AnnotationId } from 'vue-media-annotator/BaseAnnotation'; import type TrackFilterControls from 'vue-media-annotator/TrackFilterControls'; -import BaseAnnotation from 'vue-media-annotator/BaseAnnotation'; import { usePrompt } from 'dive-common/vue-utilities/prompt-service'; import { clientSettings } from 'dive-common/store/settings'; import GroupFilterControls from 'vue-media-annotator/GroupFilterControls'; import CameraStore from 'vue-media-annotator/CameraStore'; +import { SortedAnnotation } from 'vue-media-annotator/BaseAnnotationStore'; type SupportedFeature = GeoJSON.Feature; @@ -23,7 +23,7 @@ type SupportedFeature = GeoJSON.Feature( +function selectNext( filtered: Readonly[], selected: Readonly, delta = 1, ): AnnotationId | null { if (filtered.length > 0) { @@ -334,7 +334,7 @@ export default function useModeManager({ frame.value, trackType, selectedTrackId.value || undefined, overrideTrackId, - ).trackId; + ).id; selectTrack(newTrackId, true); creating = true; return newTrackId; @@ -684,7 +684,7 @@ export default function useModeManager({ )); handleRemoveTrack(otherTrackIds, true); handleToggleMerge(); - handleSelectTrack(track.trackId, false); + handleSelectTrack(track.id, false); } } From b8623191b838d1807277fd5ee7620891b59cc5d1 Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Fri, 9 Sep 2022 10:32:40 -0400 Subject: [PATCH 4/5] fix type count --- client/src/components/FilterList.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/src/components/FilterList.vue b/client/src/components/FilterList.vue index f4f0494a0..2a0cd9835 100644 --- a/client/src/components/FilterList.vue +++ b/client/src/components/FilterList.vue @@ -131,7 +131,7 @@ export default defineComponent({ const typeCounts = computed(() => filteredTracksRef.value.reduce((acc, filteredTrack) => { const confidencePair = filteredTrack.annotation .getType(filteredTrack.context.confidencePairIndex); - const trackType = confidencePair[0]; + const trackType = confidencePair; acc.set(trackType, (acc.get(trackType) || 0) + 1); return acc; @@ -223,6 +223,7 @@ export default defineComponent({ virtualHeight, virtualTypes, readOnlyMode, + filteredTracksRef, /* methods */ clickDelete, clickEdit, From a4f97e78b4faccc4a060ea10e6173588c835940e Mon Sep 17 00:00:00 2001 From: Bryon Lewis Date: Tue, 13 Sep 2022 12:05:40 -0400 Subject: [PATCH 5/5] debounce finalize import button with disable --- .../platform/desktop/frontend/components/ImportDialog.vue | 6 +++++- client/platform/desktop/frontend/components/Recent.vue | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/client/platform/desktop/frontend/components/ImportDialog.vue b/client/platform/desktop/frontend/components/ImportDialog.vue index cc0174deb..d38a5ddf4 100644 --- a/client/platform/desktop/frontend/components/ImportDialog.vue +++ b/client/platform/desktop/frontend/components/ImportDialog.vue @@ -20,6 +20,10 @@ export default defineComponent({ type: Object as PropType, required: true, }, + disabled: { + type: Boolean, + default: false, + }, }, setup(props) { const argCopy = ref(cloneDeep(props.importData)); @@ -316,7 +320,7 @@ export default defineComponent({ Finish Import diff --git a/client/platform/desktop/frontend/components/Recent.vue b/client/platform/desktop/frontend/components/Recent.vue index 7d5120478..5b9b19910 100644 --- a/client/platform/desktop/frontend/components/Recent.vue +++ b/client/platform/desktop/frontend/components/Recent.vue @@ -47,6 +47,7 @@ export default defineComponent({ const searchText: Ref = ref(''); const stereo = ref(false); const multiCamOpenType: Ref<'image-sequence'|'video'> = ref('image-sequence'); + const importing = ref(false); const { prompt } = usePrompt(); const { error, loading: checkingMedia, request, reset: resetError, @@ -61,6 +62,7 @@ export default defineComponent({ /** Accept args from the dialog, as it may have modified some parts */ async function finalizeImport(args: DesktopMediaImportResponse) { + importing.value = true; await request(async () => { const jsonMeta = await api.finalizeImport(args); pendingImportPayload.value = null; // close dialog @@ -75,6 +77,7 @@ export default defineComponent({ setRecents(recentsMeta); } }); + importing.value = false; } function openMultiCamDialog(args: {stereo: boolean; openType: 'image-sequence' | 'video'}) { @@ -195,6 +198,7 @@ export default defineComponent({ pendingImportPayload, searchText, error, + importing, importMultiCamDialog, headers, upgradedVersion, @@ -220,6 +224,7 @@ export default defineComponent({