@openeventkit/event-site 2.1.18 → 2.1.21

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.
Files changed (86) hide show
  1. package/.github/workflows/jest.yml +1 -1
  2. package/babel.config.json +9 -9
  3. package/gatsby-node.js +67 -125
  4. package/jest.setup.js +2 -0
  5. package/netlify.toml +1 -1
  6. package/package.json +25 -16
  7. package/src/__mocks__/@mdx-js/mdx.js +32 -0
  8. package/src/__mocks__/@mdx-js/react.js +15 -0
  9. package/src/__mocks__/rehype-external-links.js +3 -0
  10. package/src/__mocks__/remark-gfm.js +3 -0
  11. package/src/actions/fetch-entities-actions.js +45 -87
  12. package/src/actions/update-data-actions.js +2 -2
  13. package/src/actions/user-actions.js +578 -430
  14. package/src/cms/config/collections/configurationsCollection/siteSettings/index.js +2 -0
  15. package/src/cms/config/collections/configurationsCollection/siteSettings/typeDefs.js +10 -0
  16. package/src/cms/preview-templates/ContentPagePreview.js +27 -29
  17. package/src/components/AvatarEditorModal/index.js +10 -0
  18. package/src/components/CertificatePDF.js +313 -0
  19. package/src/components/CertificateSection.js +139 -0
  20. package/src/components/FullSchedule.js +83 -66
  21. package/src/components/Mdx.js +39 -0
  22. package/src/components/__tests__/Mdx.test.jsx +70 -0
  23. package/src/content/site-settings/index.json +1 -1
  24. package/src/content/sponsors.json +1 -1
  25. package/src/i18n/locales/en.json +9 -1
  26. package/src/pages/a/[...].js +3 -0
  27. package/src/reducers/user-reducer.js +89 -27
  28. package/src/routes/authorization-callback-route.js +20 -2
  29. package/src/styles/rsvp-page.module.scss +63 -0
  30. package/src/templates/full-profile-page.js +61 -2
  31. package/src/templates/marketing-page-template/MainColumn.js +40 -42
  32. package/src/templates/rsvp-page.js +144 -0
  33. package/src/utils/alerts.js +1 -1
  34. package/src/utils/build-json/BaseAPIRequest.js +25 -0
  35. package/src/utils/build-json/EventsAPIRequest.js +171 -0
  36. package/src/utils/build-json/SpeakersAPIRequest.js +62 -0
  37. package/src/utils/build-json/SummitAPIRequest.js +115 -0
  38. package/src/utils/build-json/constants.js +5 -0
  39. package/src/utils/certificateSettings.js +45 -0
  40. package/src/utils/customErrorHandler.js +40 -1
  41. package/src/utils/rsvpConstants.js +7 -0
  42. package/src/utils/useMarketingSettings.js +48 -1
  43. package/src/utils/useSiteSettings.js +11 -0
  44. package/src/workers/feeds.worker.js +85 -90
  45. package/src/workers/sync_strategies/activity_synch_strategy.js +147 -102
  46. package/src/workers/sync_strategies/speaker_synch_strategy.js +3 -3
  47. package/src/workers/sync_strategies/track_synch_strategy.js +149 -48
  48. package/src/workers/synch.worker.js +123 -88
  49. package/static/fonts/fonts.css +120 -20
  50. package/static/fonts/nunito-sans/nunito-sans-v18-latin-200.woff2 +0 -0
  51. package/static/fonts/nunito-sans/nunito-sans-v18-latin-200italic.ttf +0 -0
  52. package/static/fonts/nunito-sans/nunito-sans-v18-latin-200italic.woff2 +0 -0
  53. package/static/fonts/nunito-sans/nunito-sans-v18-latin-300.woff2 +0 -0
  54. package/static/fonts/nunito-sans/nunito-sans-v18-latin-300italic.ttf +0 -0
  55. package/static/fonts/nunito-sans/nunito-sans-v18-latin-300italic.woff2 +0 -0
  56. package/static/fonts/nunito-sans/nunito-sans-v18-latin-400.ttf +0 -0
  57. package/static/fonts/nunito-sans/nunito-sans-v18-latin-400.woff2 +0 -0
  58. package/static/fonts/nunito-sans/nunito-sans-v18-latin-400italic.ttf +0 -0
  59. package/static/fonts/nunito-sans/nunito-sans-v18-latin-400italic.woff2 +0 -0
  60. package/static/fonts/nunito-sans/nunito-sans-v18-latin-500.ttf +0 -0
  61. package/static/fonts/nunito-sans/nunito-sans-v18-latin-500.woff2 +0 -0
  62. package/static/fonts/nunito-sans/nunito-sans-v18-latin-500italic.ttf +0 -0
  63. package/static/fonts/nunito-sans/nunito-sans-v18-latin-500italic.woff2 +0 -0
  64. package/static/fonts/nunito-sans/nunito-sans-v18-latin-600.woff2 +0 -0
  65. package/static/fonts/nunito-sans/nunito-sans-v18-latin-600italic.woff2 +0 -0
  66. package/static/fonts/nunito-sans/nunito-sans-v18-latin-700.ttf +0 -0
  67. package/static/fonts/nunito-sans/nunito-sans-v18-latin-700.woff2 +0 -0
  68. package/static/fonts/nunito-sans/nunito-sans-v18-latin-700italic.woff2 +0 -0
  69. package/static/fonts/nunito-sans/nunito-sans-v18-latin-800.ttf +0 -0
  70. package/static/fonts/nunito-sans/nunito-sans-v18-latin-800.woff2 +0 -0
  71. package/static/fonts/nunito-sans/nunito-sans-v18-latin-800italic.woff2 +0 -0
  72. package/static/fonts/nunito-sans/nunito-sans-v18-latin-900.ttf +0 -0
  73. package/static/fonts/nunito-sans/nunito-sans-v18-latin-900.woff2 +0 -0
  74. package/static/fonts/nunito-sans/nunito-sans-v18-latin-900italic.woff2 +0 -0
  75. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300.woff +0 -0
  76. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300.woff2 +0 -0
  77. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300italic.woff +0 -0
  78. package/static/fonts/nunito-sans/nunito-sans-v12-latin-300italic.woff2 +0 -0
  79. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600.woff +0 -0
  80. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600.woff2 +0 -0
  81. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600italic.woff +0 -0
  82. package/static/fonts/nunito-sans/nunito-sans-v12-latin-600italic.woff2 +0 -0
  83. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700.woff +0 -0
  84. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700.woff2 +0 -0
  85. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700italic.woff +0 -0
  86. package/static/fonts/nunito-sans/nunito-sans-v12-latin-700italic.woff2 +0 -0
