@nascentdigital/funnel-editor-contentful 5.0.1 → 5.0.3

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 (164) hide show
  1. package/dist/EditorApp.js +11 -9
  2. package/dist/EditorPageRouter.d.ts +1 -0
  3. package/dist/EditorPageRouter.js +34 -0
  4. package/dist/EditorPageRoutes.d.ts +1 -0
  5. package/dist/EditorPageRoutes.js +16 -0
  6. package/dist/EntryEditor.js +30 -29
  7. package/dist/EntrySidebar.js +26 -5
  8. package/dist/NotFound.d.ts +1 -0
  9. package/dist/NotFound.js +18 -0
  10. package/dist/asset/index.d.ts +1 -0
  11. package/dist/{core/api → asset}/index.js +1 -3
  12. package/dist/{core → asset}/repository/AssetRepository.d.ts +11 -3
  13. package/dist/{core → asset}/repository/AssetRepository.js +6 -5
  14. package/dist/asset/repository/index.d.ts +1 -0
  15. package/dist/{core → asset}/repository/index.js +0 -4
  16. package/dist/collection/controller/{LocalCollectionController.d.ts → ContentfulCollectionController.d.ts} +17 -11
  17. package/dist/collection/controller/ContentfulCollectionController.js +278 -0
  18. package/dist/collection/controller/ContentfulCollectionControllerContainer.d.ts +5 -0
  19. package/dist/collection/controller/ContentfulCollectionControllerContainer.js +28 -0
  20. package/dist/collection/controller/index.d.ts +3 -4
  21. package/dist/collection/controller/index.js +3 -4
  22. package/dist/collection/controller/useContentfulCollectionController.d.ts +2 -0
  23. package/dist/collection/controller/useContentfulCollectionController.js +8 -0
  24. package/dist/{core → collection}/domain/Collection.d.ts +11 -2
  25. package/dist/{core → collection}/domain/Collection.js +76 -0
  26. package/dist/{core → collection}/domain/index.d.ts +0 -1
  27. package/dist/{core → collection}/domain/index.js +0 -1
  28. package/dist/collection/index.d.ts +1 -0
  29. package/dist/collection/index.js +1 -0
  30. package/dist/collection/repository/CollectionRepository.d.ts +49 -0
  31. package/dist/collection/repository/CollectionRepository.js +223 -0
  32. package/dist/collection/repository/index.d.ts +1 -0
  33. package/dist/{core/contentful → collection/repository}/index.js +1 -5
  34. package/dist/collection/screens/CollectionEditor.js +23 -6
  35. package/dist/core/bridge/AppBridge.d.ts +31 -0
  36. package/dist/core/bridge/AppBridge.js +160 -0
  37. package/dist/core/bridge/AppBridgeContainer.d.ts +3 -3
  38. package/dist/core/bridge/AppBridgeContainer.js +8 -44
  39. package/dist/core/bridge/AppBridgeContext.d.ts +7 -8
  40. package/dist/core/bridge/AppBridgeContext.js +1 -6
  41. package/dist/core/bridge/BridgeLifecycleMessage.d.ts +7 -0
  42. package/dist/core/bridge/BridgeLifecycleMessage.js +12 -0
  43. package/dist/core/bridge/IAppBridgeMessage.d.ts +16 -0
  44. package/dist/core/bridge/IAppBridgeMessage.js +12 -0
  45. package/dist/core/bridge/index.d.ts +2 -1
  46. package/dist/core/bridge/index.js +2 -1
  47. package/dist/core/bridge/useAppBridge.d.ts +2 -8
  48. package/dist/core/bridge/useAppBridge.js +24 -15
  49. package/dist/core/client/ContentfulClientContainer.d.ts +8 -0
  50. package/dist/core/client/ContentfulClientContainer.js +43 -0
  51. package/dist/core/client/ContentfulClientContext.d.ts +8 -0
  52. package/dist/core/client/ContentfulClientContext.js +8 -0
  53. package/dist/core/client/index.d.ts +3 -0
  54. package/dist/core/client/index.js +19 -0
  55. package/dist/core/client/useContentfulClient.d.ts +4 -0
  56. package/dist/core/client/useContentfulClient.js +24 -0
  57. package/dist/core/index.d.ts +1 -4
  58. package/dist/core/index.js +1 -4
  59. package/dist/explore/components/header/Header.d.ts +14 -0
  60. package/dist/explore/components/header/Header.js +33 -0
  61. package/dist/explore/components/header/HeaderActions.d.ts +8 -0
  62. package/dist/explore/components/header/HeaderActions.js +37 -0
  63. package/dist/explore/components/header/HeaderContainer.d.ts +2 -0
  64. package/dist/explore/components/header/HeaderContainer.js +15 -0
  65. package/dist/explore/components/header/HeaderContext.d.ts +7 -0
  66. package/dist/{core/api/EditorApiContext.js → explore/components/header/HeaderContext.js} +2 -2
  67. package/dist/explore/components/header/HeaderController.d.ts +39 -0
  68. package/dist/explore/components/header/HeaderController.js +100 -0
  69. package/dist/explore/components/header/HeaderOptions.d.ts +10 -0
  70. package/dist/explore/components/header/HeaderOptions.js +26 -0
  71. package/dist/explore/components/header/index.d.ts +5 -0
  72. package/dist/explore/components/header/index.js +21 -0
  73. package/dist/explore/components/header/useHeader.d.ts +2 -0
  74. package/dist/explore/components/header/useHeader.js +30 -0
  75. package/dist/explore/components/index.d.ts +2 -0
  76. package/dist/explore/components/index.js +18 -0
  77. package/dist/explore/components/pages/PagesExplorerLayout.d.ts +4 -0
  78. package/dist/explore/components/pages/PagesExplorerLayout.js +28 -0
  79. package/dist/explore/components/pages/index.d.ts +1 -0
  80. package/dist/explore/components/pages/index.js +17 -0
  81. package/dist/explore/index.d.ts +2 -0
  82. package/dist/explore/index.js +18 -0
  83. package/dist/explore/screens/Dashboard.d.ts +4 -0
  84. package/dist/explore/screens/Dashboard.js +55 -0
  85. package/dist/explore/screens/PageExplorer.d.ts +11 -0
  86. package/dist/explore/screens/PageExplorer.js +129 -0
  87. package/dist/explore/screens/PagesExplorer.d.ts +7 -0
  88. package/dist/explore/screens/PagesExplorer.js +98 -0
  89. package/dist/explore/screens/index.d.ts +3 -0
  90. package/dist/explore/screens/index.js +19 -0
  91. package/dist/page/bridge/BlockChangedMessage.d.ts +25 -0
  92. package/dist/page/bridge/BlockChangedMessage.js +18 -0
  93. package/dist/page/bridge/PageBridgeHub.d.ts +8 -0
  94. package/dist/page/bridge/PageBridgeHub.js +51 -0
  95. package/dist/page/bridge/index.d.ts +2 -0
  96. package/dist/page/bridge/index.js +18 -0
  97. package/dist/page/controller/{LocalPageController.d.ts → ContentfulPageController.d.ts} +15 -9
  98. package/dist/page/controller/ContentfulPageController.js +277 -0
  99. package/dist/page/controller/ContentfulPageControllerContainer.d.ts +5 -0
  100. package/dist/page/controller/ContentfulPageControllerContainer.js +28 -0
  101. package/dist/page/controller/index.d.ts +3 -3
  102. package/dist/page/controller/index.js +3 -3
  103. package/dist/page/controller/useContentfulPageController.d.ts +2 -0
  104. package/dist/page/controller/useContentfulPageController.js +8 -0
  105. package/dist/{core → page}/domain/Page.d.ts +23 -12
  106. package/dist/{core → page}/domain/Page.js +139 -0
  107. package/dist/page/domain/index.d.ts +1 -0
  108. package/dist/page/domain/index.js +17 -0
  109. package/dist/page/index.d.ts +3 -0
  110. package/dist/page/index.js +3 -0
  111. package/dist/page/repository/PageRepository.d.ts +45 -0
  112. package/dist/page/repository/PageRepository.js +241 -0
  113. package/dist/page/repository/index.d.ts +1 -0
  114. package/dist/page/repository/index.js +17 -0
  115. package/dist/page/screens/PageEditor.js +36 -12
  116. package/package.json +9 -5
  117. package/dist/collection/controller/LocalCollectionController.js +0 -200
  118. package/dist/collection/controller/LocalCollectionControllerContainer.d.ts +0 -8
  119. package/dist/collection/controller/LocalCollectionControllerContainer.js +0 -36
  120. package/dist/collection/controller/LocalCollectionControllerContext.d.ts +0 -7
  121. package/dist/collection/controller/LocalCollectionControllerContext.js +0 -6
  122. package/dist/collection/controller/useLocalCollectionController.d.ts +0 -2
  123. package/dist/collection/controller/useLocalCollectionController.js +0 -10
  124. package/dist/core/api/EditorApiClient.d.ts +0 -7
  125. package/dist/core/api/EditorApiClient.js +0 -29
  126. package/dist/core/api/EditorApiContainer.d.ts +0 -5
  127. package/dist/core/api/EditorApiContainer.js +0 -16
  128. package/dist/core/api/EditorApiContext.d.ts +0 -7
  129. package/dist/core/api/index.d.ts +0 -3
  130. package/dist/core/api/useEditorApi.d.ts +0 -2
  131. package/dist/core/api/useEditorApi.js +0 -12
  132. package/dist/core/bridge/AppMessage.d.ts +0 -19
  133. package/dist/core/bridge/AppMessage.js +0 -19
  134. package/dist/core/contentful/Contentful.d.ts +0 -33
  135. package/dist/core/contentful/Contentful.js +0 -67
  136. package/dist/core/contentful/ContentfulAsset.d.ts +0 -23
  137. package/dist/core/contentful/ContentfulAsset.js +0 -88
  138. package/dist/core/contentful/ContentfulCollection.d.ts +0 -29
  139. package/dist/core/contentful/ContentfulCollection.js +0 -97
  140. package/dist/core/contentful/ContentfulPage.d.ts +0 -31
  141. package/dist/core/contentful/ContentfulPage.js +0 -84
  142. package/dist/core/contentful/ContentfulRefs.d.ts +0 -16
  143. package/dist/core/contentful/ContentfulRefs.js +0 -152
  144. package/dist/core/contentful/index.d.ts +0 -5
  145. package/dist/core/repository/CollectionSerializer.d.ts +0 -9
  146. package/dist/core/repository/CollectionSerializer.js +0 -41
  147. package/dist/core/repository/LocalCollectionRepository.d.ts +0 -27
  148. package/dist/core/repository/LocalCollectionRepository.js +0 -143
  149. package/dist/core/repository/LocalPageRepository.d.ts +0 -16
  150. package/dist/core/repository/LocalPageRepository.js +0 -153
  151. package/dist/core/repository/PageSerializer.d.ts +0 -9
  152. package/dist/core/repository/PageSerializer.js +0 -47
  153. package/dist/core/repository/RemoteCollectionRepository.d.ts +0 -19
  154. package/dist/core/repository/RemoteCollectionRepository.js +0 -236
  155. package/dist/core/repository/RemotePageRepository.d.ts +0 -14
  156. package/dist/core/repository/RemotePageRepository.js +0 -113
  157. package/dist/core/repository/index.d.ts +0 -5
  158. package/dist/page/controller/LocalPageController.js +0 -190
  159. package/dist/page/controller/LocalPageControllerContainer.d.ts +0 -8
  160. package/dist/page/controller/LocalPageControllerContainer.js +0 -36
  161. package/dist/page/controller/LocalPageControllerContext.d.ts +0 -7
  162. package/dist/page/controller/LocalPageControllerContext.js +0 -6
  163. package/dist/page/controller/useLocalPageController.d.ts +0 -2
  164. package/dist/page/controller/useLocalPageController.js +0 -10
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.PageRepository = void 0;
15
+ const contentful_1 = require("@nascentdigital/funnel-cms/contentful");
16
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
17
+ const components_1 = require("@nascentdigital/funnel-editor/components");
18
+ const domain_1 = require("../domain");
19
+ // repository
20
+ class PageRepository {
21
+ constructor(args) {
22
+ // initialize instance
23
+ this.sdk = args.sdk;
24
+ this.contentfulClient = args.contentfulClient;
25
+ }
26
+ async search(args) {
27
+ // execute search
28
+ const { template } = args, query = __rest(args, ["template"]);
29
+ const _a = await this.contentfulClient.pages.search(Object.assign(Object.assign({}, query), { version: 'draft', brand: funnel_core_1.AppConfig.brand(), type: template.type })), { items } = _a, result = __rest(_a, ["items"]);
30
+ // return items
31
+ return Object.assign(Object.assign({}, result), { items: items.map(item => components_1.IPage.Ref.fromData(item)) });
32
+ }
33
+ async getOverview({ region }) {
34
+ // get overviews
35
+ const overview = await this.contentfulClient.pages.getOverview({
36
+ brand: funnel_core_1.AppConfig.brand(),
37
+ region,
38
+ version: 'draft'
39
+ });
40
+ // enrich with templates and flip to array
41
+ const templates = Object.values(funnel_core_1.Schema.Site.current().regions[region]);
42
+ return templates.map(template => {
43
+ var _a, _b;
44
+ // extract static page overview
45
+ const { type } = template;
46
+ if (template.variant === 'static') {
47
+ return new components_1.StaticPageOverview(Object.assign({ template,
48
+ type, refs: {} }, ((_a = overview.static.find(page => page.type === type)) !== null && _a !== void 0 ? _a : {})));
49
+ }
50
+ // or dynamic one
51
+ else {
52
+ return new components_1.DynamicPageOverview(Object.assign({ template,
53
+ type, count: 0 }, ((_b = overview.dynamic.find(page => page.type === type)) !== null && _b !== void 0 ? _b : {})));
54
+ }
55
+ });
56
+ }
57
+ async create(args) {
58
+ throw new funnel_core_1.NotImplementedError();
59
+ }
60
+ async createLanguage(args) {
61
+ throw new funnel_core_1.NotImplementedError();
62
+ }
63
+ async load(sys) {
64
+ // guard method
65
+ const { sdk, contentfulClient } = this;
66
+ if (!('entry' in sdk)) {
67
+ throw new funnel_core_1.InvalidOperationError('Attempt to call load() outside of EntryEditor');
68
+ }
69
+ // throw if entry isn't a page (it should be in this case)
70
+ const { entry } = sdk;
71
+ sys = sys !== null && sys !== void 0 ? sys : entry.getSys();
72
+ const { id: entryId, contentType: { sys: { id: entryType } } } = sys;
73
+ if (entryType !== contentful_1.ContentfulPageData.contentType) {
74
+ throw new funnel_core_1.ArgumentError(`Cannot load Page from entry that isn't of "${contentful_1.ContentfulPageData.contentType}" type`);
75
+ }
76
+ // throw if page doesn't have a group id
77
+ const pageId = entryId;
78
+ const type = entry.fields.type.getValue();
79
+ const groupId = entry.fields.groupId.getValue();
80
+ const brand = entry.fields.brand.getValue();
81
+ const region = entry.fields.region.getValue();
82
+ const language = funnel_core_1.Language.tryParse(entry.fields.language.getValue());
83
+ if (!groupId || !type || !brand || !region || !language) {
84
+ throw new funnel_core_1.ArgumentError(`Page "${pageId}" is not configured (missing required fields)`);
85
+ }
86
+ // fetch remote data (throw if not found)
87
+ const _a = await contentfulClient.pages.getGroup({ groupId, type, brand, region, language, version: 'draft' }), _b = _a.group, { instances: pages } = _b, group = __rest(_b, ["instances"]), { page: pageResult } = _a;
88
+ if (!pageResult) {
89
+ throw new funnel_core_1.NotFoundError(`Unable to resolve Page with id "${pageId}"`);
90
+ }
91
+ console.debug('[PageRepository/load] loaded page data:', { group, pages, pageResult });
92
+ // get entry data + remote references
93
+ const { data: _remote } = pageResult, references = __rest(pageResult, ["data"]);
94
+ const data = entryToPageData(entry);
95
+ // return page model from data
96
+ const page = domain_1.Page.fromSchemaData({ data, references });
97
+ return {
98
+ pageSummary: new components_1.PageSummary(Object.assign(Object.assign({ template: page.template }, group), { pages })),
99
+ page,
100
+ sys
101
+ };
102
+ }
103
+ async save(page) {
104
+ // guard method
105
+ const { sdk } = this;
106
+ if (!('entry' in sdk)) {
107
+ throw new funnel_core_1.InvalidOperationError('Attempt to call save() outside of EntryEditor');
108
+ }
109
+ // serialize page updates (or skip if there are no changes)
110
+ const savables = [];
111
+ const updateArgs = domain_1.Page.toUpdates(page, savables);
112
+ if (updateArgs.updates.length === 0) {
113
+ return page;
114
+ }
115
+ // start all updates
116
+ page.onSaveStart();
117
+ savables.forEach(savable => savable.onSaveStart());
118
+ // update entry if page is local
119
+ let updatedOn;
120
+ const { entry } = sdk;
121
+ const { id: entryId, contentType: { sys: { id: entryType } } } = entry.getSys();
122
+ if (entryType === contentful_1.ContentfulPageData.contentType && page.id === entryId) {
123
+ console.debug('[PageRepository/save] saving local entry', { entry, page });
124
+ // save updates to entry
125
+ await saveEntryUpdates({ entry, updates: updateArgs.updates });
126
+ // capture updated timestamp
127
+ updatedOn = new Date(entry.getSys().updatedAt).getTime();
128
+ }
129
+ // or update remotely
130
+ else {
131
+ console.debug('[PageRepository/save] saving remote entry', page);
132
+ // push updates remotely
133
+ const result = await this.contentfulClient.pages.update(updateArgs);
134
+ // capture update timestamp
135
+ updatedOn = result.updatedOn;
136
+ // swap to new page (if update returned new data)
137
+ if (result.page) {
138
+ console.debug('[PageRepository/save] switching to new page data', result.page);
139
+ const _a = result.page, { data } = _a, references = __rest(_a, ["data"]);
140
+ page = domain_1.Page.fromSchemaData({ data, references });
141
+ }
142
+ }
143
+ console.debug('[PageRepository/save] finalizing save');
144
+ // complete updates
145
+ savables.forEach(savable => savable.onSaveComplete());
146
+ page.onSaveComplete(updatedOn);
147
+ console.debug('[PageRepository/save] save complete', page);
148
+ // return page
149
+ return page;
150
+ }
151
+ async sync({ page, sys }) {
152
+ // guard method
153
+ const { sdk } = this;
154
+ if (!('entry' in sdk)) {
155
+ throw new funnel_core_1.InvalidOperationError('Attempt to call sync() outside of EntryEditor');
156
+ }
157
+ // skip if entity version and updated sys are same
158
+ const { entry } = sdk;
159
+ const entrySys = entry.getSys();
160
+ if (sys.version === entrySys.version) {
161
+ console.debug('[ContentfulPageRepository/sync] ignoring unchanged entity');
162
+ return { page, sys };
163
+ }
164
+ // force reload if entry references changed
165
+ const entryReferences = entry.fields.references.getValue();
166
+ const entryRefs = new Set(entryReferences.map(link => `${link.sys.linkType}.${link.sys.id}`));
167
+ const { references: pageReferences } = page;
168
+ const pageRefs = new Set(pageReferences.map(link => `${link.sys.linkType}.${link.sys.id}`));
169
+ if (!pageRefs.isSupersetOf(entryRefs)) {
170
+ console.debug('[ContentfulPageRepository/sync] references changed - triggering reload');
171
+ return this.load(sys);
172
+ }
173
+ console.debug('[ContentfulPageRepository/sync] reusing stable references - merging entity');
174
+ // or load page data, but use old references in store
175
+ const data = entryToPageData(entry);
176
+ const references = {
177
+ files: page.assets.reduce((files, file) => {
178
+ files[file.value.id] = file;
179
+ return files;
180
+ }, {}),
181
+ collections: page.collections.reduce((collections, collection) => {
182
+ collections[collection.id] = collection;
183
+ return collections;
184
+ }, {}),
185
+ pages: page.pages.reduce((pages, page) => {
186
+ pages[page.id] = page;
187
+ return pages;
188
+ }, {})
189
+ };
190
+ // create new page (use existing sys)
191
+ return {
192
+ page: domain_1.Page.fromSchemaData({ data, references }),
193
+ sys
194
+ };
195
+ }
196
+ async unpublish(page) {
197
+ throw new funnel_core_1.NotImplementedError();
198
+ }
199
+ }
200
+ exports.PageRepository = PageRepository;
201
+ // helpers
202
+ function entryToPageData(entry) {
203
+ // throw if it's not a page
204
+ const entryType = entry.getSys().contentType.sys.id;
205
+ if (entryType !== contentful_1.ContentfulPageData.contentType) {
206
+ throw new funnel_core_1.ArgumentError(`Expected entry to be "${contentful_1.ContentfulPageData.contentType}", but found "${entryType}"`);
207
+ }
208
+ // serialize entry (this is the latest data)
209
+ const entryData = contentful_1.ContentfulData.fromEntryData(entry);
210
+ return contentful_1.ContentfulPageData.deserialize(entryData);
211
+ }
212
+ async function saveEntryUpdates({ entry, updates }) {
213
+ var _a;
214
+ // serialize entry (this is the latest data)
215
+ const pageData = entryToPageData(entry);
216
+ // create model and apply updates
217
+ const pageModel = new funnel_core_1.Schema.PageModel(pageData);
218
+ pageModel.update(updates);
219
+ pageModel.sync();
220
+ // resolve references
221
+ const references = contentful_1.ContentfulData.findReferences([pageData.discover, pageData.content]);
222
+ // update fields
223
+ const { fields } = entry;
224
+ await Promise.all([
225
+ // required updates
226
+ fields.name.setValue(pageData.name),
227
+ fields.slug.setValue(pageData.slug),
228
+ fields.url.setValue(pageData.url),
229
+ fields.state.setValue(pageData.state),
230
+ fields.errors.setValue(pageData.errors),
231
+ fields.references.setValue(references),
232
+ // TODO: save only fields that changed
233
+ // conditional updates
234
+ fields.tags.setValue((_a = pageData.tags) !== null && _a !== void 0 ? _a : []),
235
+ fields.preview.setValue(pageData.preview),
236
+ fields.discover.setValue(pageData.discover),
237
+ fields.content.setValue(pageData.content)
238
+ ]);
239
+ // save entry
240
+ await entry.save();
241
+ }
@@ -0,0 +1 @@
1
+ export * from './PageRepository';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./PageRepository"), exports);
@@ -2,34 +2,57 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PageEditor = PageEditor;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
5
6
  const components_1 = require("@nascentdigital/funnel-editor/components");
