@sprucelabs/spruce-calendar-components 25.6.4 → 25.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,11 +2,17 @@ import { SpruceSchemas } from '@sprucelabs/calendar-utils';
2
2
  import { ToolBeltViewController } from '@sprucelabs/heartwood-view-controllers';
3
3
  import { ViewFixture } from '@sprucelabs/spruce-test-fixtures';
4
4
  import CalendarViewController from '../../calendar/Calendar.vc';
5
+ import CalendarEventManager, { CalendarEventManagerOptions, EventManagerCalendarVc } from '../../calendar/CalendarEventManager';
6
+ import { PeopleManagerCalendarVc } from '../../calendar/CalendarPeopleManager';
5
7
  import { GetScheduleOptions, RemoteEventStore } from '../../stores/RemoteEventStore';
6
8
  import { Calendar, CalendarToolBeltStateMachine, UpdateEvent } from '../../types/calendar.types';
7
9
  import SpyEventManager from './SpyEventManager';
8
10
  export default class CalendarToolBeltStateMachineTestFactory {
9
- static StateMachine(views: ViewFixture): Promise<{
11
+ static StateMachine(views: ViewFixture, options?: {
12
+ calendarVc?: EventManagerCalendarVc & PeopleManagerCalendarVc;
13
+ shouldUpdateCalendarOnContextUpdates?: boolean;
14
+ EventManagerClass?: new (options: CalendarEventManagerOptions) => CalendarEventManager;
15
+ }): Promise<{
10
16
  stateMachine: CalendarToolBeltStateMachine;
11
17
  toolBeltVc: ToolBeltViewController;
12
18
  calendarVc: CalendarViewController;
@@ -14,7 +14,8 @@ const draftGenerator_1 = __importDefault(require("../../utilities/draftGenerator
14
14
  const SpyEventManager_1 = __importDefault(require("./SpyEventManager"));
15
15
  const SpyPeopleManager_1 = __importDefault(require("./SpyPeopleManager"));
16
16
  class CalendarToolBeltStateMachineTestFactory {
17
- static async StateMachine(views) {
17
+ static async StateMachine(views, options) {
18
+ const { EventManagerClass, calendarVc: passedCalendarVc, shouldUpdateCalendarOnContextUpdates = true, } = options !== null && options !== void 0 ? options : {};
18
19
  const factory = views.getFactory();
19
20
  if (!factory.hasController('calendar.calendar')) {
20
21
  factory.mixinControllers({
@@ -24,7 +25,7 @@ class CalendarToolBeltStateMachineTestFactory {
24
25
  }
25
26
  const toolBeltVc = views.Controller('toolBelt', {});
26
27
  const event = heartwood_view_controllers_1.calendarSeeder.generateEventValues();
27
- const calendarVc = views.Controller('calendar.calendar', {
28
+ const calendarVc = passedCalendarVc !== null && passedCalendarVc !== void 0 ? passedCalendarVc : views.Controller('calendar.calendar', {
28
29
  onAddDraftEvent: () => { },
29
30
  onEventSwapped(draft, saved) {
30
31
  return events.silentlySwapEvent(draft.id, saved);
@@ -35,13 +36,12 @@ class CalendarToolBeltStateMachineTestFactory {
35
36
  vcFactory: views.getFactory(),
36
37
  toolBeltVc,
37
38
  connectToApi: async () => spruce_test_fixtures_1.fake.getClient(),
38
- context: Object.assign(Object.assign({}, views.getRouter().buildLoadOptions()), { event,
39
- calendarVc, cancelEvent: () => { } }),
39
+ context: Object.assign(Object.assign({}, views.getRouter().buildLoadOptions()), { event, calendarVc: calendarVc, cancelEvent: () => { } }),
40
40
  });
41
41
  const connectToApi = async () => spruce_test_fixtures_1.fake.getClient();
42
42
  const preferences = new FakeRemotePreferences({ connectToApi });
43
43
  const remoteEventsStore = new FakeRemoteEventStore();
44
- const events = new SpyEventManager_1.default({
44
+ const events = new (EventManagerClass !== null && EventManagerClass !== void 0 ? EventManagerClass : SpyEventManager_1.default)({
45
45
  calendarVc,
46
46
  sm,
47
47
  dates: calendar_utils_1.dateUtil,
@@ -63,9 +63,13 @@ class CalendarToolBeltStateMachineTestFactory {
63
63
  getVisibleEvents: () => [],
64
64
  });
65
65
  await sm.updateContext({ events, people });
66
- await sm.on('did-update-context', () => {
67
- calendarVc.updateEvent(event.id, Object.assign({}, sm.getContext().event));
68
- });
66
+ if (shouldUpdateCalendarOnContextUpdates) {
67
+ // await sm.on('did-update-context', () => {
68
+ // calendarVc.updateEvent(event.id, {
69
+ // ...sm.getContext().event,
70
+ // })
71
+ // })
72
+ }
69
73
  //@ts-ignore
70
74
  return { stateMachine: sm, toolBeltVc, calendarVc, events };
71
75
  }
@@ -7,7 +7,8 @@ export default class SpyEventManager extends CalendarEventManager {
7
7
  wereShiftsRefreshed: boolean;
8
8
  getShouldIgnoreNextContextUpdate(): boolean;
9
9
  constructor(options: SpyEventManagerOptions);
10
- protected refreshShifts(): void;
10
+ refreshShifts(): void;
11
+ generateShifts(): import("@sprucelabs/spruce-core-schemas").SpruceSchemas.HeartwoodViewControllers.v2021_02_11.CalendarShift[];
11
12
  getRemoteEventsStore(): SpyRemoteEventStore;
12
13
  getStateMachine(): import("../../types/calendar.types").CalendarToolBeltStateMachine<CalendarToolBeltContext>;
13
14
  getPreferences(): SpyRemotePreferencesStore;
@@ -16,6 +16,9 @@ class SpyEventManager extends CalendarEventManager_1.default {
16
16
  this.wereShiftsRefreshed = true;
17
17
  return super.refreshShifts();
18
18
  }
19
+ generateShifts() {
20
+ return super.generateShifts();
21
+ }
19
22
  getRemoteEventsStore() {
20
23
  return this.events;
21
24
  }
@@ -20,14 +20,19 @@ export default class CalendarEventManager {
20
20
  protected shouldUpdateAllEventsGoingForward?: boolean;
21
21
  protected prefs: RemotePreferencesStore;
22
22
  private calendars;
23
- private shouldUpdateContextOnNextSave;
24
23
  private isLoaded;
25
24
  protected updateContext?: UpdateCalendarToolBeltContextHandler<CalendarToolBeltContext>;
25
+ private updateOperations;
26
+ private isRunningUpdateQueue;
27
+ private updateQueuePromise?;
26
28
  protected get calendarIds(): string[];
27
29
  protected get visibleCalendarIds(): string[];
28
30
  constructor(options: CalendarEventManagerOptions);
29
31
  replaceEventsInRange(events: CalendarEvent[], startMs: number, endMs: number): void;
30
32
  protected refreshShifts(): void;
33
+ private doShiftsMatchWhatIsInCalendar;
34
+ private generateShiftKeys;
35
+ protected generateShifts(): import("@sprucelabs/calendar-utils").SpruceSchemas.HeartwoodViewControllers.v2021_02_11.CalendarShift[];
31
36
  private isCalendarSelected;
32
37
  private doesCalenderExist;
33
38
  getEvents(): CalendarEvent[];
@@ -49,6 +54,9 @@ export default class CalendarEventManager {
49
54
  makeCalendarVisible(calendarId: string): Promise<void>;
50
55
  makeCalendarHidden(calendarId: string): Promise<void>;
51
56
  updateEvent(id: string, updates: UpdateEventOptions): void;
57
+ waitForPendingOperations(): Promise<void>;
58
+ private startUpdateQueue;
59
+ private _updateEvent;
52
60
  private calculateDifferences;
53
61
  loadEvents(startDate: number, endDate: number, peopleIds: string[]): Promise<import("@sprucelabs/calendar-utils").SpruceSchemas.HeartwoodViewControllers.v2021_02_11.CalendarEvent[]>;
54
62
  setCalendarVisibility(calendarId: string, shouldBeVisible: boolean): Promise<void>;
@@ -95,7 +103,7 @@ export default class CalendarEventManager {
95
103
  setCurrentDate(dateMs: number): Promise<void>;
96
104
  private handleDidUpdateContext;
97
105
  protected saveEvent(event: SavedEvent): Promise<void>;
98
- private clearPersistRepeatingOptions;
106
+ private clearRepeatingStrategyOptions;
99
107
  getIsLoaded(): boolean;
100
108
  hasEvent(id: string): boolean;
101
109
  optionallyAskForUpdateRepeatingStrategy(event: UpdateEvent, action?: RepeatingUpdateAction): Promise<boolean>;
@@ -34,8 +34,9 @@ class CalendarEventManager {
34
34
  this.hasVcForEventTypeBeenLoaded = {};
35
35
  this.shouldIgnoreNextContextUpdate = false;
36
36
  this.calendars = [];
37
- this.shouldUpdateContextOnNextSave = true;
38
37
  this.isLoaded = false;
38
+ this.updateOperations = [];
39
+ this.isRunningUpdateQueue = false;
39
40
  const { calendarVc, events, remoteVc, sm, askForUpdateStrategy, preferences, } = (0, schema_1.assertOptions)(options, [
40
41
  'calendarVc',
41
42
  'events',
@@ -60,9 +61,29 @@ class CalendarEventManager {
60
61
  this.refreshShifts();
61
62
  }
62
63
  refreshShifts() {
64
+ const shifts = this.generateShifts();
65
+ const areSame = this.doShiftsMatchWhatIsInCalendar(shifts);
66
+ if (areSame) {
67
+ return;
68
+ }
69
+ this.calendarVc.setShifts([...shifts]);
70
+ }
71
+ doShiftsMatchWhatIsInCalendar(shifts) {
72
+ var _a;
73
+ const currentShifts = this.generateShiftKeys((_a = this.calendarVc.getShifts()) !== null && _a !== void 0 ? _a : []);
74
+ const newShifts = this.generateShiftKeys(shifts);
75
+ const areShiftsTheSame = newShifts === currentShifts;
76
+ return areShiftsTheSame;
77
+ }
78
+ generateShiftKeys(shifts) {
79
+ return shifts
80
+ .map((s) => `${s.id}-${s.startDateTimeMs}-${s.endDateTimeMs}`)
81
+ .join('-');
82
+ }
83
+ generateShifts() {
63
84
  const inclusiveEvents = this.allEvents.filter((e) => this.inclusiveCalendarIds.indexOf(e.calendarId) > -1);
64
85
  const shifts = calendarShiftGenerator_1.default.generateFromEvents(inclusiveEvents);
65
- this.calendarVc.setShifts([...shifts]);
86
+ return shifts;
66
87
  }
67
88
  isCalendarSelected(calendarId) {
68
89
  return this.visibleCalendarIds.indexOf(calendarId) > -1;
@@ -159,7 +180,7 @@ class CalendarEventManager {
159
180
  console.error((_a = err.stack) !== null && _a !== void 0 ? _a : err.message);
160
181
  this.calendarVc.addEvent(Object.assign(Object.assign({}, event), { error: err }));
161
182
  }
162
- this.clearPersistRepeatingOptions();
183
+ this.clearRepeatingStrategyOptions();
163
184
  }
164
185
  async makeCalendarVisible(calendarId) {
165
186
  this.assertValidCalendarId(calendarId);
@@ -176,27 +197,54 @@ class CalendarEventManager {
176
197
  await this.prefs.setVisibleCalendarIds(visibleCalendarIds);
177
198
  }
178
199
  updateEvent(id, updates) {
179
- var _a;
200
+ const operation = new UpdateOperation({
201
+ id,
202
+ updates,
203
+ onComplete: this._updateEvent.bind(this),
204
+ });
205
+ this.updateOperations.push(operation);
206
+ this.clearRepeatingStrategyOptions();
207
+ this.updateQueuePromise = this.startUpdateQueue();
208
+ }
209
+ async waitForPendingOperations() {
210
+ await this.updateQueuePromise;
211
+ }
212
+ async startUpdateQueue() {
213
+ if (this.isRunningUpdateQueue) {
214
+ return;
215
+ }
216
+ this.isRunningUpdateQueue = true;
217
+ let next;
218
+ while ((next = this.updateOperations.shift())) {
219
+ await next.execute();
220
+ }
221
+ this.isRunningUpdateQueue = false;
222
+ }
223
+ async _updateEvent(id, updates) {
224
+ var _a, _b;
180
225
  const { shouldPersist } = updates, rest = __rest(updates, ["shouldPersist"]);
181
226
  const { isTheSame, isTheSameWithoutBusy } = this.calculateDifferences(id, updates);
182
227
  if (isTheSame) {
183
228
  return;
184
229
  }
185
- this.calendarVc.updateEvent(id, Object.assign(Object.assign({}, rest), { shouldPersist }));
230
+ try {
231
+ this.calendarVc.updateEvent(id, Object.assign(Object.assign({}, rest), { shouldPersist }));
232
+ }
233
+ catch (err) {
234
+ console.error((_a = err.stack) !== null && _a !== void 0 ? _a : err.message);
235
+ return;
236
+ }
186
237
  const idx = this.allEvents.findIndex((e) => e.id === id);
187
238
  this.allEvents[idx] = Object.assign(Object.assign({}, this.allEvents[idx]), rest);
188
239
  if (isTheSameWithoutBusy) {
189
240
  return;
190
241
  }
191
242
  this.refreshShifts();
192
- if (this.shouldUpdateContextOnNextSave &&
193
- ((_a = this.sm.getContext().event) === null || _a === void 0 ? void 0 : _a.id) === id) {
243
+ if (((_b = this.sm.getContext().event) === null || _b === void 0 ? void 0 : _b.id) === id) {
194
244
  this.shouldIgnoreNextContextUpdate = true;
195
- void this.updateEventInContext(rest).then(() => {
196
- this.shouldIgnoreNextContextUpdate = false;
197
- });
245
+ await this.updateEventInContext(rest);
246
+ this.shouldIgnoreNextContextUpdate = false;
198
247
  }
199
- this.shouldUpdateContextOnNextSave = true;
200
248
  }
201
249
  calculateDifferences(id, updates) {
202
250
  const match = this.allEvents.find((e) => e.id === id);
@@ -335,7 +383,6 @@ class CalendarEventManager {
335
383
  }
336
384
  const { event } = this.sm.getContext();
337
385
  if (event === null || event === void 0 ? void 0 : event.id) {
338
- this.shouldUpdateContextOnNextSave = false;
339
386
  await this.saveEvent(event);
340
387
  }
341
388
  }
@@ -347,9 +394,8 @@ class CalendarEventManager {
347
394
  catch (err) {
348
395
  console.error('Updating event in context from root failed because:\n\n', err);
349
396
  }
350
- this.clearPersistRepeatingOptions();
351
397
  }
352
- clearPersistRepeatingOptions() {
398
+ clearRepeatingStrategyOptions() {
353
399
  this.dateToUpdate = undefined;
354
400
  this.shouldUpdateAllEventsGoingForward = undefined;
355
401
  }
@@ -387,3 +433,14 @@ class CalendarEventManager {
387
433
  }
388
434
  }
389
435
  exports.default = CalendarEventManager;
436
+ class UpdateOperation {
437
+ constructor(options) {
438
+ const { id, updates, onComplete } = options;
439
+ this.id = id;
440
+ this.updates = updates;
441
+ this.onComplete = onComplete;
442
+ }
443
+ async execute() {
444
+ await this.onComplete(this.id, this.updates);
445
+ }
446
+ }
@@ -2,11 +2,17 @@ import { SpruceSchemas } from '@sprucelabs/calendar-utils';
2
2
  import { ToolBeltViewController } from '@sprucelabs/heartwood-view-controllers';
3
3
  import { ViewFixture } from '@sprucelabs/spruce-test-fixtures';
4
4
  import CalendarViewController from '../../calendar/Calendar.vc';
5
+ import CalendarEventManager, { CalendarEventManagerOptions, EventManagerCalendarVc } from '../../calendar/CalendarEventManager';
6
+ import { PeopleManagerCalendarVc } from '../../calendar/CalendarPeopleManager';
5
7
  import { GetScheduleOptions, RemoteEventStore } from '../../stores/RemoteEventStore';
6
8
  import { Calendar, CalendarToolBeltStateMachine, UpdateEvent } from '../../types/calendar.types';
7
9
  import SpyEventManager from './SpyEventManager';
8
10
  export default class CalendarToolBeltStateMachineTestFactory {
9
- static StateMachine(views: ViewFixture): Promise<{
11
+ static StateMachine(views: ViewFixture, options?: {
12
+ calendarVc?: EventManagerCalendarVc & PeopleManagerCalendarVc;
13
+ shouldUpdateCalendarOnContextUpdates?: boolean;
14
+ EventManagerClass?: new (options: CalendarEventManagerOptions) => CalendarEventManager;
15
+ }): Promise<{
10
16
  stateMachine: CalendarToolBeltStateMachine;
11
17
  toolBeltVc: ToolBeltViewController;
12
18
  calendarVc: CalendarViewController;
@@ -17,8 +17,9 @@ import draftEventGenerator from '../../utilities/draftGenerator.js';
17
17
  import SpyEventManager from './SpyEventManager.js';
18
18
  import SpyPeopleManager from './SpyPeopleManager.js';
19
19
  export default class CalendarToolBeltStateMachineTestFactory {
20
- static StateMachine(views) {
20
+ static StateMachine(views, options) {
21
21
  return __awaiter(this, void 0, void 0, function* () {
22
+ const { EventManagerClass, calendarVc: passedCalendarVc, shouldUpdateCalendarOnContextUpdates = true, } = options !== null && options !== void 0 ? options : {};
22
23
  const factory = views.getFactory();
23
24
  if (!factory.hasController('calendar.calendar')) {
24
25
  factory.mixinControllers({
@@ -28,7 +29,7 @@ export default class CalendarToolBeltStateMachineTestFactory {
28
29
  }
29
30
  const toolBeltVc = views.Controller('toolBelt', {});
30
31
  const event = calendarSeeder.generateEventValues();
31
- const calendarVc = views.Controller('calendar.calendar', {
32
+ const calendarVc = passedCalendarVc !== null && passedCalendarVc !== void 0 ? passedCalendarVc : views.Controller('calendar.calendar', {
32
33
  onAddDraftEvent: () => { },
33
34
  onEventSwapped(draft, saved) {
34
35
  return events.silentlySwapEvent(draft.id, saved);
@@ -39,13 +40,12 @@ export default class CalendarToolBeltStateMachineTestFactory {
39
40
  vcFactory: views.getFactory(),
40
41
  toolBeltVc,
41
42
  connectToApi: () => __awaiter(this, void 0, void 0, function* () { return fake.getClient(); }),
42
- context: Object.assign(Object.assign({}, views.getRouter().buildLoadOptions()), { event,
43
- calendarVc, cancelEvent: () => { } }),
43
+ context: Object.assign(Object.assign({}, views.getRouter().buildLoadOptions()), { event, calendarVc: calendarVc, cancelEvent: () => { } }),
44
44
  });
45
45
  const connectToApi = () => __awaiter(this, void 0, void 0, function* () { return fake.getClient(); });
46
46
  const preferences = new FakeRemotePreferences({ connectToApi });
47
47
  const remoteEventsStore = new FakeRemoteEventStore();
48
- const events = new SpyEventManager({
48
+ const events = new (EventManagerClass !== null && EventManagerClass !== void 0 ? EventManagerClass : SpyEventManager)({
49
49
  calendarVc,
50
50
  sm,
51
51
  dates: dateUtil,
@@ -67,9 +67,13 @@ export default class CalendarToolBeltStateMachineTestFactory {
67
67
  getVisibleEvents: () => [],
68
68
  });
69
69
  yield sm.updateContext({ events, people });
70
- yield sm.on('did-update-context', () => {
71
- calendarVc.updateEvent(event.id, Object.assign({}, sm.getContext().event));
72
- });
70
+ if (shouldUpdateCalendarOnContextUpdates) {
71
+ // await sm.on('did-update-context', () => {
72
+ // calendarVc.updateEvent(event.id, {
73
+ // ...sm.getContext().event,
74
+ // })
75
+ // })
76
+ }
73
77
  //@ts-ignore
74
78
  return { stateMachine: sm, toolBeltVc, calendarVc, events };
75
79
  });
@@ -7,7 +7,8 @@ export default class SpyEventManager extends CalendarEventManager {
7
7
  wereShiftsRefreshed: boolean;
8
8
  getShouldIgnoreNextContextUpdate(): boolean;
9
9
  constructor(options: SpyEventManagerOptions);
10
- protected refreshShifts(): void;
10
+ refreshShifts(): void;
11
+ generateShifts(): import("@sprucelabs/spruce-core-schemas").SpruceSchemas.HeartwoodViewControllers.v2021_02_11.CalendarShift[];
11
12
  getRemoteEventsStore(): SpyRemoteEventStore;
12
13
  getStateMachine(): import("../../types/calendar.types").CalendarToolBeltStateMachine<CalendarToolBeltContext>;
13
14
  getPreferences(): SpyRemotePreferencesStore;
@@ -20,6 +20,9 @@ export default class SpyEventManager extends CalendarEventManager {
20
20
  this.wereShiftsRefreshed = true;
21
21
  return super.refreshShifts();
22
22
  }
23
+ generateShifts() {
24
+ return super.generateShifts();
25
+ }
23
26
  getRemoteEventsStore() {
24
27
  return this.events;
25
28
  }
@@ -20,14 +20,19 @@ export default class CalendarEventManager {
20
20
  protected shouldUpdateAllEventsGoingForward?: boolean;
21
21
  protected prefs: RemotePreferencesStore;
22
22
  private calendars;
23
- private shouldUpdateContextOnNextSave;
24
23
  private isLoaded;
25
24
  protected updateContext?: UpdateCalendarToolBeltContextHandler<CalendarToolBeltContext>;
25
+ private updateOperations;
26
+ private isRunningUpdateQueue;
27
+ private updateQueuePromise?;
26
28
  protected get calendarIds(): string[];
27
29
  protected get visibleCalendarIds(): string[];
28
30
  constructor(options: CalendarEventManagerOptions);
29
31
  replaceEventsInRange(events: CalendarEvent[], startMs: number, endMs: number): void;
30
32
  protected refreshShifts(): void;
33
+ private doShiftsMatchWhatIsInCalendar;
34
+ private generateShiftKeys;
35
+ protected generateShifts(): import("@sprucelabs/calendar-utils").SpruceSchemas.HeartwoodViewControllers.v2021_02_11.CalendarShift[];
31
36
  private isCalendarSelected;
32
37
  private doesCalenderExist;
33
38
  getEvents(): CalendarEvent[];
@@ -49,6 +54,9 @@ export default class CalendarEventManager {
49
54
  makeCalendarVisible(calendarId: string): Promise<void>;
50
55
  makeCalendarHidden(calendarId: string): Promise<void>;
51
56
  updateEvent(id: string, updates: UpdateEventOptions): void;
57
+ waitForPendingOperations(): Promise<void>;
58
+ private startUpdateQueue;
59
+ private _updateEvent;
52
60
  private calculateDifferences;
53
61
  loadEvents(startDate: number, endDate: number, peopleIds: string[]): Promise<import("@sprucelabs/calendar-utils").SpruceSchemas.HeartwoodViewControllers.v2021_02_11.CalendarEvent[]>;
54
62
  setCalendarVisibility(calendarId: string, shouldBeVisible: boolean): Promise<void>;
@@ -95,7 +103,7 @@ export default class CalendarEventManager {
95
103
  setCurrentDate(dateMs: number): Promise<void>;
96
104
  private handleDidUpdateContext;
97
105
  protected saveEvent(event: SavedEvent): Promise<void>;
98
- private clearPersistRepeatingOptions;
106
+ private clearRepeatingStrategyOptions;
99
107
  getIsLoaded(): boolean;
100
108
  hasEvent(id: string): boolean;
101
109
  optionallyAskForUpdateRepeatingStrategy(event: UpdateEvent, action?: RepeatingUpdateAction): Promise<boolean>;
@@ -38,8 +38,9 @@ export default class CalendarEventManager {
38
38
  this.hasVcForEventTypeBeenLoaded = {};
39
39
  this.shouldIgnoreNextContextUpdate = false;
40
40
  this.calendars = [];
41
- this.shouldUpdateContextOnNextSave = true;
42
41
  this.isLoaded = false;
42
+ this.updateOperations = [];
43
+ this.isRunningUpdateQueue = false;
43
44
  const { calendarVc, events, remoteVc, sm, askForUpdateStrategy, preferences, } = assertOptions(options, [
44
45
  'calendarVc',
45
46
  'events',
@@ -64,9 +65,29 @@ export default class CalendarEventManager {
64
65
  this.refreshShifts();
65
66
  }
66
67
  refreshShifts() {
68
+ const shifts = this.generateShifts();
69
+ const areSame = this.doShiftsMatchWhatIsInCalendar(shifts);
70
+ if (areSame) {
71
+ return;
72
+ }
73
+ this.calendarVc.setShifts([...shifts]);
74
+ }
75
+ doShiftsMatchWhatIsInCalendar(shifts) {
76
+ var _a;
77
+ const currentShifts = this.generateShiftKeys((_a = this.calendarVc.getShifts()) !== null && _a !== void 0 ? _a : []);
78
+ const newShifts = this.generateShiftKeys(shifts);
79
+ const areShiftsTheSame = newShifts === currentShifts;
80
+ return areShiftsTheSame;
81
+ }
82
+ generateShiftKeys(shifts) {
83
+ return shifts
84
+ .map((s) => `${s.id}-${s.startDateTimeMs}-${s.endDateTimeMs}`)
85
+ .join('-');
86
+ }
87
+ generateShifts() {
67
88
  const inclusiveEvents = this.allEvents.filter((e) => this.inclusiveCalendarIds.indexOf(e.calendarId) > -1);
68
89
  const shifts = calendarShiftGenerator.generateFromEvents(inclusiveEvents);
69
- this.calendarVc.setShifts([...shifts]);
90
+ return shifts;
70
91
  }
71
92
  isCalendarSelected(calendarId) {
72
93
  return this.visibleCalendarIds.indexOf(calendarId) > -1;
@@ -174,7 +195,7 @@ export default class CalendarEventManager {
174
195
  console.error((_a = err.stack) !== null && _a !== void 0 ? _a : err.message);
175
196
  this.calendarVc.addEvent(Object.assign(Object.assign({}, event), { error: err }));
176
197
  }
177
- this.clearPersistRepeatingOptions();
198
+ this.clearRepeatingStrategyOptions();
178
199
  });
179
200
  }
180
201
  makeCalendarVisible(calendarId) {
@@ -196,27 +217,60 @@ export default class CalendarEventManager {
196
217
  });
197
218
  }
198
219
  updateEvent(id, updates) {
199
- var _a;
200
- const { shouldPersist } = updates, rest = __rest(updates, ["shouldPersist"]);
201
- const { isTheSame, isTheSameWithoutBusy } = this.calculateDifferences(id, updates);
202
- if (isTheSame) {
203
- return;
204
- }
205
- this.calendarVc.updateEvent(id, Object.assign(Object.assign({}, rest), { shouldPersist }));
206
- const idx = this.allEvents.findIndex((e) => e.id === id);
207
- this.allEvents[idx] = Object.assign(Object.assign({}, this.allEvents[idx]), rest);
208
- if (isTheSameWithoutBusy) {
209
- return;
210
- }
211
- this.refreshShifts();
212
- if (this.shouldUpdateContextOnNextSave &&
213
- ((_a = this.sm.getContext().event) === null || _a === void 0 ? void 0 : _a.id) === id) {
214
- this.shouldIgnoreNextContextUpdate = true;
215
- void this.updateEventInContext(rest).then(() => {
220
+ const operation = new UpdateOperation({
221
+ id,
222
+ updates,
223
+ onComplete: this._updateEvent.bind(this),
224
+ });
225
+ this.updateOperations.push(operation);
226
+ this.clearRepeatingStrategyOptions();
227
+ this.updateQueuePromise = this.startUpdateQueue();
228
+ }
229
+ waitForPendingOperations() {
230
+ return __awaiter(this, void 0, void 0, function* () {
231
+ yield this.updateQueuePromise;
232
+ });
233
+ }
234
+ startUpdateQueue() {
235
+ return __awaiter(this, void 0, void 0, function* () {
236
+ if (this.isRunningUpdateQueue) {
237
+ return;
238
+ }
239
+ this.isRunningUpdateQueue = true;
240
+ let next;
241
+ while ((next = this.updateOperations.shift())) {
242
+ yield next.execute();
243
+ }
244
+ this.isRunningUpdateQueue = false;
245
+ });
246
+ }
247
+ _updateEvent(id, updates) {
248
+ var _a, _b;
249
+ return __awaiter(this, void 0, void 0, function* () {
250
+ const { shouldPersist } = updates, rest = __rest(updates, ["shouldPersist"]);
251
+ const { isTheSame, isTheSameWithoutBusy } = this.calculateDifferences(id, updates);
252
+ if (isTheSame) {
253
+ return;
254
+ }
255
+ try {
256
+ this.calendarVc.updateEvent(id, Object.assign(Object.assign({}, rest), { shouldPersist }));
257
+ }
258
+ catch (err) {
259
+ console.error((_a = err.stack) !== null && _a !== void 0 ? _a : err.message);
260
+ return;
261
+ }
262
+ const idx = this.allEvents.findIndex((e) => e.id === id);
263
+ this.allEvents[idx] = Object.assign(Object.assign({}, this.allEvents[idx]), rest);
264
+ if (isTheSameWithoutBusy) {
265
+ return;
266
+ }
267
+ this.refreshShifts();
268
+ if (((_b = this.sm.getContext().event) === null || _b === void 0 ? void 0 : _b.id) === id) {
269
+ this.shouldIgnoreNextContextUpdate = true;
270
+ yield this.updateEventInContext(rest);
216
271
  this.shouldIgnoreNextContextUpdate = false;
217
- });
218
- }
219
- this.shouldUpdateContextOnNextSave = true;
272
+ }
273
+ });
220
274
  }
221
275
  calculateDifferences(id, updates) {
222
276
  const match = this.allEvents.find((e) => e.id === id);
@@ -378,7 +432,6 @@ export default class CalendarEventManager {
378
432
  }
379
433
  const { event } = this.sm.getContext();
380
434
  if (event === null || event === void 0 ? void 0 : event.id) {
381
- this.shouldUpdateContextOnNextSave = false;
382
435
  yield this.saveEvent(event);
383
436
  }
384
437
  });
@@ -392,10 +445,9 @@ export default class CalendarEventManager {
392
445
  catch (err) {
393
446
  console.error('Updating event in context from root failed because:\n\n', err);
394
447
  }
395
- this.clearPersistRepeatingOptions();
396
448
  });
397
449
  }
398
- clearPersistRepeatingOptions() {
450
+ clearRepeatingStrategyOptions() {
399
451
  this.dateToUpdate = undefined;
400
452
  this.shouldUpdateAllEventsGoingForward = undefined;
401
453
  }
@@ -434,3 +486,16 @@ export default class CalendarEventManager {
434
486
  });
435
487
  }
436
488
  }
489
+ class UpdateOperation {
490
+ constructor(options) {
491
+ const { id, updates, onComplete } = options;
492
+ this.id = id;
493
+ this.updates = updates;
494
+ this.onComplete = onComplete;
495
+ }
496
+ execute() {
497
+ return __awaiter(this, void 0, void 0, function* () {
498
+ yield this.onComplete(this.id, this.updates);
499
+ });
500
+ }
501
+ }
@@ -169,6 +169,7 @@ class RootSkillViewController extends AbstractSkillViewController {
169
169
  this.loadEventsPromise,
170
170
  this.sm.waitForContextUpdate(),
171
171
  this.calendarVc.waitForPendingSaves(),
172
+ this.events.waitForPendingOperations(),
172
173
  ]);
173
174
  });
174
175
  }
@@ -0,0 +1 @@
1
+ export default function isEqual(a: any, b: any): boolean;
@@ -0,0 +1,29 @@
1
+ export default function isEqual(a, b) {
2
+ if (a === b) {
3
+ return true;
4
+ }
5
+ if (a instanceof Date && b instanceof Date) {
6
+ return a.getTime() === b.getTime();
7
+ }
8
+ if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) {
9
+ return a === b;
10
+ }
11
+ if (a.prototype !== b.prototype) {
12
+ return false;
13
+ }
14
+ a = removeUndefinedAndNullFields(Object.assign({}, a));
15
+ b = removeUndefinedAndNullFields(Object.assign({}, b));
16
+ const keys = Object.keys(a);
17
+ if (keys.length !== Object.keys(b).length) {
18
+ return false;
19
+ }
20
+ return keys.every((k) => isEqual(a[k], b[k]));
21
+ }
22
+ function removeUndefinedAndNullFields(obj) {
23
+ for (const key in obj) {
24
+ if (obj[key] === undefined || obj[key] === null) {
25
+ delete obj[key];
26
+ }
27
+ }
28
+ return obj;
29
+ }
@@ -36,7 +36,7 @@ export default class AbstractCalendarEventToolBeltState {
36
36
  position: 'bottom',
37
37
  });
38
38
  this.handleDidUpdateContext = this.handleDidUpdateContext.bind(this);
39
- yield this.sm.on('did-update-context', this.handleDidUpdateContext);
39
+ void this.sm.on('did-update-context', this.handleDidUpdateContext);
40
40
  });
41
41
  }
42
42
  getIsLoaded() {
@@ -150,6 +150,7 @@ class RootSkillViewController extends heartwood_view_controllers_1.AbstractSkill
150
150
  this.loadEventsPromise,
151
151
  this.sm.waitForContextUpdate(),
152
152
  this.calendarVc.waitForPendingSaves(),
153
+ this.events.waitForPendingOperations(),
153
154
  ]);
154
155
  }
155
156
  async handleSelectCalendar(calendar) {
@@ -0,0 +1 @@
1
+ export default function isEqual(a: any, b: any): boolean;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function isEqual(a, b) {
4
+ if (a === b) {
5
+ return true;
6
+ }
7
+ if (a instanceof Date && b instanceof Date) {
8
+ return a.getTime() === b.getTime();
9
+ }
10
+ if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) {
11
+ return a === b;
12
+ }
13
+ if (a.prototype !== b.prototype) {
14
+ return false;
15
+ }
16
+ a = removeUndefinedAndNullFields(Object.assign({}, a));
17
+ b = removeUndefinedAndNullFields(Object.assign({}, b));
18
+ const keys = Object.keys(a);
19
+ if (keys.length !== Object.keys(b).length) {
20
+ return false;
21
+ }
22
+ return keys.every((k) => isEqual(a[k], b[k]));
23
+ }
24
+ exports.default = isEqual;
25
+ function removeUndefinedAndNullFields(obj) {
26
+ for (const key in obj) {
27
+ if (obj[key] === undefined || obj[key] === null) {
28
+ delete obj[key];
29
+ }
30
+ }
31
+ return obj;
32
+ }
@@ -31,7 +31,7 @@ class AbstractCalendarEventToolBeltState {
31
31
  position: 'bottom',
32
32
  });
33
33
  this.handleDidUpdateContext = this.handleDidUpdateContext.bind(this);
34
- await this.sm.on('did-update-context', this.handleDidUpdateContext);
34
+ void this.sm.on('did-update-context', this.handleDidUpdateContext);
35
35
  }
36
36
  getIsLoaded() {
37
37
  return this.isLoaded;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sprucelabs/spruce-calendar-components",
3
3
  "description": "Calendar components for working with calendars and Sprucebot.",
4
- "version": "25.6.4",
4
+ "version": "25.6.6",
5
5
  "skill": {
6
6
  "namespace": "calendar"
7
7
  },
@@ -62,6 +62,10 @@
62
62
  "build/constants.d.ts",
63
63
  "build/esm/constants.js",
64
64
  "build/esm/constants.d.ts",
65
+ "build/stores/isEqual.js",
66
+ "build/stores/isEqual.d.ts",
67
+ "build/esm/stores/isEqual.js",
68
+ "build/esm/stores/isEqual.d.ts",
65
69
  "build/index-components.js",
66
70
  "build/index-components.d.ts",
67
71
  "build/esm/index-components.js",