@nascentdigital/funnel-editor-contentful 5.0.1 → 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.
- package/dist/EditorApp.js +11 -9
- package/dist/EditorPageRouter.d.ts +1 -0
- package/dist/EditorPageRouter.js +40 -0
- package/dist/EntryEditor.js +30 -29
- package/dist/EntrySidebar.js +1 -1
- package/dist/asset/index.d.ts +1 -0
- package/dist/{core/api → asset}/index.js +1 -3
- package/dist/{core → asset}/repository/AssetRepository.js +3 -3
- package/dist/asset/repository/index.d.ts +1 -0
- package/dist/asset/repository/index.js +17 -0
- package/dist/collection/controller/{LocalCollectionController.d.ts → ContentfulCollectionController.d.ts} +17 -11
- package/dist/collection/controller/ContentfulCollectionController.js +278 -0
- package/dist/collection/controller/ContentfulCollectionControllerContainer.d.ts +5 -0
- package/dist/collection/controller/ContentfulCollectionControllerContainer.js +28 -0
- package/dist/collection/controller/index.d.ts +3 -4
- package/dist/collection/controller/index.js +3 -4
- package/dist/collection/controller/useContentfulCollectionController.d.ts +2 -0
- package/dist/collection/controller/useContentfulCollectionController.js +8 -0
- package/dist/{core → collection}/domain/Collection.d.ts +11 -2
- package/dist/{core → collection}/domain/Collection.js +75 -0
- package/dist/{core → collection}/domain/index.d.ts +0 -1
- package/dist/{core → collection}/domain/index.js +0 -1
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +1 -0
- package/dist/collection/repository/CollectionRepository.d.ts +49 -0
- package/dist/collection/repository/CollectionRepository.js +210 -0
- package/dist/collection/repository/index.d.ts +1 -0
- package/dist/collection/repository/index.js +17 -0
- package/dist/collection/screens/CollectionEditor.js +23 -6
- package/dist/core/client/ContentfulClientContainer.d.ts +8 -0
- package/dist/core/client/ContentfulClientContainer.js +43 -0
- package/dist/core/client/ContentfulClientContext.d.ts +8 -0
- package/dist/core/client/ContentfulClientContext.js +8 -0
- package/dist/core/client/index.d.ts +3 -0
- package/dist/core/client/index.js +19 -0
- package/dist/core/client/useContentfulClient.d.ts +4 -0
- package/dist/core/client/useContentfulClient.js +24 -0
- package/dist/core/contentful/ContentfulAsset.d.ts +3 -3
- package/dist/core/contentful/ContentfulAsset.js +5 -4
- package/dist/core/contentful/ContentfulCollection.d.ts +1 -1
- package/dist/core/contentful/ContentfulRefs.js +9 -9
- package/dist/core/index.d.ts +1 -2
- package/dist/core/index.js +1 -2
- package/dist/core/repository/CollectionSerializer.d.ts +1 -1
- package/dist/core/repository/CollectionSerializer.js +1 -1
- package/dist/core/repository/LocalCollectionRepository.d.ts +2 -2
- package/dist/core/repository/LocalPageRepository.d.ts +1 -1
- package/dist/core/repository/PageSerializer.d.ts +1 -1
- package/dist/core/repository/PageSerializer.js +2 -1
- package/dist/core/repository/RemoteCollectionRepository.d.ts +2 -2
- package/dist/core/repository/index.d.ts +0 -1
- package/dist/core/repository/index.js +0 -1
- package/dist/explore/components/Header.d.ts +26 -0
- package/dist/explore/components/Header.js +31 -0
- package/dist/explore/components/index.d.ts +1 -0
- package/dist/explore/components/index.js +17 -0
- package/dist/explore/index.d.ts +1 -0
- package/dist/explore/index.js +17 -0
- package/dist/explore/screens/Dashboard.d.ts +1 -0
- package/dist/explore/screens/Dashboard.js +22 -0
- package/dist/explore/screens/PageExplorer.d.ts +1 -0
- package/dist/explore/screens/PageExplorer.js +9 -0
- package/dist/explore/screens/SiteExplorer.d.ts +1 -0
- package/dist/explore/screens/SiteExplorer.js +24 -0
- package/dist/explore/screens/index.d.ts +3 -0
- package/dist/explore/screens/index.js +19 -0
- package/dist/page/controller/{LocalPageController.d.ts → ContentfulPageController.d.ts} +15 -9
- package/dist/page/controller/ContentfulPageController.js +277 -0
- package/dist/page/controller/ContentfulPageControllerContainer.d.ts +5 -0
- package/dist/page/controller/ContentfulPageControllerContainer.js +28 -0
- package/dist/page/controller/index.d.ts +3 -3
- package/dist/page/controller/index.js +3 -3
- package/dist/page/controller/useContentfulPageController.d.ts +2 -0
- package/dist/page/controller/useContentfulPageController.js +8 -0
- package/dist/{core → page}/domain/Page.d.ts +22 -11
- package/dist/{core → page}/domain/Page.js +138 -0
- package/dist/page/domain/index.d.ts +1 -0
- package/dist/page/domain/index.js +17 -0
- package/dist/page/index.d.ts +2 -0
- package/dist/page/index.js +2 -0
- package/dist/page/repository/PageRepository.d.ts +45 -0
- package/dist/page/repository/PageRepository.js +224 -0
- package/dist/page/repository/index.d.ts +1 -0
- package/dist/page/repository/index.js +17 -0
- package/dist/page/screens/PageEditor.js +23 -7
- package/package.json +8 -4
- package/dist/collection/controller/LocalCollectionController.js +0 -200
- package/dist/collection/controller/LocalCollectionControllerContainer.d.ts +0 -8
- package/dist/collection/controller/LocalCollectionControllerContainer.js +0 -36
- package/dist/collection/controller/LocalCollectionControllerContext.d.ts +0 -7
- package/dist/collection/controller/LocalCollectionControllerContext.js +0 -6
- package/dist/collection/controller/useLocalCollectionController.d.ts +0 -2
- package/dist/collection/controller/useLocalCollectionController.js +0 -10
- package/dist/core/api/EditorApiClient.d.ts +0 -7
- package/dist/core/api/EditorApiClient.js +0 -29
- package/dist/core/api/EditorApiContainer.d.ts +0 -5
- package/dist/core/api/EditorApiContainer.js +0 -16
- package/dist/core/api/EditorApiContext.d.ts +0 -7
- package/dist/core/api/EditorApiContext.js +0 -6
- package/dist/core/api/index.d.ts +0 -3
- package/dist/core/api/useEditorApi.d.ts +0 -2
- package/dist/core/api/useEditorApi.js +0 -12
- package/dist/page/controller/LocalPageController.js +0 -190
- package/dist/page/controller/LocalPageControllerContainer.d.ts +0 -8
- package/dist/page/controller/LocalPageControllerContainer.js +0 -36
- package/dist/page/controller/LocalPageControllerContext.d.ts +0 -7
- package/dist/page/controller/LocalPageControllerContext.js +0 -6
- package/dist/page/controller/useLocalPageController.d.ts +0 -2
- package/dist/page/controller/useLocalPageController.js +0 -10
- /package/dist/{core → asset}/repository/AssetRepository.d.ts +0 -0
|
@@ -0,0 +1,224 @@
|
|
|
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
|
+
throw new funnel_core_1.NotImplementedError();
|
|
28
|
+
}
|
|
29
|
+
async getOverview({ region }) {
|
|
30
|
+
// get overviews
|
|
31
|
+
const overview = await this.contentfulClient.pages.getOverview({
|
|
32
|
+
brand: funnel_core_1.AppConfig.brand(),
|
|
33
|
+
region,
|
|
34
|
+
version: 'draft'
|
|
35
|
+
});
|
|
36
|
+
// enrich with templates and flip to array
|
|
37
|
+
const templates = Object.values(funnel_core_1.Schema.Site.current().regions[region]);
|
|
38
|
+
return templates.map(template => {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
// extract static page overview
|
|
41
|
+
const { type } = template;
|
|
42
|
+
if (template.variant === 'static') {
|
|
43
|
+
return new components_1.StaticPageOverview(Object.assign({ template,
|
|
44
|
+
type, refs: {} }, ((_a = overview.static.find(page => page.type === type)) !== null && _a !== void 0 ? _a : {})));
|
|
45
|
+
}
|
|
46
|
+
// or dynamic one
|
|
47
|
+
else {
|
|
48
|
+
return new components_1.DynamicPageOverview(Object.assign({ template,
|
|
49
|
+
type, count: 0 }, ((_b = overview.dynamic.find(page => page.type === type)) !== null && _b !== void 0 ? _b : {})));
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async create(args) {
|
|
54
|
+
throw new funnel_core_1.NotImplementedError();
|
|
55
|
+
}
|
|
56
|
+
async createLanguage(args) {
|
|
57
|
+
throw new funnel_core_1.NotImplementedError();
|
|
58
|
+
}
|
|
59
|
+
async load(sys) {
|
|
60
|
+
// throw if entry isn't a page (it should be in this case)
|
|
61
|
+
const { sdk, contentfulClient } = this;
|
|
62
|
+
const { entry } = sdk;
|
|
63
|
+
sys = sys !== null && sys !== void 0 ? sys : entry.getSys();
|
|
64
|
+
const { id: entryId, contentType: { sys: { id: entryType } } } = sys;
|
|
65
|
+
if (entryType !== contentful_1.ContentfulPageData.contentType) {
|
|
66
|
+
throw new funnel_core_1.ArgumentError(`Cannot load Page from entry that isn't of "${contentful_1.ContentfulPageData.contentType}" type`);
|
|
67
|
+
}
|
|
68
|
+
// throw if page doesn't have a group id
|
|
69
|
+
const pageId = entryId;
|
|
70
|
+
const type = entry.fields.type.getValue();
|
|
71
|
+
const groupId = entry.fields.groupId.getValue();
|
|
72
|
+
const brand = entry.fields.brand.getValue();
|
|
73
|
+
const region = entry.fields.region.getValue();
|
|
74
|
+
const language = funnel_core_1.Language.tryParse(entry.fields.language.getValue());
|
|
75
|
+
if (!groupId || !type || !brand || !region || !language) {
|
|
76
|
+
throw new funnel_core_1.ArgumentError(`Page "${pageId}" is not configured (missing required fields)`);
|
|
77
|
+
}
|
|
78
|
+
// fetch remote data (throw if not found)
|
|
79
|
+
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;
|
|
80
|
+
if (!pageResult) {
|
|
81
|
+
throw new funnel_core_1.NotFoundError(`Unable to resolve Page with id "${pageId}"`);
|
|
82
|
+
}
|
|
83
|
+
console.debug('[PageRepository/load] loaded page data:', { group, pages, pageResult });
|
|
84
|
+
// get entry data + remote references
|
|
85
|
+
const { data: _remote } = pageResult, references = __rest(pageResult, ["data"]);
|
|
86
|
+
const data = entryToPageData(entry);
|
|
87
|
+
// return page model from data
|
|
88
|
+
const page = domain_1.Page.fromSchemaData(Object.assign({ data }, references));
|
|
89
|
+
return {
|
|
90
|
+
pageSummary: new components_1.PageSummary(Object.assign(Object.assign({ template: page.template }, group), { pages })),
|
|
91
|
+
page,
|
|
92
|
+
sys
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async save(page) {
|
|
96
|
+
// serialize page updates (or skip if there are no changes)
|
|
97
|
+
const savables = [];
|
|
98
|
+
const updateArgs = domain_1.Page.toUpdates(page, savables);
|
|
99
|
+
if (updateArgs.updates.length === 0) {
|
|
100
|
+
return page;
|
|
101
|
+
}
|
|
102
|
+
// start all updates
|
|
103
|
+
page.onSaveStart();
|
|
104
|
+
savables.forEach(savable => savable.onSaveStart());
|
|
105
|
+
// update entry if page is local
|
|
106
|
+
let updatedOn;
|
|
107
|
+
const { sdk } = this;
|
|
108
|
+
const { entry } = sdk;
|
|
109
|
+
const { id: entryId, contentType: { sys: { id: entryType } } } = entry.getSys();
|
|
110
|
+
if (entryType === contentful_1.ContentfulPageData.contentType && page.id === entryId) {
|
|
111
|
+
console.debug('[PageRepository/save] saving local entry', { entry, page });
|
|
112
|
+
// save updates to entry
|
|
113
|
+
await saveEntryUpdates({ entry, updates: updateArgs.updates });
|
|
114
|
+
// capture updated timestamp
|
|
115
|
+
updatedOn = new Date(entry.getSys().updatedAt).getTime();
|
|
116
|
+
}
|
|
117
|
+
// or update remotely
|
|
118
|
+
else {
|
|
119
|
+
console.debug('[PageRepository/save] saving remote entry', page);
|
|
120
|
+
// push updates remotely
|
|
121
|
+
const result = await this.contentfulClient.pages.update(updateArgs);
|
|
122
|
+
// capture update timestamp
|
|
123
|
+
updatedOn = result.updatedOn;
|
|
124
|
+
// swap to new page (if update returned new data)
|
|
125
|
+
if (result.page) {
|
|
126
|
+
console.debug('[PageRepository/save] switching to new page data', result.page);
|
|
127
|
+
page = domain_1.Page.fromSchemaData(result.page);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
console.debug('[PageRepository/save] finalizing save');
|
|
131
|
+
// complete updates
|
|
132
|
+
savables.forEach(savable => savable.onSaveComplete());
|
|
133
|
+
page.onSaveComplete(updatedOn);
|
|
134
|
+
console.debug('[PageRepository/save] save complete', page);
|
|
135
|
+
// return page
|
|
136
|
+
return page;
|
|
137
|
+
}
|
|
138
|
+
async sync({ page, sys }) {
|
|
139
|
+
// skip if entity version and updated sys are same
|
|
140
|
+
const { sdk } = this;
|
|
141
|
+
const { entry } = sdk;
|
|
142
|
+
const entrySys = entry.getSys();
|
|
143
|
+
if (sys.version === entrySys.version) {
|
|
144
|
+
console.debug('[ContentfulPageRepository/sync] ignoring unchanged entity');
|
|
145
|
+
return { page, sys };
|
|
146
|
+
}
|
|
147
|
+
// force reload if entry references changed
|
|
148
|
+
const entryReferences = entry.fields.references.getValue();
|
|
149
|
+
const entryRefs = new Set(entryReferences.map(link => `${link.sys.linkType}.${link.sys.id}`));
|
|
150
|
+
const { references: pageReferences } = page;
|
|
151
|
+
const pageRefs = new Set(pageReferences.map(link => `${link.sys.linkType}.${link.sys.id}`));
|
|
152
|
+
if (!pageRefs.isSupersetOf(entryRefs)) {
|
|
153
|
+
console.debug('[ContentfulPageRepository/sync] references changed - triggering reload');
|
|
154
|
+
return this.load(sys);
|
|
155
|
+
}
|
|
156
|
+
console.debug('[ContentfulPageRepository/sync] reusing stable references - merging entity');
|
|
157
|
+
// or load page data, but use old references in store
|
|
158
|
+
const data = entryToPageData(entry);
|
|
159
|
+
const references = {
|
|
160
|
+
files: page.assets.reduce((files, file) => {
|
|
161
|
+
files[file.value.id] = file;
|
|
162
|
+
return files;
|
|
163
|
+
}, {}),
|
|
164
|
+
collections: page.collections.reduce((collections, collection) => {
|
|
165
|
+
collections[collection.id] = collection;
|
|
166
|
+
return collections;
|
|
167
|
+
}, {}),
|
|
168
|
+
pages: page.pages.reduce((pages, page) => {
|
|
169
|
+
pages[page.id] = page;
|
|
170
|
+
return pages;
|
|
171
|
+
}, {})
|
|
172
|
+
};
|
|
173
|
+
// create new page (use existing sys)
|
|
174
|
+
return {
|
|
175
|
+
page: domain_1.Page.fromSchemaData(Object.assign({ data }, references)),
|
|
176
|
+
sys
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async unpublish(page) {
|
|
180
|
+
throw new funnel_core_1.NotImplementedError();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.PageRepository = PageRepository;
|
|
184
|
+
// helpers
|
|
185
|
+
function entryToPageData(entry) {
|
|
186
|
+
// throw if it's not a page
|
|
187
|
+
const entryType = entry.getSys().contentType.sys.id;
|
|
188
|
+
if (entryType !== contentful_1.ContentfulPageData.contentType) {
|
|
189
|
+
throw new funnel_core_1.ArgumentError(`Expected entry to be "${contentful_1.ContentfulPageData.contentType}", but found "${entryType}"`);
|
|
190
|
+
}
|
|
191
|
+
// serialize entry (this is the latest data)
|
|
192
|
+
const entryData = contentful_1.ContentfulData.fromEntryData(entry);
|
|
193
|
+
return contentful_1.ContentfulPageData.deserialize(entryData);
|
|
194
|
+
}
|
|
195
|
+
async function saveEntryUpdates({ entry, updates }) {
|
|
196
|
+
var _a;
|
|
197
|
+
// serialize entry (this is the latest data)
|
|
198
|
+
const pageData = entryToPageData(entry);
|
|
199
|
+
// create model and apply updates
|
|
200
|
+
const pageModel = new funnel_core_1.Schema.PageModel(pageData);
|
|
201
|
+
pageModel.update(updates);
|
|
202
|
+
pageModel.sync();
|
|
203
|
+
// resolve references
|
|
204
|
+
const references = contentful_1.ContentfulData.findReferences([pageData.discover, pageData.preview, pageData.content]);
|
|
205
|
+
// update fields
|
|
206
|
+
const { fields } = entry;
|
|
207
|
+
await Promise.all([
|
|
208
|
+
// required updates
|
|
209
|
+
fields.name.setValue(pageData.name),
|
|
210
|
+
fields.slug.setValue(pageData.slug),
|
|
211
|
+
fields.url.setValue(pageData.url),
|
|
212
|
+
fields.state.setValue(pageData.state),
|
|
213
|
+
fields.errors.setValue(pageData.errors),
|
|
214
|
+
fields.references.setValue(references),
|
|
215
|
+
// TODO: save only fields that changed
|
|
216
|
+
// conditional updates
|
|
217
|
+
fields.tags.setValue((_a = pageData.tags) !== null && _a !== void 0 ? _a : []),
|
|
218
|
+
fields.preview.setValue(pageData.preview),
|
|
219
|
+
fields.discover.setValue(pageData.discover),
|
|
220
|
+
fields.content.setValue(pageData.content)
|
|
221
|
+
]);
|
|
222
|
+
// save entry
|
|
223
|
+
await entry.save();
|
|
224
|
+
}
|
|
@@ -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,6 +2,7 @@
|
|
|
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");
|
|
6
7
|
const react_1 = require("react");
|
|
7
8
|
const core_1 = require("../../core");
|
|
@@ -9,25 +10,40 @@ const controller_1 = require("../controller");
|
|
|
9
10
|
// component
|
|
10
11
|
function PageEditor() {
|
|
11
12
|
// hooks
|
|
13
|
+
const splash = (0, components_1.useSplash)();
|
|
12
14
|
const bridge = (0, core_1.useAppBridge)();
|
|
13
|
-
const
|
|
14
|
-
const { controller } = controllerContext;
|
|
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
|
-
|
|
20
|
-
(0, components_1.usePageController)(controller);
|
|
20
|
+
const { ready } = controller;
|
|
21
21
|
// load
|
|
22
22
|
(0, react_1.useEffect)(() => {
|
|
23
|
-
controller.load().catch(
|
|
24
|
-
|
|
23
|
+
controller.load().catch(e => {
|
|
24
|
+
// transform error
|
|
25
|
+
const error = funnel_core_1.IsometricErrorUtils.coerceError(e);
|
|
26
|
+
// TODO: come up with an appropriate message
|
|
27
|
+
const message = error.message;
|
|
28
|
+
// show error via splash
|
|
29
|
+
splash.showError({ message, onRetry: () => controller.reload() });
|
|
30
|
+
});
|
|
31
|
+
}, [splash, controller]);
|
|
32
|
+
// dismiss splash when ready
|
|
33
|
+
(0, react_1.useEffect)(() => {
|
|
34
|
+
if (ready) {
|
|
35
|
+
splash.dismiss();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
splash.showLoading('Fetching content');
|
|
39
|
+
}
|
|
40
|
+
}, [splash, ready]);
|
|
25
41
|
const onSelectItem = (item) => {
|
|
26
42
|
setItemId(item.identifier);
|
|
27
43
|
bridge.sendMessage(core_1.AppBlockNavMessage.create(item.identifier));
|
|
28
44
|
};
|
|
29
45
|
// render
|
|
30
|
-
return ((0, jsx_runtime_1.jsx)(components_1.
|
|
46
|
+
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)("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 }) })] }) }) }) }) }) }) }));
|
|
31
47
|
}
|
|
32
48
|
function renderContent({ controller, page, selectedItem, onSelectItem }) {
|
|
33
49
|
// show loader if not ready
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nascentdigital/funnel-editor-contentful",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "restricted"
|
|
@@ -37,9 +37,13 @@
|
|
|
37
37
|
"react-dom": "^19"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@contentful/app-sdk": "^4
|
|
40
|
+
"@contentful/app-sdk": "^4",
|
|
41
41
|
"@contentful/react-apps-toolkit": "^2.0.1",
|
|
42
|
-
"@nascentdigital/funnel-
|
|
42
|
+
"@nascentdigital/funnel-cms": "^5.0.2",
|
|
43
|
+
"@nascentdigital/funnel-editor": "^5.0.2",
|
|
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": "
|
|
62
|
+
"gitHead": "24d9690cf813052f6561c3ccef11c7cf69ad7f7c"
|
|
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,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
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EditorApiClient = void 0;
|
|
4
|
-
// class
|
|
5
|
-
class EditorApiClient {
|
|
6
|
-
constructor(sdk) {
|
|
7
|
-
this.sdk = sdk;
|
|
8
|
-
}
|
|
9
|
-
async currentPageGroup() {
|
|
10
|
-
const entry = this.sdk.entry;
|
|
11
|
-
const { type, content } = entry.fields;
|
|
12
|
-
return {
|
|
13
|
-
group: {},
|
|
14
|
-
page: {
|
|
15
|
-
data: {
|
|
16
|
-
id: entry.getSys().id,
|
|
17
|
-
type: 'home', // type.getValue(),
|
|
18
|
-
region: 'ca',
|
|
19
|
-
language: 'en',
|
|
20
|
-
content: content.getValue(),
|
|
21
|
-
},
|
|
22
|
-
files: {},
|
|
23
|
-
pages: {},
|
|
24
|
-
collections: {},
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
exports.EditorApiClient = EditorApiClient;
|