7
+ const hooks_1 = require("@nascentdigital/funnel-ui/hooks");
6
8
  const react_1 = require("react");
7
- const core_1 = require("../../core");
9
+ const bridge_1 = require("../bridge");
8
10
  const controller_1 = require("../controller");
9
11
  // component
10
12
  function PageEditor() {
11
13
  // hooks
12
- const bridge = (0, core_1.useAppBridge)();
13
- const controllerContext = (0, controller_1.useLocalPageController)();
14
- const { controller } = controllerContext;
14
+ const splash = (0, components_1.useSplash)();
15
+ const controller = (0, controller_1.useContentfulPageController)();
15
16
  const { page } = controller;
16
17
  const pageContext = (0, react_1.useMemo)(() => ({ page }), [page]);
17
18
  const [itemId, setItemId] = (0, react_1.useState)();
18
19
  const selectedItem = (0, react_1.useMemo)(() => page === null || page === void 0 ? void 0 : page.coerceItem(itemId), [page, itemId]);
19
- // sync with controller
20
- (0, components_1.usePageController)(controller);
20
+ const { ready } = controller;
21
+ const [setBlockConfigurationRef, blockConfigurationRef] = (0, hooks_1.useRefValue)();
22
+ const blockConfiguration = (0, components_1.useBlockConfiguration$)(blockConfigurationRef === null || blockConfigurationRef === void 0 ? void 0 : blockConfigurationRef.configuration$);
21
23
  // load
22
24
  (0, react_1.useEffect)(() => {
23
- controller.load().catch(console.error);
24
- }, [controller]);
25
+ controller.load().catch(e => {
26
+ // transform error
27
+ const error = funnel_core_1.IsometricErrorUtils.coerceError(e);
28
+ // TODO: come up with an appropriate message
29
+ const message = error.message;
30
+ // show error via splash
31
+ splash.showError({ message, onRetry: () => controller.reload() });
32
+ });
33
+ }, [splash, controller]);
34
+ // dismiss splash when ready
35
+ (0, react_1.useEffect)(() => {
36
+ if (ready) {
37
+ splash.dismiss();
38
+ }
39
+ else {
40
+ splash.showLoading('Fetching content');
41
+ }
42
+ }, [splash, ready]);
25
43
  const onSelectItem = (item) => {
26
44
  setItemId(item.identifier);
27
- bridge.sendMessage(core_1.AppBlockNavMessage.create(item.identifier));
28
45
  };
