@nascentdigital/funnel-editor-contentful 4.6.7 → 5.0.2

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 (129) hide show
  1. package/dist/Editor.d.ts +6 -1
  2. package/dist/Editor.js +17 -1
  3. package/dist/EditorApp.js +11 -9
  4. package/dist/EditorPageRouter.d.ts +1 -0
  5. package/dist/EditorPageRouter.js +40 -0
  6. package/dist/EntryEditor.js +43 -1
  7. package/dist/EntrySidebar.js +18 -1
  8. package/dist/asset/index.d.ts +1 -0
  9. package/dist/{page/hooks → asset}/index.js +1 -1
  10. package/dist/asset/repository/AssetRepository.d.ts +10 -0
  11. package/dist/asset/repository/AssetRepository.js +33 -0
  12. package/dist/asset/repository/index.d.ts +1 -0
  13. package/dist/{core/api → asset/repository}/index.js +1 -3
  14. package/dist/collection/controller/ContentfulCollectionController.d.ts +41 -0
  15. package/dist/collection/controller/ContentfulCollectionController.js +278 -0
  16. package/dist/collection/controller/ContentfulCollectionControllerContainer.d.ts +5 -0
  17. package/dist/collection/controller/ContentfulCollectionControllerContainer.js +28 -0
  18. package/dist/collection/controller/index.d.ts +3 -0
  19. package/dist/collection/controller/index.js +19 -0
  20. package/dist/collection/controller/useContentfulCollectionController.d.ts +2 -0
  21. package/dist/collection/controller/useContentfulCollectionController.js +8 -0
  22. package/dist/collection/domain/Collection.d.ts +108 -0
  23. package/dist/collection/domain/Collection.js +386 -0
  24. package/dist/collection/domain/index.d.ts +1 -0
  25. package/dist/collection/domain/index.js +17 -0
  26. package/dist/collection/index.d.ts +3 -0
  27. package/dist/collection/index.js +19 -0
  28. package/dist/collection/repository/CollectionRepository.d.ts +49 -0
  29. package/dist/collection/repository/CollectionRepository.js +210 -0
  30. package/dist/collection/repository/index.d.ts +1 -0
  31. package/dist/collection/repository/index.js +17 -0
  32. package/dist/collection/screens/CollectionEditor.d.ts +1 -0
  33. package/dist/collection/screens/CollectionEditor.js +59 -0
  34. package/dist/collection/screens/index.d.ts +1 -0
  35. package/dist/collection/screens/index.js +17 -0
  36. package/dist/core/bridge/AppBridgeContainer.d.ts +9 -0
  37. package/dist/core/bridge/AppBridgeContainer.js +55 -0
  38. package/dist/core/bridge/AppBridgeContext.d.ts +8 -0
  39. package/dist/core/bridge/AppBridgeContext.js +11 -0
  40. package/dist/core/bridge/AppMessage.d.ts +19 -0
  41. package/dist/core/bridge/AppMessage.js +19 -0
  42. package/dist/core/bridge/index.d.ts +4 -0
  43. package/dist/core/bridge/index.js +20 -0
  44. package/dist/core/bridge/useAppBridge.d.ts +8 -0
  45. package/dist/core/bridge/useAppBridge.js +24 -0
  46. package/dist/core/client/ContentfulClientContainer.d.ts +8 -0
  47. package/dist/core/client/ContentfulClientContainer.js +43 -0
  48. package/dist/core/client/ContentfulClientContext.d.ts +8 -0
  49. package/dist/core/client/ContentfulClientContext.js +8 -0
  50. package/dist/core/client/index.d.ts +3 -0
  51. package/dist/core/client/index.js +19 -0
  52. package/dist/core/client/useContentfulClient.d.ts +4 -0
  53. package/dist/core/client/useContentfulClient.js +24 -0
  54. package/dist/core/contentful/Contentful.d.ts +33 -0
  55. package/dist/core/contentful/Contentful.js +67 -0
  56. package/dist/core/contentful/ContentfulAsset.d.ts +23 -0
  57. package/dist/core/contentful/ContentfulAsset.js +89 -0
  58. package/dist/core/contentful/ContentfulCollection.d.ts +29 -0
  59. package/dist/core/contentful/ContentfulCollection.js +97 -0
  60. package/dist/core/contentful/ContentfulPage.d.ts +31 -0
  61. package/dist/core/contentful/ContentfulPage.js +84 -0
  62. package/dist/core/contentful/ContentfulRefs.d.ts +16 -0
  63. package/dist/core/contentful/ContentfulRefs.js +152 -0
  64. package/dist/core/contentful/index.d.ts +5 -0
  65. package/dist/core/contentful/index.js +21 -0
  66. package/dist/core/index.d.ts +3 -2
  67. package/dist/core/index.js +3 -2
  68. package/dist/core/repository/CollectionSerializer.d.ts +9 -0
  69. package/dist/core/repository/CollectionSerializer.js +41 -0
  70. package/dist/core/repository/LocalCollectionRepository.d.ts +27 -0
  71. package/dist/core/repository/LocalCollectionRepository.js +143 -0
  72. package/dist/core/repository/LocalPageRepository.d.ts +16 -0
  73. package/dist/core/repository/LocalPageRepository.js +153 -0
  74. package/dist/core/repository/PageSerializer.d.ts +3 -3
  75. package/dist/core/repository/PageSerializer.js +18 -14
  76. package/dist/core/repository/RemoteCollectionRepository.d.ts +19 -0
  77. package/dist/core/repository/RemoteCollectionRepository.js +236 -0
  78. package/dist/core/repository/RemotePageRepository.d.ts +14 -0
  79. package/dist/core/repository/RemotePageRepository.js +113 -0
  80. package/dist/core/repository/index.d.ts +4 -1
  81. package/dist/core/repository/index.js +4 -1
  82. package/dist/explore/components/Header.d.ts +26 -0
  83. package/dist/explore/components/Header.js +31 -0
  84. package/dist/explore/components/index.d.ts +1 -0
  85. package/dist/explore/components/index.js +17 -0
  86. package/dist/explore/index.d.ts +1 -0
  87. package/dist/explore/index.js +17 -0
  88. package/dist/explore/screens/Dashboard.d.ts +1 -0
  89. package/dist/explore/screens/Dashboard.js +22 -0
  90. package/dist/explore/screens/PageExplorer.d.ts +1 -0
  91. package/dist/explore/screens/PageExplorer.js +9 -0
  92. package/dist/explore/screens/SiteExplorer.d.ts +1 -0
  93. package/dist/explore/screens/SiteExplorer.js +24 -0
  94. package/dist/explore/screens/index.d.ts +3 -0
  95. package/dist/explore/screens/index.js +19 -0
  96. package/dist/page/{hooks/PageController.d.ts → controller/ContentfulPageController.d.ts} +17 -6
  97. package/dist/page/controller/ContentfulPageController.js +277 -0
  98. package/dist/page/controller/ContentfulPageControllerContainer.d.ts +5 -0
  99. package/dist/page/controller/ContentfulPageControllerContainer.js +28 -0
  100. package/dist/page/controller/index.d.ts +3 -0
  101. package/dist/page/controller/index.js +19 -0
  102. package/dist/page/controller/useContentfulPageController.d.ts +2 -0
  103. package/dist/page/controller/useContentfulPageController.js +8 -0
  104. package/dist/{core → page}/domain/Page.d.ts +71 -10
  105. package/dist/page/domain/Page.js +631 -0
  106. package/dist/page/index.d.ts +3 -0
  107. package/dist/page/index.js +3 -0
  108. package/dist/page/repository/PageRepository.d.ts +45 -0
  109. package/dist/page/repository/PageRepository.js +224 -0
  110. package/dist/page/repository/index.d.ts +1 -0
  111. package/dist/page/repository/index.js +17 -0
  112. package/dist/page/screens/PageEditor.js +29 -15
  113. package/package.json +10 -4
  114. package/dist/core/api/EditorApiClient.d.ts +0 -7
  115. package/dist/core/api/EditorApiClient.js +0 -29
  116. package/dist/core/api/EditorApiContainer.d.ts +0 -5
  117. package/dist/core/api/EditorApiContainer.js +0 -16
  118. package/dist/core/api/EditorApiContext.d.ts +0 -7
  119. package/dist/core/api/EditorApiContext.js +0 -6
  120. package/dist/core/api/index.d.ts +0 -3
  121. package/dist/core/api/useEditorApi.d.ts +0 -2
  122. package/dist/core/api/useEditorApi.js +0 -12
  123. package/dist/core/domain/Page.js +0 -180
  124. package/dist/core/repository/PageRepository.d.ts +0 -12
  125. package/dist/core/repository/PageRepository.js +0 -23
  126. package/dist/page/hooks/PageController.js +0 -107
  127. package/dist/page/hooks/index.d.ts +0 -1
  128. /package/dist/{core → page}/domain/index.d.ts +0 -0
  129. /package/dist/{core → page}/domain/index.js +0 -0