@@ -4,7 +4,8 @@ import {insertSorted, intCheck, rebuildIndex} from "../../utils/arrayUtils";
4
4
  import {
5
5
  BUCKET_EVENTS_DATA_KEY,
6
6
  BUCKET_EVENTS_IDX_DATA_KEY,
7
- BUCKET_SPEAKERS_DATA_KEY, BUCKET_SPEAKERS_IDX_DATA_KEY,
7
+ BUCKET_SPEAKERS_DATA_KEY,
8
+ BUCKET_SPEAKERS_IDX_DATA_KEY,
8
9
  BUCKET_SUMMIT_DATA_KEY,
9
10
  saveFile
10
11
  } from "../../utils/dataUpdatesUtils";
@@ -15,123 +16,167 @@ import moment from "moment-timezone";
15
16
  */
16
17
  class ActivitySynchStrategy extends AbstractSynchStrategy{
17
18
 
18
- async process(payload){
19
+ _removeEventById(entity_id, eventsData){
20
+ // was deleted ( un - published)
21
+ // try to get from index
22
+ const idx = this.allIDXEvents[entity_id] ?? -1;
23
+ console.log(`ActivitySynchStrategy::_removeEventById unpublished presentation ${entity_id} idx ${idx}`);
19
24
 
20
- console.log(`ActivitySynchStrategy::process`, payload);
25
+ // Remove from array and index
26
+ const pruned = eventsData.filter(e => e.id !== entity_id);
27
+ delete this.allIDXEvents[entity_id];
28
+ return pruned;
29
+ }
21
30
 
22
- const {entity_operator, entity_id} = payload;
31
+ _touchSummitTimestamp() {
32
+ this.summit = {
33
+ ...this.summit,
34
+ timestamp: moment().unix(),
35
+ };
36
+ }
23
37
 
24
- let entity = await fetchEventById(this.summit.id, entity_id, this.accessToken);
25
- if(this.accessToken) {
26
- const streaming_info = await fetchStreamingInfoByEventId(this.summit.id, entity_id, this.accessToken);
27
- if(streaming_info) entity = {...entity, ...streaming_info};
38
+ /** Standard result payload + log */
39
+ _result(payload, entity, eventsData) {
40
+ const res = {
41
+ payload,
42
+ entity: entity,
43
+ summit: this.summit,
44
+ eventsData,
45
+ allIDXEvents: this.allIDXEvents,
46
+ allSpeakers: this.allSpeakers,
47
+ allIDXSpeakers: this.allIDXSpeakers,
48
+ };
49
+ console.log("ActivitySynchStrategy::process done", res);
50
+ return res;
51
+ }
52
+
53
+ async _persistAll(eventsData) {
54
+ this._touchSummitTimestamp();
55
+ const localNowUtc = Date.now();
56
+
57
+ await Promise.all([
58
+ saveFile(this.summit.id, BUCKET_SUMMIT_DATA_KEY, this.summit, localNowUtc),
59
+ saveFile(this.summit.id, BUCKET_EVENTS_DATA_KEY, eventsData, localNowUtc),
60
+ saveFile(this.summit.id, BUCKET_EVENTS_IDX_DATA_KEY, this.allIDXEvents, localNowUtc),
61
+ saveFile(this.summit.id, BUCKET_SPEAKERS_DATA_KEY, this.allSpeakers, localNowUtc),
62
+ saveFile(this.summit.id, BUCKET_SPEAKERS_IDX_DATA_KEY, this.allIDXSpeakers, localNowUtc),
63
+ ]);
64
+ }
65
+
66
+ async _handleDeleteOrUnpublish(entityId, payload, eventsData) {
67
+ console.log(
68
+ `ActivitySynchStrategy::_handleDeleteOrUnpublish delete/unpublish presentation ${entityId}`
69
+ );
70
+
71
+ // Remove and rebuild index
72
+ const newEventsData = this._removeEventById(entityId, eventsData);
73
+ this.allIDXEvents = rebuildIndex(newEventsData);
74
+
75
+ // Persist
76
+ try {
77
+ console.log("ActivitySynchStrategy::_handleDeleteOrUnpublish updating cache files (delete)");
78
+ await this._persistAll(newEventsData);
79
+ } catch (e) {
80
+ console.error(e);
28
81
  }
29
82
 
30
- let eventsData = [...this.allEvents];
83
+ // No entity on delete/unpublish
84
+ return this._result(payload, null, newEventsData);
85
+ }
31
86
 
32
- if (entity_operator === 'UPDATE') {
87
+ /** Insert/update a single person (speaker/moderator) into arrays + index */
88
+ _upsertSpeaker(person) {
89
+ if (!person) return;
90
+ const idx = this.allIDXSpeakers[person.id] ?? -1;
91
+ if (idx === -1 || !this.allSpeakers[idx]) {
92
+ this.allSpeakers.push(person);
93
+ this.allIDXSpeakers[person.id] = this.allSpeakers.length - 1;
94
+ } else {
95
+ this.allSpeakers[idx] = person;
96
+ }
97
+ }
33
98
 
34
- if(!entity){
35
- // was deleted ( un - published)
36
- // try to get from index
37
- const idx = this.allIDXEvents[entity_id] ?? -1;
38
- console.log(`ActivitySynchStrategy::process unpublished presentation ${entity_id} idx ${idx}`);
99
+ /** Apply speakers & moderator coming from an entity */
100
+ _applyPeopleFromEntity(entity) {
101
+ if (Array.isArray(entity?.speakers)) {
102
+ for (const s of entity.speakers) this._upsertSpeaker(s);
103
+ }
104
+ if (entity?.moderator) this._upsertSpeaker(entity.moderator);
105
+ }
39
106
 
40
- if(idx === -1)
41
- return Promise.reject('ActivitySynchStrategy::process unpublished idx === -1.'); // does not exists on index ...
42
- // Remove from array and index
43
- eventsData = eventsData.filter(e => e.id !== entity_id);
44
- delete this.allIDXEvents[entity_id];
45
- }
46
- else {
47
- // Entity is published or updated
48
-
49
- // Remove any existing version of this event
50
- eventsData = eventsData.filter(e => e.id !== entity.id);
51
-
52
- // Re-insert the updated entity into sorted position
53
-
54
- this.allIDXEvents[entity.id] = insertSorted(eventsData, entity, (a, b) => {
55
- if (a.start_date === b.start_date) {
56
- if (a.end_date === b.end_date) {
57
- return intCheck(a.id, b.id);
58
- }
59
- return intCheck(a.end_date, b.end_date);
60
- }
61
- return intCheck(a.start_date, b.start_date);
62
- });
63
-
64
- // Rebuild the full event index to be safe
65
-
66
- this.allIDXEvents = rebuildIndex(eventsData);
67
-
68
- // Update speakers
69
- if (entity.speakers) {
70
- console.log(`ActivitySynchStrategy::process updating speakers`, entity.speakers);
71
- for (const speaker of entity.speakers) {
72
- const idx = this.allIDXSpeakers[speaker.id] ?? -1;
73
- if (idx === -1 || !this.allSpeakers[idx]) {
74
- console.log(`ActivitySynchStrategy::process speaker does not exists, inserting it at end`, speaker);
75
- this.allSpeakers.push(speaker);
76
- this.allIDXSpeakers[speaker.id] = this.allSpeakers.length - 1;
77
- } else {
78
- console.log(`ActivitySynchStrategy::process updating speaker at idx ${idx}`, speaker);
79
- this.allSpeakers[idx] = speaker;
80
- }
81
- }
82
- }
107
+ async _handleUpsert(entity, payload, eventsData) {
108
+ console.log("ActivitySynchStrategy::process upsert", { id: entity?.id });
83
109
 
84
- // Update moderator
85
- if (entity.moderator) {
86
- console.log(`ActivitySynchStrategy::process updating moderator`, entity.moderator);
87
- const idx = this.allIDXSpeakers[entity.moderator.id] ?? -1;
88
- if (idx === -1 || !this.allSpeakers[idx]) {
89
- console.log(`ActivitySynchStrategy::process moderator does not exists, inserting it at end`, entity.moderator);
90
- this.allSpeakers.push(entity.moderator);
91
- this.allIDXSpeakers[entity.moderator.id] = this.allSpeakers.length - 1;
92
- } else {
93
- console.log(`ActivitySynchStrategy::process updating moderator at idx ${idx}`, entity.moderator);
94
- this.allSpeakers[idx] = entity.moderator;
95
- }
96
- }
110
+ // Entity is published or updated
97
111
 
98
- }
112
+ // Remove any existing version of this event
113
+ const without = eventsData.filter(e => e.id !== entity.id);
99
114
 
100
- // update files on cache
101
- console.log(`ActivitySynchStrategy::process updating cache files`);
102
-
103
- // Update summit timestamp to trigger data reload downstream
104
- this.summit = {
105
- ...this.summit,
106
- timestamp: moment().unix(),
107
- };
108
- // Persist everything
109
- try {
110
- const localNowUtc = Date.now();
111
-
112
- await saveFile(this.summit.id, BUCKET_SUMMIT_DATA_KEY, this.summit, localNowUtc);
113
- await saveFile(this.summit.id, BUCKET_EVENTS_DATA_KEY, eventsData, localNowUtc);
114
- await saveFile(this.summit.id, BUCKET_EVENTS_IDX_DATA_KEY, this.allIDXEvents, localNowUtc);
115
- await saveFile(this.summit.id, BUCKET_SPEAKERS_DATA_KEY, this.allSpeakers, localNowUtc);
116
- await saveFile(this.summit.id, BUCKET_SPEAKERS_IDX_DATA_KEY, this.allIDXSpeakers, localNowUtc);
117
- } catch (e) {
118
- console.error(e);
115
+ // Re-insert the updated entity into sorted position
116
+
117
+ this.allIDXEvents[entity.id] = insertSorted(without, entity, (a, b) => {
118
+ if (a.start_date === b.start_date) {
119
+ if (a.end_date === b.end_date) {
120
+ return intCheck(a.id, b.id);
121
+ }
122
+ return intCheck(a.end_date, b.end_date);
119
123
  }
124
+ return intCheck(a.start_date, b.start_date);
125
+ });
120
126
 
121
- const res = {
122
- payload,
123
- entity,
124
- summit: this.summit,
125
- eventsData,
126
- allIDXEvents: this.allIDXEvents,
127
- allSpeakers: this.allSpeakers,
128
- allIDXSpeakers: this.allIDXSpeakers,
129
- };
127
+ // Rebuild full index for safety/consistency
128
+ this.allIDXEvents = rebuildIndex(without);
130
129
 
131
- console.log(`ActivitySynchStrategy::process done`, res);
132
130
 
133
- return Promise.resolve(res);
131
+ // Update speakers & moderator
132
+ this._applyPeopleFromEntity(entity);
133
+
134
+ // Persist
135
+ try {
136
+ console.log("ActivitySynchStrategy::process updating cache files (upsert)");
137
+ await this._persistAll(without);
138
+ } catch (e) {
139
+ console.error(e);
140
+ }
141
+
142
+
143
+ return this._result(payload, entity, without);
144
+ }
145
+
146
+ async process(payload){
147
+
148
+ console.log(`ActivitySynchStrategy::process`, payload);
149
+
150
+ const {entity_operator, entity_id} = payload;
151
+
152
+ let eventsData = [...this.allEvents];
153
+
154
+ switch (entity_operator) {
155
+ case 'INSERT':
156
+ case 'UPDATE':{
157
+ let entity = await fetchEventById(this.summit.id, entity_id, this.accessToken);
158
+ if(this.accessToken) {
159
+ const streaming_info = await fetchStreamingInfoByEventId(this.summit.id, entity_id, this.accessToken);
160
+ if(streaming_info) entity = {...entity, ...streaming_info};
161
+ }
162
+
163
+ if(!entity){
164
+ // was deleted ( un - published)
165
+ return this._handleDeleteOrUnpublish(entity_id, payload, eventsData);
166
+ }
167
+ // Normal upsert flow
168
+ return this._handleUpsert(entity, payload, eventsData);
169
+ }
170
+ case 'DELETE':{
171
+ // was hard deleted
172
+ return this._handleDeleteOrUnpublish(entity_id, payload, eventsData);
173
+ }
134
174
  }
175
+
176
+ // Unknown op
177
+ const msg = `ActivitySynchStrategy::process unknown entity_operator '${entity_operator}'`;
178
+ console.warn(msg);
179
+ throw new Error(msg);
135
180
  }
136
181
  }
137
182
 
@@ -36,7 +36,7 @@ class SpeakerSynchStrategy extends AbstractSynchStrategy {
36
36
  this.allIDXSpeakers = rebuildIndex(this.allSpeakers);
37
37
 
38
38
  // Update events where speaker is listed
39
- for (const eventId of entity.all_presentations || []) {
39
+ for (const eventId of entity.presentations || []) {
40
40
  const res = getIndexedItem(this.allIDXEvents, eventsData, eventId);
41
41
  if (!res || !Array.isArray(res.item.speakers)) {
42
42
  console.log(`SpeakerSynchStrategy::process: event ${eventId} not found or invalid`);
@@ -54,7 +54,7 @@ class SpeakerSynchStrategy extends AbstractSynchStrategy {
54
54
  }
55
55
 
56
56
  // Update events where speaker is moderator
57
- for (const eventId of entity.all_moderated_presentations || []) {
57
+ for (const eventId of entity.moderated_presentations || []) {
58
58
  const res = getIndexedItem(this.allIDXEvents, eventsData, eventId);
59
59
  if (!res) {
60
60
  console.log(`SpeakerSynchStrategy::process: moderator event ${eventId} not found`);
@@ -111,4 +111,4 @@ class SpeakerSynchStrategy extends AbstractSynchStrategy {
111
111
 
112
112
  }
113
113
 
114
- export default SpeakerSynchStrategy;
114
+ export default SpeakerSynchStrategy;
@@ -1,76 +1,177 @@
1
1
  import AbstractSynchStrategy from "./abstract_synch_strategy";
2
2
  import {fetchTrackById} from "../../actions/fetch-entities-actions";
3
3
  import {
4
- BUCKET_EVENTS_DATA_KEY,
5
- BUCKET_SUMMIT_DATA_KEY,
6
- saveFile
4
+ BUCKET_EVENTS_DATA_KEY,
5
+ BUCKET_EVENTS_IDX_DATA_KEY,
6
+ BUCKET_SUMMIT_DATA_KEY,
7
+ saveFile
7
8
  } from "../../utils/dataUpdatesUtils";
8
9
  import moment from "moment-timezone";
10
+ import {rebuildIndex} from "../../utils/arrayUtils";
11
+
9
12
  /**
10
13
  * TrackSynchStrategy
11
14
  */
12
- class TrackSynchStrategy extends AbstractSynchStrategy{
15
+ class TrackSynchStrategy extends AbstractSynchStrategy {
16
+
17
+
18
+ _normalizeTracks(tracks) {
19
+ const base = Array.isArray(tracks) ? tracks : [];
20
+ return base.map(t => ({
21
+ ...t,
22
+ subtracks: Array.isArray(t?.subtracks) ? t.subtracks : []
23
+ }));
24
+ }
25
+
26
+ _touchSummitTimestamp() {
27
+ this.summit = {
28
+ ...this.summit,
29
+ timestamp: moment().unix(),
30
+ };
31
+ }
32
+
33
+ _stripEntityEverywhere(tracks, entityId) {
34
+ return this._normalizeTracks(tracks)
35
+ .filter(t => t.id !== entityId) // remove it from top level ...
36
+ // then remove it from subtracks
37
+ .map(t => ({ ...t, subtracks: t.subtracks.filter(st => st?.id !== entityId) }));
38
+ }
13
39
 
14
- async process(payload){
40
+ async _persistAll() {
41
+ this._touchSummitTimestamp();
42
+ const localNowUtc = Date.now();
43
+
44
+ await Promise.all([
45
+ saveFile(this.summit.id, BUCKET_EVENTS_DATA_KEY, this.allEvents, localNowUtc),
46
+ saveFile(this.summit.id, BUCKET_SUMMIT_DATA_KEY, this.summit, localNowUtc),
47
+ saveFile(this.summit.id, BUCKET_EVENTS_IDX_DATA_KEY, this.allIDXEvents, localNowUtc),
48
+ ]);
49
+ }
15
50
 
16
- console.log(`TrackSynchStrategy::process`, payload);
51
+ _result(payload, entity) {
52
+ return {
53
+ payload,
54
+ entity: entity,
55
+ summit: this.summit,
56
+ eventsData: this.allEvents,
57
+ allIDXEvents: this.allIDXEvents,
58
+ allSpeakers: this.allSpeakers,
59
+ allIDXSpeakers: this.allIDXSpeakers
60
+ };
61
+ }
17
62
 
18
- const {entity_operator, entity_id} = payload;
63
+ _upsertIntoParentSubtracks(tracks, parentId, entity) {
64
+ if (parentId === entity.id) {
65
+ console.warn(`TrackSynchStrategy::_upsertIntoParentSubtracks ignored self-parent for ${entity.id}`);
66
+ return tracks;
67
+ }
68
+
69
+ let found = false;
70
+ const normalizedEntity = { ...entity, subtracks: Array.isArray(entity.subtracks) ? entity.subtracks : [] };
71
+ const next = tracks.map(t => {
72
+ if (t.id !== parentId) return t;
73
+ // found the parent
74
+ found = true;
75
+ // get current idx if exists as subtrack ...
76
+ const idx = t.subtracks.findIndex(st => st?.id === entity.id);
77
+ const subtracks = idx === -1
78
+ ? [...t.subtracks, { ...normalizedEntity }] // not found it as subtrack .. then add it
79
+ : t.subtracks.map(st => (st?.id === entity.id ? { ...st, ...normalizedEntity } : st));// else update it
80
+ return { ...t, subtracks };
81
+ });
82
+ if (!found) {
83
+ console.warn(`TrackSynchStrategy::_upsertIntoParentSubtracks parent ${parentId} not found for track ${entity.id}`);
84
+ }
85
+ return next;
86
+ }
19
87
 
20
- const entity = await fetchTrackById(this.summit.id, entity_id, this.accessToken);
88
+ async _handleUpsert(entity, payload) {
89
+ // remove it from top level and subtracks
90
+ let tracks = this._stripEntityEverywhere(this.summit?.tracks, entity.id);
91
+ // top level re insert
92
+ const normalizedEntity = { ...entity, subtracks: Array.isArray(entity.subtracks) ? entity.subtracks : [] };
93
+ tracks.push(normalizedEntity)
21
94
 
22
- if (entity_operator === 'UPDATE') {
23
- if (!entity) return Promise.reject('TrackSynchStrategy::process entity not found.');
95
+ if (entity.parent_id > 0) {
96
+ // re insert or update it
97
+ tracks = this._upsertIntoParentSubtracks(tracks, entity.parent_id, entity);
98
+ }
24
99
 
25
- // update summit
100
+ // update summit racks
26
101
 
27
- this.summit = {
28
- ...this.summit,
29
- timestamp : moment().unix(),
30
- tracks : this.summit.tracks.map(t => {
31
- if(t?.id === entity_id) return {...t, ...entity};
102
+ this.summit = { ...this.summit, tracks };
32
103
 
33
- return {...t, subtracks: t.subtracks.map(st => {
34
- if(st?.id === entity_id) return {...st, ...entity};
35
- return st;
36
- })};
104
+ // update events
105
+ this.allEvents = this.allEvents.map(ev => {
106
+ if (ev?.track?.id === entity.id) return ({...ev, track: entity});
107
+ return ev;
37
108
  })
38
- };
39
- // update events
40
- this.allEvents = this.allEvents.map(ev => {
41
- if (ev?.track?.id === entity_id) return ({...ev, track: entity});
42
- return ev;
43
- })
44
109
 
45
- // update files on cache
46
- console.log(`TrackSynchStrategy::process updating cache files`);
110
+ // Rebuild index to keep positions in sync
111
+ this.allIDXEvents = rebuildIndex(this.allEvents);
112
+ // Persist
113
+ try {
114
+ console.log("TrackSynchStrategy::process updating cache files (upsert)");
115
+ await this._persistAll();
116
+ } catch (e) {
117
+ console.error(e);
118
+ }
119
+
120
+ return this._result(payload, entity);
121
+ }
47
122
 
48
- try {
49
- const localNowUtc = Date.now();
123
+ async _handleDelete(entityId, payload) {
124
+ console.log(
125
+ `TrackSynchStrategy::_handleDelete delete track ${entityId}`
126
+ );
127
+ // remove track from top level / childs
128
+ let tracks = this._stripEntityEverywhere(this.summit?.tracks, entityId);
129
+ // clean orphan childs ...
130
+ tracks = tracks.map(t => (t?.parent_id === entityId ? { ...t, parent_id: 0 } : t));
131
+
132
+ this.summit = {
133
+ ...this.summit,
134
+ tracks: [...tracks],
135
+ };
136
+
137
+ // update events remove events with that track
138
+ this.allEvents = this.allEvents.filter(ev => ev.track?.id !== entityId);
139
+ // Rebuild index to keep positions in sync
140
+ this.allIDXEvents = rebuildIndex(this.allEvents);
141
+ // Persist
142
+ try {
143
+ console.log("TrackSynchStrategy::_handleDelete updating cache files (delete)");
144
+ await this._persistAll();
145
+ } catch (e) {
146
+ console.error(e);
147
+ }
148
+
149
+ // No entity on delete/unpublish
150
+ return this._result(payload, null);
151
+ }
50
152
 
51
- await saveFile(this.summit.id, BUCKET_EVENTS_DATA_KEY, this.allEvents, localNowUtc);
52
- await saveFile(this.summit.id, BUCKET_SUMMIT_DATA_KEY, this.summit, localNowUtc);
153
+ async process(payload) {
53
154
 
54
- }
55
- catch (e){
56
- console.log(e);
57
- }
155
+ console.log(`TrackSynchStrategy::process`, payload);
58
156
 
59
- let res = {
60
- payload,
61
- entity,
62
- summit : this.summit,
63
- eventsData: this.allEvents,
64
- allIDXEvents : this.allIDXEvents,
65
- allSpeakers : this.allSpeakers,
66
- allIDXSpeakers : this.allIDXSpeakers
67
- };
157
+ const {entity_operator, entity_id} = payload;
68
158
 
69
- console.log(`TrackSynchStrategy::process done`, res);
159
+ switch (entity_operator) {
160
+ case 'INSERT':
161
+ case 'UPDATE':{
162
+ const entity = await fetchTrackById(this.summit.id, entity_id, this.accessToken);
163
+ if (!entity) return Promise.reject('TrackSynchStrategy::process entity not found.');
164
+ return this._handleUpsert(entity, payload);
165
+ }
166
+ case 'DELETE':{
167
+ return this._handleDelete(entity_id, payload);
168
+ }
169
+ }
70
170
 
71
- return Promise.resolve(res);
171
+ const msg = `TrackSynchStrategy::process unknown entity_operator '${entity_operator}'`;
172
+ console.warn(msg);
173
+ throw new Error(msg);
72
174
  }
73
- }
74
175
  }
75
176
 
76
177
  export default TrackSynchStrategy;