29
46
  // render
30
- return ((0, jsx_runtime_1.jsx)(components_1.PageControllerContext.Provider, { value: controllerContext, children: (0, jsx_runtime_1.jsx)(components_1.PageContext.Provider, { value: pageContext, children: (0, jsx_runtime_1.jsx)(components_1.AssetStoreContext.Provider, { value: page, children: (0, jsx_runtime_1.jsx)(components_1.CollectionStoreContext.Provider, { value: page, children: (0, jsx_runtime_1.jsx)(components_1.PageRefStoreContext.Provider, { value: page, children: (0, jsx_runtime_1.jsx)("div", { className: "bg-background flex h-dvh w-dvw flex-col", children: (0, jsx_runtime_1.jsx)("div", { className: "relative flex-1", children: (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 flex", children: [(0, jsx_runtime_1.jsx)("section", { className: "scrollbar-thin w-[220px] shrink-0 overflow-y-scroll lg:w-[240px] xl:w-[280px]", tabIndex: -1, children: (0, jsx_runtime_1.jsx)(components_1.PageNav, { selectedItem: selectedItem, onSelectItem: onSelectItem }) }), (0, jsx_runtime_1.jsx)("section", { className: "mx-auto h-full max-h-full max-w-[1000px] flex-1", children: renderContent({ controller, page, selectedItem, onSelectItem }) })] }) }) }) }) }) }) }) }));
47
+ return ((0, jsx_runtime_1.jsx)(components_1.PageContext.Provider, { value: pageContext, children: (0, jsx_runtime_1.jsx)(components_1.AssetStoreContext.Provider, { value: page, children: (0, jsx_runtime_1.jsx)(components_1.CollectionStoreContext.Provider, { value: page, children: (0, jsx_runtime_1.jsx)(components_1.PageRefStoreContext.Provider, { value: page, children: (0, jsx_runtime_1.jsx)(components_1.BlockConfigurationProvider, { ref: setBlockConfigurationRef, item: selectedItem, children: (0, jsx_runtime_1.jsx)(bridge_1.PageBridgeHub, { selectedItem: selectedItem, children: (0, jsx_runtime_1.jsx)("div", { className: "h-dvh w-dvw flex flex-col bg-background", children: (0, jsx_runtime_1.jsx)("div", { className: "relative flex-1", children: (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 flex", children: [(0, jsx_runtime_1.jsx)("section", { className: "scrollbar-thin w-[220px] shrink-0 overflow-y-scroll lg:w-[240px] xl:w-[280px]", tabIndex: -1, children: (0, jsx_runtime_1.jsx)(components_1.PageNav, { selectedItem: selectedItem, onSelectItem: onSelectItem }) }), (0, jsx_runtime_1.jsx)("section", { className: "mx-auto h-full max-h-full max-w-[1000px] flex-1", children: renderContent({
48
+ controller,
49
+ page,
50
+ blockConfiguration,
51
+ selectedItem,
52
+ onSelectItem
53
+ }) })] }) }) }) }) }) }) }) }) }));
31
54
  }
32
- function renderContent({ controller, page, selectedItem, onSelectItem }) {
55
+ function renderContent({ controller, page, blockConfiguration, selectedItem, onSelectItem }) {
33
56
  // show loader if not ready
34
57
  const { pageSummary } = controller;
35
58
  if (!controller.ready) {
@@ -53,6 +76,7 @@ function renderContent({ controller, page, selectedItem, onSelectItem }) {
53
76
  }
54
77
  // or render block editor
55
78
  else {
56
- return ((0, jsx_runtime_1.jsx)(components_1.BlockEditor, { className: "h-full max-h-full", block: selectedItem, onSelectItem: onSelectItem }));
79
+ return ((0, jsx_runtime_1.jsx)(components_1.BlockEditor, { className: "h-full max-h-full", block: selectedItem, headerActions: (blockConfiguration === null || blockConfiguration === void 0 ? void 0 : blockConfiguration.hasPanels) &&
80
+ (0, jsx_runtime_1.jsx)(components_1.BlockConfigButton, { target: "block" }), onSelectItem: onSelectItem }));
57
81
  }
58
82
  }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@nascentdigital/funnel-editor-contentful",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
- "access": "restricted"
6
+ "access": "public"
7
7
  },
8
8
  "repository": {
9
9
  "url": "git+https://github.com/nascentdigital/funnel.git",
@@ -37,9 +37,13 @@
37
37
  "react-dom": "^19"
38
38
  },
39
39
  "dependencies": {
40
- "@contentful/app-sdk": "^4.69.0",
40
+ "@contentful/app-sdk": "^4",
41
41
  "@contentful/react-apps-toolkit": "^2.0.1",
42
- "@nascentdigital/funnel-editor": "^5.0.1",
42
+ "@nascentdigital/funnel-cms": "^5.0.3",
43
+ "@nascentdigital/funnel-editor": "^5.0.3",
44
+ "react-dom": "^19",
45
+ "react-router": "^8",
46
+ "react-router-dom": "^7",
43
47
  "rxjs": "^7",
44
48
  "uuid": "^11"
45
49
  },
@@ -55,5 +59,5 @@
55
59
  "ts-jest": "^29",
56
60
  "type-fest": "^4"
57
61
  },
58
- "gitHead": "6ac66e04e5d6d10b959826b84cf3827422ddf47b"
62
+ "gitHead": "0d1cc7d78504e8819998d928ffb342d622f75690"
59
63
  }
@@ -1,200 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LocalCollectionController = void 0;
4
- const funnel_core_1 = require("@nascentdigital/funnel-core");
5
- const components_1 = require("@nascentdigital/funnel-editor/components");
6
- const rxjs_1 = require("rxjs");
7
- // class
8
- class LocalCollectionController {
9
- constructor({ localRepository, remoteRepository }) {
10
- // initialize instance variables
11
- this.localRepository = localRepository;
12
- this.remoteRepository = remoteRepository;
13
- this._eventBus = new funnel_core_1.EventBus();
14
- this._state = 'uninitialized';
15
- }
16
- get events$() {
17
- return this._eventBus.events$
18
- .pipe((0, rxjs_1.filter)(e => components_1.ICollectionController.Event.filter(e) || components_1.ICollection.Event.filter(e)));
19
- }
20
- get state() {
21
- return this._state;
22
- }
23
- set state(value) {
24
- // skip if unchanged
25
- if (this._state === value) {
26
- console.debug('[CollectionController/state] skipping redundant update', value);
27
- return;
28
- }
29
- // or update
30
- this._state = value;
31
- console.debug('[CollectionController/state] updated state', value);
32
- // raise event
33
- this.raiseEvent('state.changed');
34
- }
35
- get ready() {
36
- const { _state } = this;
37
- return _state !== 'uninitialized' && _state !== 'initializing';
38
- }
39
- get languages() {
40
- const brand = funnel_core_1.AppConfig.brandConfig();
41
- return funnel_core_1.RegionsConfig.getLanguages(brand.regionalization);
42
- }
43
- get collectionGroup() {
44
- return this._collectionGroup;
45
- }
46
- set collectionGroup(collectionGroup) {
47
- // skip if group is the same
48
- if (this._collectionGroup === collectionGroup) {
49
- return;
50
- }
51
- // update group
52
- this._collectionGroup = collectionGroup;
53
- // raise event
54
- this.raiseEvent('collectionGroup.changed');
55
- }
56
- get collection() {
57
- return this._collection;
58
- }
59
- set collection(collection) {
60
- // skip if collection is the same
61
- if (this._collection === collection) {
62
- console.debug('[CE][CollectionController] ignoring collection change for', collection);
63
- return;
64
- }
65
- // unbind previous subscription (if any)
66
- if (this._subscription) {
67
- this._subscription.unsubscribe();
68
- delete this._subscription;
69
- }
70
- console.debug('[CE][CollectionController] updating to new collection', collection);
71
- // update collection
72
- this._collection = collection;
73
- // subscribe to collection changes
74
- if (collection) {
75
- this._subscription = collection.events$
76
- .pipe((0, rxjs_1.filter)(e => {
77
- // handle collection events
78
- if (components_1.ICollection.Event.filter(e)) {
79
- return e.type === 'modified';
80
- }
81
- // or item/object/list events
82
- else if (components_1.Item.Event.filter(e)
83
- || components_1.Item.Event.filter(e)
84
- || funnel_core_1.ObjectField.Event.filter(e)
85
- || funnel_core_1.FieldList.Event.filter(e)) {
86
- return e.type === 'changed';
87
- }
88
- // or field
89
- else {
90
- return funnel_core_1.Field.Event.filter(e) && (e.type === 'changed' || e.type === 'reset');
91
- }
92
- }), (0, rxjs_1.debounce)(e => {
93
- if ((components_1.ICollection.Event.filter(e) && e.type === 'modified')
94
- || components_1.Item.Event.filter(e)
95
- || funnel_core_1.FieldList.Event.filter(e)
96
- || (funnel_core_1.Field.Event.filter(e) && e.field.isComposite)) {
97
- return (0, rxjs_1.interval)(0);
98
- }
99
- else {
100
- return (0, rxjs_1.interval)(2000);
101
- }
102
- }))
103
- .subscribe(async () => {
104
- if (collection.modified && this._collection === collection) {
105
- console.debug('[CE][CollectionController] saving modified collection', collection);
106
- await this.save(collection);
107
- }
108
- else {
109
- console.debug('[CE][CollectionController] ignoring change for zombie collection', collection);
110
- }
111
- });
112
- }
113
- // raise event
114
- this.raiseEvent('collection.changed');
115
- }
116
- async sync(updatedOn) {
117
- // get current collection
118
- const collection = this._collection;
119
- // bail if no collection
120
- if (!collection) {
121
- console.warn('[CE][CollectionController] tried to sync with no collection');
122
- return;
123
- }
124
- // skip if page already in sync
125
- if (collection.updatedOn >= updatedOn) {
126
- console.debug('[CE][CollectionController] skipping sync, collection already in sync');
127
- return;
128
- }
129
- // skip if saving already
130
- if (this.state === 'saving') {
131
- console.debug('[CE][CollectionController] skipping sync while saving');
132
- return;
133
- }
134
- // start fresh load
135
- console.debug('[CE][CollectionController] syncing, page out of date...');
136
- await this.load();
137
- }
138
- async load() {
139
- // mark as loading (or initialing if loading for first time)
140
- this.state = this._state === 'uninitialized'
141
- ? 'initializing'
142
- : 'loading';
143
- // try to fetch collection
144
- try {
145
- // load collection
146
- const collection = await this.localRepository.load({ eventBus: this._eventBus });
147
- // get collection references
148
- if (collection) {
149
- const references = await this.remoteRepository.getReferences(collection.id);
150
- collection.hydrateStores(references);
151
- }
152
- // set collection
153
- // TODO: create real collection group
154
- this._collectionGroup = new components_1.CollectionGroup({ collections: [] });
155
- this.collection = collection;
156
- // update state
157
- this.state = 'ready';
158
- }
159
- // handle load error
160
- catch (e) {
161
- console.error('[CollectionController] failed to load collection', e);
162
- // TODO: expose error
163
- // set error state
164
- this.state = 'error';
165
- }
166
- }
167
- async createLanguage(target) {
168
- throw new funnel_core_1.NotImplementedError();
169
- }
170
- async unpublish() {
171
- throw new funnel_core_1.NotImplementedError();
172
- }
173
- async save(collection) {
174
- // try to save collection
175
- try {
176
- console.debug('[CE][CollectionController/save] starting save');
177
- // update state
178
- this.state = 'saving';
179
- // save page
180
- const updatedCollection = await this.localRepository.save(collection);
181
- this.collection = updatedCollection;
182
- // update state
183
- this.state = 'ready';
184
- console.debug('[CE][CollectionController/save] save finished');
185
- }
186
- // handle error
187
- catch (e) {
188
- console.error('[CE][CollectionController/save] save failed', e);
189
- // update state
190
- this.state = 'error';
191
- }
192
- }
193
- async reload() {
194
- throw new funnel_core_1.NotImplementedError();
195
- }
196
- raiseEvent(type) {
197
- this._eventBus.publish(components_1.ICollectionController.Event.create({ type, controller: this }));
198
- }
199
- }
200
- exports.LocalCollectionController = LocalCollectionController;
@@ -1,8 +0,0 @@
1
- import { PropsWithChildren } from 'react';
2
- import { RemoteCollectionRepository } from '../../core';
3
- export declare function LocalCollectionControllerContainer({ children, remoteRepository }: LocalCollectionControllerContainer.Props): import("react/jsx-runtime").JSX.Element;
4
- export declare namespace LocalCollectionControllerContainer {
5
- type Props = PropsWithChildren & {
6
- remoteRepository: RemoteCollectionRepository;
7
- };
8
- }
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LocalCollectionControllerContainer = LocalCollectionControllerContainer;
4
- const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_apps_toolkit_1 = require("@contentful/react-apps-toolkit");
6
- const react_1 = require("react");
7
- const core_1 = require("../../core");
8
- const LocalCollectionController_1 = require("./LocalCollectionController");
9
- const LocalCollectionControllerContext_1 = require("./LocalCollectionControllerContext");
10
- // container
11
- function LocalCollectionControllerContainer({ children, remoteRepository }) {
12
- // bind context
13
- const sdk = (0, react_apps_toolkit_1.useSDK)();
14
- const context = (0, react_1.useMemo)(() => {
15
- console.debug('[CE][CollectionControllerContainer] creating collection controller');
16
- const localRepository = new core_1.LocalCollectionRepository(sdk);
17
- // return context
18
- return {
19
- controller: new LocalCollectionController_1.LocalCollectionController({ localRepository, remoteRepository })
20
- };
21
- }, [sdk, remoteRepository]);
22
- // list to collection changes
23
- (0, react_1.useEffect)(() => {
24
- // get controller
25
- const { controller } = context;
26
- // sync on change
27
- sdk.entry.onSysChanged(sys => {
28
- // get updatedOn in milliseconds
29
- const updatedOn = new Date(sys.updatedAt).getTime();
30
- // call sync
31
- controller.sync(updatedOn).catch(console.error);
32
- });
33
- }, [sdk, context]);
34
- // render
35
- return ((0, jsx_runtime_1.jsx)(LocalCollectionControllerContext_1.LocalCollectionControllerContext.Provider, { value: context, children: children }));
36
- }
@@ -1,7 +0,0 @@
1
- import { LocalCollectionController } from './LocalCollectionController';
2
- export declare const LocalCollectionControllerContext: import("react").Context<LocalCollectionControllerContext.Type>;
3
- export declare namespace LocalCollectionControllerContext {
4
- type Type = {
5
- controller: LocalCollectionController;
6
- };
7
- }
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LocalCollectionControllerContext = void 0;
4
- const react_1 = require("react");
5
- // context
6
- exports.LocalCollectionControllerContext = (0, react_1.createContext)({});
@@ -1,2 +0,0 @@
1
- import { LocalCollectionControllerContext } from './LocalCollectionControllerContext';
2
- export declare function useLocalCollectionController(): LocalCollectionControllerContext.Type;
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useLocalCollectionController = useLocalCollectionController;
4
- const react_1 = require("react");
5
- const LocalCollectionControllerContext_1 = require("./LocalCollectionControllerContext");
6
- // hook
7
- function useLocalCollectionController() {
8
- // bind and return context
9
- return (0, react_1.useContext)(LocalCollectionControllerContext_1.LocalCollectionControllerContext);
10
- }
@@ -1,7 +0,0 @@
1
- import { EditorAppSDK } from '@contentful/app-sdk';
2
- import { PageClient } from '@nascentdigital/funnel-cms';
3
- export declare class EditorApiClient {
4
- readonly sdk: EditorAppSDK;
5
- constructor(sdk: EditorAppSDK);
6
- currentPageGroup(): Promise<PageClient.GetGroup.Result>;
7
- }