@@ -0,0 +1,210 @@
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.CollectionRepository = 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 CollectionRepository {
21
+ constructor(args) {
22
+ // initialize instance
23
+ this.sdk = args.sdk;
24
+ this.contentfulClient = args.contentfulClient;
25
+ }
26
+ async getOverview(args) {
27
+ throw new funnel_core_1.NotImplementedError();
28
+ }
29
+ async getItems(args) {
30
+ throw new funnel_core_1.NotImplementedError();
31
+ }
32
+ async get(args) {
33
+ throw new funnel_core_1.NotImplementedError();
34
+ }
35
+ async create(args) {
36
+ throw new funnel_core_1.NotImplementedError();
37
+ }
38
+ async createRaw(args) {
39
+ throw new funnel_core_1.NotImplementedError();
40
+ }
41
+ async createLanguage(args) {
42
+ throw new funnel_core_1.NotImplementedError();
43
+ }
44
+ async update(args) {
45
+ throw new funnel_core_1.NotImplementedError();
46
+ }
47
+ serializeUpdate(collection, savables) {
48
+ throw new funnel_core_1.NotImplementedError();
49
+ }
50
+ async load(sys) {
51
+ // throw if entry isn't a collection (it should be in this case)
52
+ const { sdk, contentfulClient } = this;
53
+ const { entry } = sdk;
54
+ sys = sys !== null && sys !== void 0 ? sys : entry.getSys();
55
+ const { id: entryId, contentType: { sys: { id: entryType } } } = sys;
56
+ if (entryType !== contentful_1.ContentfulCollectionData.contentType) {
57
+ throw new funnel_core_1.ArgumentError(`Cannot load Collection from entry that isn't of "${contentful_1.ContentfulCollectionData.contentType}" type`);
58
+ }
59
+ // throw if page doesn't have a group id
60
+ const collectionId = entryId;
61
+ const type = entry.fields.type.getValue();
62
+ const groupId = entry.fields.groupId.getValue();
63
+ const brand = entry.fields.brand.getValue();
64
+ const language = funnel_core_1.Language.tryParse(entry.fields.language.getValue());
65
+ if (!groupId || !type || !brand || !language) {
66
+ throw new funnel_core_1.ArgumentError(`Collection "${collectionId}" is not configured (missing required fields)`);
67
+ }
68
+ // fetch remote data (throw if not found)
69
+ const result = await contentfulClient.collections.getGroup({ groupId, type, brand, language, version: 'draft' });
70
+ if (!result) {
71
+ throw new funnel_core_1.NotFoundError(`Unable to resolve Collection with id "${collectionId}"`);
72
+ }
73
+ const _a = result.group, { instances: collections } = _a, group = __rest(_a, ["instances"]), { collection: collectionResult } = result;
74
+ if (!collectionResult) {
75
+ throw new funnel_core_1.NotFoundError(`Unable to resolve Collection with id "${collectionId}"`);
76
+ }
77
+ // get entry data + remote references
78
+ const { data: _remote } = collectionResult, references = __rest(collectionResult, ["data"]);
79
+ const data = entryToCollectionData(entry);
80
+ // return collection model from data
81
+ const collection = domain_1.Collection.fromSchemaData(Object.assign({ data }, references));
82
+ return {
83
+ collectionGroup: new components_1.CollectionGroup(Object.assign({ template: collection.template, collections }, group)),
84
+ collection,
85
+ sys
86
+ };
87
+ }
88
+ async save(collection) {
89
+ // serialize collection updates (or skip if there are no changes)
90
+ const savables = [];
91
+ const updateArgs = domain_1.Collection.toUpdates(collection, savables);
92
+ if (updateArgs.updates.length === 0) {
93
+ return collection;
94
+ }
95
+ // start all updates
96
+ collection.onSaveStart();
97
+ savables.forEach(savable => savable.onSaveStart());
98
+ // update entry if page is local
99
+ let updatedOn;
100
+ const { sdk } = this;
101
+ const { entry } = sdk;
102
+ const { id: entryId, contentType: { sys: { id: entryType } } } = entry.getSys();
103
+ if (entryType === contentful_1.ContentfulCollectionData.contentType && collection.id === entryId) {
104
+ console.debug('[CollectionRepository/save] saving local entry', { entry, collection, updates: updateArgs.updates });
105
+ // save updates to entry
106
+ await saveEntryUpdates({ entry, updates: updateArgs.updates });
107
+ // capture updated timestamp
108
+ updatedOn = new Date(entry.getSys().updatedAt).getTime();
109
+ }
110
+ // or update remotely
111
+ else {
112
+ console.debug('[CollectionRepository/save] saving remote entry', collection);
113
+ // push updates remotely
114
+ const result = await this.contentfulClient.collections.update(updateArgs);
115
+ // capture update timestamp
116
+ updatedOn = result.updatedOn;
117
+ // swap to new collection (if update returned new data)
118
+ if (result.collection) {
119
+ console.debug('[PageRepository/save] switching to new page data', result.collection);
120
+ collection = domain_1.Collection.fromSchemaData(result.collection);
121
+ }
122
+ }
123
+ console.debug('[PageRepository/save] finalizing save');
124
+ // complete updates
125
+ savables.forEach(savable => savable.onSaveComplete());
126
+ collection.onSaveComplete(updatedOn);
127
+ console.debug('[PageRepository/save] save complete', collection);
128
+ // return collection
129
+ return collection;
130
+ }
131
+ async sync({ collection, sys }) {
132
+ // skip if entity version and updated sys are same
133
+ const { sdk } = this;
134
+ const { entry } = sdk;
135
+ const entrySys = entry.getSys();
136
+ if (sys.version === entrySys.version) {
137
+ console.debug('[ContentfulCollectionRepository/sync] ignoring unchanged entity');
138
+ return { collection, sys };
139
+ }
140
+ // force reload if entry references changed
141
+ const entryReferences = entry.fields.references.getValue();
142
+ const entryRefs = new Set(entryReferences.map(link => `${link.sys.linkType}.${link.sys.id}`));
143
+ const { references: collectionReferences } = collection;
144
+ const collectionRefs = new Set(collectionReferences.map(link => `${link.sys.linkType}.${link.sys.id}`));
145
+ if (!collectionRefs.isSupersetOf(entryRefs)) {
146
+ console.debug('[ContentfulCollectionRepository/sync] references changed - triggering reload');
147
+ return this.load(sys);
148
+ }
149
+ console.debug('[ContentfulCollectionRepository/sync] reusing stable references - merging entity');
150
+ // or load page data, but use old references in store
151
+ const data = entryToCollectionData(entry);
152
+ const references = {
153
+ files: collection.assets.reduce((files, file) => {
154
+ files[file.value.id] = file;
155
+ return files;
156
+ }, {}),
157
+ collections: collection.collections.reduce((collections, collection) => {
158
+ collections[collection.id] = collection;
159
+ return collections;
160
+ }, {}),
161
+ };
162
+ // create new page (use existing sys)
163
+ return {
164
+ collection: domain_1.Collection.fromSchemaData(Object.assign({ data }, references)),
165
+ sys
166
+ };
167
+ }
168
+ async unpublish(collection) {
169
+ throw new funnel_core_1.NotImplementedError();
170
+ }
171
+ }
172
+ exports.CollectionRepository = CollectionRepository;
173
+ // helpers
174
+ function entryToCollectionData(entry) {
175
+ // throw if it's not a page
176
+ const entryType = entry.getSys().contentType.sys.id;
177
+ if (entryType !== contentful_1.ContentfulCollectionData.contentType) {
178
+ throw new funnel_core_1.ArgumentError(`Expected entry to be "${contentful_1.ContentfulCollectionData.contentType}", but found "${entryType}"`);
179
+ }
180
+ // serialize entry (this is the latest data)
181
+ const entryData = contentful_1.ContentfulData.fromEntryData(entry);
182
+ return contentful_1.ContentfulCollectionData.deserialize(entryData);
183
+ }
184
+ async function saveEntryUpdates({ entry, updates }) {
185
+ var _a;
186
+ // serialize entry (this is the latest data)
187
+ const entryData = contentful_1.ContentfulData.fromEntryData(entry);
188
+ const collectionData = contentful_1.ContentfulCollectionData.deserialize(entryData);
189
+ // create model and apply updates
190
+ const collectionModel = new funnel_core_1.Schema.CollectionModel(collectionData);
191
+ collectionModel.update(updates);
192
+ collectionModel.sync();
193
+ // resolve references
194
+ const references = contentful_1.ContentfulData.findReferences([collectionData.fields]);
195
+ // update fields
196
+ const { fields } = entry;
197
+ await Promise.all([
198
+ // required updates
199
+ fields.name.setValue(collectionData.name),
200
+ fields.state.setValue(collectionData.state),
201
+ fields.errors.setValue(collectionData.errors),
202
+ fields.references.setValue(references),
203
+ // TODO: save only fields that changed
204
+ // conditional updates
205
+ fields.tags.setValue((_a = collectionData.tags) !== null && _a !== void 0 ? _a : []),
206
+ fields.fields.setValue(collectionData.fields),
207
+ ]);
208
+ // save entry
209
+ await entry.save();
210
+ }
@@ -0,0 +1 @@
1
+ export * from './CollectionRepository';
@@ -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("./CollectionRepository"), exports);
@@ -0,0 +1 @@
1
+ export declare function CollectionEditor(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CollectionEditor = CollectionEditor;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
6
+ const components_1 = require("@nascentdigital/funnel-editor/components");
7
+ const react_1 = require("react");
8
+ const controller_1 = require("../controller");
9
+ // component
10
+ function CollectionEditor() {
11
+ // hooks
12
+ const splash = (0, components_1.useSplash)();
13
+ const controller = (0, controller_1.useContentfulCollectionController)();
14
+ const { ready, collection } = controller;
15
+ const collectionContext = (0, react_1.useMemo)(() => ({ collection }), [collection]);
16
+ // sync with controller
17
+ (0, components_1.useCollectionController)(controller);
18
+ // load
19
+ (0, react_1.useEffect)(() => {
20
+ controller.load().catch(e => {
21
+ // transform error
22
+ const error = funnel_core_1.IsometricErrorUtils.coerceError(e);
23
+ // TODO: come up with an appropriate message
24
+ const message = error.message;
25
+ // show error via splash
26
+ splash.showError({ message, onRetry: () => controller.reload() });
27
+ });
28
+ }, [controller, splash]);
29
+ // dismiss splash when ready
30
+ (0, react_1.useEffect)(() => {
31
+ if (ready) {
32
+ splash.dismiss();
33
+ }
34
+ else {
35
+ splash.showLoading('Fetching content');
36
+ }
37
+ }, [splash, ready]);
38
+ // render
39
+ return ((0, jsx_runtime_1.jsx)(components_1.CollectionContext.Provider, { value: collectionContext, children: (0, jsx_runtime_1.jsx)(components_1.AssetStoreContext.Provider, { value: collection, children: (0, jsx_runtime_1.jsx)(components_1.CollectionStoreContext.Provider, { value: collection, children: (0, jsx_runtime_1.jsx)("div", { className: "w-dvw h-dvh flex flex-col bg-background", children: (0, jsx_runtime_1.jsx)("div", { className: "relative flex-1", children: (0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 flex", children: (0, jsx_runtime_1.jsx)("section", { className: "flex-1 h-full max-h-full max-w-[1000px] mx-auto", children: renderContent({ controller }) }) }) }) }) }) }) }));
40
+ }
41
+ function renderContent({ controller }) {
42
+ // render loader if not ready
43
+ const { ready, collectionGroup, collection } = controller;
44
+ if (!ready) {
45
+ return ((0, jsx_runtime_1.jsx)(components_1.Loader, {}, "loader"));
46
+ }
47
+ // render error if there's no group
48
+ else if (!collectionGroup) {
49
+ return ((0, jsx_runtime_1.jsx)(components_1.EmptyState, { variant: "missing", size: "md", children: "Oops! This page doesn't seem to exist..." }));
50
+ }
51
+ // render create state if there's no group
52
+ else if (!collection) {
53
+ return null;
54
+ }
55
+ // otherwise render editor
56
+ else {
57
+ return ((0, jsx_runtime_1.jsx)("div", { className: "flex-1 flex flex-col gap-l10 px-l6 py-l10 overflow-y-scroll scrollbar-thin bg-background", children: (0, jsx_runtime_1.jsx)(components_1.FieldsPanel, { title: "Content", fields: collection.allFields }) }));
58
+ }
59
+ }
@@ -0,0 +1 @@
1
+ export * from './CollectionEditor';
@@ -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("./CollectionEditor"), exports);
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from 'react';
2
+ export declare const CHANNEL_PREFIX = "funnel:channel:";
3
+ export declare function AppBridgeContainer({ children }: AppBridgeContainer.Props): import("react/jsx-runtime").JSX.Element;
4
+ export declare namespace AppBridgeContainer {
5
+ type Props = {
6
+ children: ReactNode;
7
+ };
8
+ }
9
+ export declare function getUniqueId(): string;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CHANNEL_PREFIX = void 0;
4
+ exports.AppBridgeContainer = AppBridgeContainer;
5
+ exports.getUniqueId = getUniqueId;
6
+ const jsx_runtime_1 = require("react/jsx-runtime");
7
+ const react_1 = require("react");
8
+ const rxjs_1 = require("rxjs");
9
+ const uuid_1 = require("uuid");
10
+ const AppBridgeContext_1 = require("./AppBridgeContext");
11
+ // constants
12
+ exports.CHANNEL_PREFIX = 'funnel:channel:';
13
+ // container
14
+ function AppBridgeContainer({ children }) {
15
+ // create context
16
+ const context = (0, react_1.useMemo)(() => ({
17
+ id: (0, uuid_1.v4)(),
18
+ eventBus: new rxjs_1.Subject(),
19
+ channel: new BroadcastChannel(exports.CHANNEL_PREFIX + getUniqueId())
20
+ }), []);
21
+ // forward all events to channel
22
+ (0, react_1.useEffect)(() => {
23
+ // create message handler
24
+ const { id, eventBus, channel } = context;
25
+ const onChannelMessage = (event) => {
26
+ console.debug(`[${id}] received event`, event);
27
+ // ignore message if sent from this container
28
+ const message = event.data;
29
+ if (message.source === id) {
30
+ console.debug(`[${id}] ignoring echo`);
31
+ return;
32
+ }
33
+ console.debug(`[${id}] forwarding message to local bus`);
34
+ eventBus.next(message);
35
+ };
36
+ // listen to messages and forward all to the channel
37
+ channel.addEventListener('message', onChannelMessage);
38
+ // remove listener on unmount
39
+ return () => channel.removeEventListener('message', onChannelMessage);
40
+ }, [context]);
41
+ // render
42
+ return ((0, jsx_runtime_1.jsx)(AppBridgeContext_1.AppBridgeContext.Provider, { value: context, children: children }));
43
+ }
44
+ // helpers
45
+ const UUID_KEY = 'funnel:uuid';
46
+ function getUniqueId() {
47
+ // fetch window id (or create one)
48
+ let value = window.sessionStorage.getItem(UUID_KEY);
49
+ if (!value) {
50
+ value = (0, uuid_1.v4)();
51
+ window.sessionStorage.setItem(UUID_KEY, value);
52
+ }
53
+ // return value
54
+ return value;
55
+ }
@@ -0,0 +1,8 @@
1
+ import { Subject } from 'rxjs';
2
+ import { AppContextId, TrackedAppMessage } from './AppMessage';
3
+ export type IAppBridgeContext = {
4
+ id: AppContextId;
5
+ eventBus: Subject<TrackedAppMessage>;
6
+ channel: BroadcastChannel;
7
+ };
8
+ export declare const AppBridgeContext: import("react").Context<IAppBridgeContext>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppBridgeContext = void 0;
4
+ const react_1 = require("react");
5
+ const rxjs_1 = require("rxjs");
6
+ // context
7
+ exports.AppBridgeContext = (0, react_1.createContext)({
8
+ id: '',
9
+ eventBus: new rxjs_1.Subject(),
10
+ channel: {}
11
+ });
@@ -0,0 +1,19 @@
1
+ import { Tagged } from 'type-fest';
2
+ export type AppDomain = 'app';
3
+ export interface AppMessage {
4
+ domain: AppDomain;
5
+ type: string;
6
+ timestamp: number;
7
+ }
8
+ export type AppContextId = Tagged<string, 'funnel.AppContextId'>;
9
+ export interface TrackedAppMessage extends AppMessage {
10
+ source: AppContextId;
11
+ }
12
+ export interface AppBlockNavMessage extends AppMessage {
13
+ type: 'blockNav';
14
+ itemId: string;
15
+ }
16
+ export declare namespace AppBlockNavMessage {
17
+ function create(itemId: string): AppBlockNavMessage;
18
+ function guard(message: AppMessage): message is AppBlockNavMessage;
19
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppBlockNavMessage = void 0;
4
+ var AppBlockNavMessage;
5
+ (function (AppBlockNavMessage) {
6
+ function create(itemId) {
7
+ return {
8
+ domain: 'app',
9
+ type: 'blockNav',
10
+ timestamp: Date.now(),
11
+ itemId
12
+ };
13
+ }
14
+ AppBlockNavMessage.create = create;
15
+ function guard(message) {
16
+ return message.domain === 'app' && message.type === 'blockNav';
17
+ }
18
+ AppBlockNavMessage.guard = guard;
19
+ })(AppBlockNavMessage || (exports.AppBlockNavMessage = AppBlockNavMessage = {}));
@@ -0,0 +1,4 @@
1
+ export * from './AppBridgeContainer';
2
+ export * from './AppBridgeContext';
3
+ export * from './AppMessage';
4
+ export * from './useAppBridge';
@@ -0,0 +1,20 @@
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("./AppBridgeContainer"), exports);
18
+ __exportStar(require("./AppBridgeContext"), exports);
19
+ __exportStar(require("./AppMessage"), exports);
20
+ __exportStar(require("./useAppBridge"), exports);
@@ -0,0 +1,8 @@
1
+ import { Observable } from 'rxjs';
2
+ import { AppMessage } from './AppMessage';
3
+ export type AppMessageSubmission = Omit<AppMessage, 'source'>;
4
+ export type UseAppBridgetHook = {
5
+ sendMessage(message: AppMessageSubmission): void;
6
+ messages$: Observable<AppMessage>;
7
+ };
8
+ export declare function useAppBridge(): UseAppBridgetHook;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useAppBridge = useAppBridge;
4
+ const react_1 = require("react");
5
+ const AppBridgeContext_1 = require("./AppBridgeContext");
6
+ // hook
7
+ function useAppBridge() {
8
+ // bind context
9
+ const { id, eventBus, channel } = (0, react_1.useContext)(AppBridgeContext_1.AppBridgeContext);
10
+ // helpers
11
+ const sendMessage = (message) => {
12
+ // track the message
13
+ const trackedMessage = Object.assign({ source: id }, message);
14
+ // send to local bus
15
+ eventBus.next(trackedMessage);
16
+ // echo to remote bus (if any)
17
+ channel.postMessage(trackedMessage);
18
+ };
19
+ // return hook interface
20
+ return {
21
+ sendMessage,
22
+ messages$: eventBus.asObservable()
23
+ };
24
+ }
@@ -0,0 +1,8 @@
1
+ import { KnownAppSDK } from '@contentful/app-sdk';
2
+ import { PropsWithChildren } from 'react';
3
+ export declare function ContentfulClientContainer({ sdk, children }: ContentfulClientContainer.Props): import("react/jsx-runtime").JSX.Element;
4
+ export declare namespace ContentfulClientContainer {
5
+ type Props = PropsWithChildren & {
6
+ sdk: KnownAppSDK;
7
+ };
8
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentfulClientContainer = ContentfulClientContainer;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const contentful_1 = require("@nascentdigital/funnel-cms/contentful");
6
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
7
+ const react_1 = require("react");
8
+ const ContentfulClientContext_1 = require("./ContentfulClientContext");
9
+ // container
10
+ function ContentfulClientContainer({ sdk, children }) {
11
+ // bind state
12
+ const [client, setClient] = (0, react_1.useState)(() => ({ __type: 'pending' }));
13
+ const context = (0, react_1.useMemo)(() => ({ client }), [client]);
14
+ // create client on mount
15
+ (0, react_1.useEffect)(() => {
16
+ // fetch client
17
+ let cancelled = false;
18
+ const mount = async () => {
19
+ // try to create
20
+ try {
21
+ // create editor
22
+ const client = await contentful_1.ContentfulContentClient.forEditor(sdk);
23
+ // bind if not cancelled
24
+ if (!cancelled) {
25
+ setClient({ __type: 'success', result: client });
26
+ }
27
+ }
28
+ // or handle error (if not cancelled)
29
+ catch (e) {
30
+ if (!cancelled) {
31
+ setClient({ __type: 'failure', error: funnel_core_1.IsometricErrorUtils.coerceError(e) });
32
+ }
33
+ }
34
+ };
35
+ mount().catch(console.error);
36
+ // cancel on unmount
37
+ return () => {
38
+ cancelled = true;
39
+ };
40
+ }, [sdk]);
41
+ // render
42
+ return ((0, jsx_runtime_1.jsx)(ContentfulClientContext_1.ContentfulClientContext.Provider, { value: context, children: client.__type === 'success' && children }));
43
+ }
@@ -0,0 +1,8 @@
1
+ import { ContentfulContentClient } from '@nascentdigital/funnel-cms/contentful';
2
+ import { Result } from '@nascentdigital/funnel-core';
3
+ export declare const ContentfulClientContext: import("react").Context<ContentfulClientContext.Data>;
4
+ export declare namespace ContentfulClientContext {
5
+ type Data = {
6
+ client: Result<ContentfulContentClient>;
7
+ };
8
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentfulClientContext = void 0;
4
+ const react_1 = require("react");
5
+ // context
6
+ exports.ContentfulClientContext = (0, react_1.createContext)({
7
+ client: { __type: 'pending' }
8
+ });
@@ -0,0 +1,3 @@
1
+ export * from './ContentfulClientContainer';
2
+ export * from './ContentfulClientContext';
3
+ export * from './useContentfulClient';
@@ -0,0 +1,19 @@
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("./ContentfulClientContainer"), exports);
18
+ __exportStar(require("./ContentfulClientContext"), exports);
19
+ __exportStar(require("./useContentfulClient"), exports);
@@ -0,0 +1,4 @@
1
+ import { ContentfulContentClient } from '@nascentdigital/funnel-cms/contentful';
2
+ import { Result } from '@nascentdigital/funnel-core';
3
+ export declare function usePossibleContentfulClient(): Result<ContentfulContentClient>;
4
+ export declare function useContentfulClient(): ContentfulContentClient;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePossibleContentfulClient = usePossibleContentfulClient;
4
+ exports.useContentfulClient = useContentfulClient;
5
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
6
+ const react_1 = require("react");
7
+ const ContentfulClientContext_1 = require("./ContentfulClientContext");
8
+ // hooks
9
+ function usePossibleContentfulClient() {
10
+ // bind context
11
+ const { client } = (0, react_1.useContext)(ContentfulClientContext_1.ContentfulClientContext);
12
+ // return result
13
+ return client;
14
+ }
15
+ function useContentfulClient() {
16
+ // bind context
17
+ const { client } = (0, react_1.useContext)(ContentfulClientContext_1.ContentfulClientContext);
18
+ // throw if unavailable (shouldn't happen - see ContentfulClientContainer)
19
+ if (client.__type !== 'success') {
20
+ throw new funnel_core_1.IllegalStateError('ContentfulContentClient isn\'t available');
21
+ }
22
+ // return result
23
+ return client.result;
24
+ }