@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
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { AssetClient } from '@nascentdigital/funnel-cms';
|
|
1
|
+
import { AssetClient, CollectionClient } from '@nascentdigital/funnel-cms';
|
|
2
|
+
import { ContentfulCollectionData, ContentfulData } from '@nascentdigital/funnel-cms/contentful';
|
|
2
3
|
import { EventBus, Field, FieldModel, Language, Schema } from '@nascentdigital/funnel-core';
|
|
3
|
-
import { Asset, CollectionGroup, ICollection, IPage } from '@nascentdigital/funnel-editor/components';
|
|
4
|
+
import { Asset, CollectionFieldProxy, CollectionGroup, ICollection, IPage } from '@nascentdigital/funnel-editor/components';
|
|
4
5
|
import { Observable } from 'rxjs';
|
|
5
6
|
export declare class Collection implements ICollection {
|
|
6
7
|
readonly template: Schema.Collection;
|
|
@@ -27,6 +28,7 @@ export declare class Collection implements ICollection {
|
|
|
27
28
|
private _initialErrors;
|
|
28
29
|
private _syncing;
|
|
29
30
|
private _savingOn?;
|
|
31
|
+
readonly references: Readonly<ContentfulData.References>;
|
|
30
32
|
constructor(args: Collection.ConstructorArgs);
|
|
31
33
|
get events$(): Observable<ICollection.Events>;
|
|
32
34
|
get stable$(): Observable<boolean>;
|
|
@@ -91,9 +93,16 @@ export declare namespace Collection {
|
|
|
91
93
|
createdOn: number;
|
|
92
94
|
updatedOn: number;
|
|
93
95
|
publishedOn?: number;
|
|
96
|
+
references: ContentfulData.References;
|
|
94
97
|
};
|
|
95
98
|
type WithGroup = {
|
|
96
99
|
group: CollectionGroup;
|
|
97
100
|
collection?: Collection;
|
|
98
101
|
};
|
|
102
|
+
type FromSchemaData = {
|
|
103
|
+
data: ContentfulCollectionData.ForSchema;
|
|
104
|
+
references?: Schema.Collection.References;
|
|
105
|
+
};
|
|
106
|
+
function fromSchemaData({ data, references }: FromSchemaData): Collection;
|
|
107
|
+
function toUpdates(collection: Collection | CollectionFieldProxy, savables: ICollection.Savable[]): CollectionClient.Update.Args;
|
|
99
108
|
}
|
|
@@ -1,4 +1,15 @@
|
|
|
1
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
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.Collection = void 0;
|
|
4
15
|
const funnel_core_1 = require("@nascentdigital/funnel-core");
|
|
@@ -41,6 +52,7 @@ class Collection {
|
|
|
41
52
|
this.createdOn = args.createdOn;
|
|
42
53
|
this._updatedOn = args.updatedOn;
|
|
43
54
|
this.publishedOn = args.publishedOn;
|
|
55
|
+
this.references = args.references;
|
|
44
56
|
this._state = args.state;
|
|
45
57
|
this._initialErrors = args.errors;
|
|
46
58
|
this._syncing = false;
|
|
@@ -309,3 +321,66 @@ class Collection {
|
|
|
309
321
|
}
|
|
310
322
|
}
|
|
311
323
|
exports.Collection = Collection;
|
|
324
|
+
// extension
|
|
325
|
+
(function (Collection) {
|
|
326
|
+
function fromSchemaData({ data, references }) {
|
|
327
|
+
// setup
|
|
328
|
+
const { snapshot, fields, tags } = data, collectionArgs = __rest(data, ["snapshot", "fields", "tags"]);
|
|
329
|
+
const { files = {}, collections = {} } = references !== null && references !== void 0 ? references : {};
|
|
330
|
+
const { fields: fieldsSnapshot = {} } = snapshot !== null && snapshot !== void 0 ? snapshot : {};
|
|
331
|
+
const template = funnel_core_1.Schema.Site.getCollection(data.type);
|
|
332
|
+
const eventBus = new funnel_core_1.EventBus();
|
|
333
|
+
// create collection
|
|
334
|
+
return new Collection(Object.assign(Object.assign({}, collectionArgs), { eventBus,
|
|
335
|
+
template, tags: components_1.TagSerializer.deserializeTags({
|
|
336
|
+
groups: template.tags,
|
|
337
|
+
tagsData: tags !== null && tags !== void 0 ? tags : []
|
|
338
|
+
}), fields: Object.entries(template.editableFields).map(([id, schema]) => components_1.FieldSerializer.deserializeField({
|
|
339
|
+
id,
|
|
340
|
+
eventBus,
|
|
341
|
+
schema,
|
|
342
|
+
content: fields[id],
|
|
343
|
+
snapshot: fieldsSnapshot[id]
|
|
344
|
+
})), files: Object.values(files), collections: Object.values(collections) }));
|
|
345
|
+
}
|
|
346
|
+
Collection.fromSchemaData = fromSchemaData;
|
|
347
|
+
function toUpdates(collection, savables) {
|
|
348
|
+
// add name updates
|
|
349
|
+
const { id, nameField, name, initialTags, tags, fields, initialErrors, valid } = collection;
|
|
350
|
+
const updates = [];
|
|
351
|
+
if (nameField.modified) {
|
|
352
|
+
updates.push({
|
|
353
|
+
type: 'collection',
|
|
354
|
+
property: 'name',
|
|
355
|
+
value: name
|
|
356
|
+
});
|
|
357
|
+
// track change
|
|
358
|
+
savables.push(nameField);
|
|
359
|
+
}
|
|
360
|
+
// add tag updates
|
|
361
|
+
if (initialTags.symmetricDifference(tags).size > 0) {
|
|
362
|
+
updates.push({
|
|
363
|
+
type: 'collection',
|
|
364
|
+
property: 'tags',
|
|
365
|
+
value: Array.from(tags).map(tag => tag.data)
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
// add errors updates
|
|
369
|
+
if (initialErrors !== !valid) {
|
|
370
|
+
updates.push({
|
|
371
|
+
type: 'collection',
|
|
372
|
+
property: 'errors',
|
|
373
|
+
value: !valid
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// add fields
|
|
377
|
+
components_1.SerializeCollectionUpdate.serializeFieldsUpdate(updates, fields, ['fields'], savables);
|
|
378
|
+
// return updates
|
|
379
|
+
return {
|
|
380
|
+
id,
|
|
381
|
+
updates,
|
|
382
|
+
updatedOn: Date.now()
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
Collection.toUpdates = toUpdates;
|
|
386
|
+
})(Collection || (exports.Collection = Collection = {}));
|
package/dist/collection/index.js
CHANGED
|
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./screens"), exports);
|
|
18
18
|
__exportStar(require("./controller"), exports);
|
|
19
|
+
__exportStar(require("./repository"), exports);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EditorAppSDK, EntrySys } from '@contentful/app-sdk';
|
|
2
|
+
import { ContentfulContentClient } from '@nascentdigital/funnel-cms/contentful';
|
|
3
|
+
import { Schema } from '@nascentdigital/funnel-core';
|
|
4
|
+
import { CollectionFieldProxy, CollectionGroup, ICollection, ICollectionRepository } from '@nascentdigital/funnel-editor/components';
|
|
5
|
+
import { Collection } from '../domain';
|
|
6
|
+
export declare class CollectionRepository implements ICollectionRepository {
|
|
7
|
+
readonly sdk: EditorAppSDK;
|
|
8
|
+
readonly contentfulClient: ContentfulContentClient;
|
|
9
|
+
constructor(args: CollectionRepository.ConstructorArgs);
|
|
10
|
+
getOverview(args: ICollectionRepository.GetOverview.Args): Promise<ICollectionRepository.GetOverview.Result>;
|
|
11
|
+
getItems(args: ICollectionRepository.GetItems.Args): Promise<ICollectionRepository.GetItems.Result>;
|
|
12
|
+
get(args: ICollectionRepository.Get.Args): Promise<ICollectionRepository.Get.Result>;
|
|
13
|
+
create(args: ICollectionRepository.Create.Args): Promise<ICollectionRepository.Create.Result>;
|
|
14
|
+
createRaw(args: ICollectionRepository.CreateRaw.Args): Promise<ICollectionRepository.CreateRaw.Result>;
|
|
15
|
+
createLanguage(args: ICollectionRepository.CreateLang.Args): Promise<ICollectionRepository.CreateLang.Result>;
|
|
16
|
+
update(args: ICollectionRepository.Update.Args): Promise<ICollectionRepository.Update.Result>;
|
|
17
|
+
serializeUpdate(collection: ICollection | CollectionFieldProxy, savables: ICollection.Savable[]): ICollectionRepository.SerializeUpdates.Result;
|
|
18
|
+
load(sys?: EntrySys): Promise<CollectionRepository.Load.Result>;
|
|
19
|
+
save(collection: Collection): Promise<Collection>;
|
|
20
|
+
sync({ collection, sys }: CollectionRepository.Sync.Args): Promise<CollectionRepository.Sync.Result>;
|
|
21
|
+
unpublish(collection: ICollection): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export declare namespace CollectionRepository {
|
|
24
|
+
type ConstructorArgs = {
|
|
25
|
+
sdk: EditorAppSDK;
|
|
26
|
+
contentfulClient: ContentfulContentClient;
|
|
27
|
+
};
|
|
28
|
+
namespace Load {
|
|
29
|
+
type Args = {
|
|
30
|
+
collectionId?: Schema.Collection.Id;
|
|
31
|
+
};
|
|
32
|
+
type Result = {
|
|
33
|
+
collectionGroup: CollectionGroup;
|
|
34
|
+
collection: Collection;
|
|
35
|
+
sys: EntrySys;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
namespace Sync {
|
|
39
|
+
type Args = {
|
|
40
|
+
collection: Collection;
|
|
41
|
+
sys: EntrySys;
|
|
42
|
+
};
|
|
43
|
+
type Result = {
|
|
44
|
+
collectionGroup?: CollectionGroup;
|
|
45
|
+
collection: Collection;
|
|
46
|
+
sys: EntrySys;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -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);
|
|
@@ -2,24 +2,41 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.CollectionEditor = CollectionEditor;
|
|
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 controller_1 = require("../controller");
|
|
8
9
|
// component
|
|
9
10
|
function CollectionEditor() {
|
|
10
11
|
// hooks
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const { collection } = controller;
|
|
12
|
+
const splash = (0, components_1.useSplash)();
|
|
13
|
+
const controller = (0, controller_1.useContentfulCollectionController)();
|
|
14
|
+
const { ready, collection } = controller;
|
|
14
15
|
const collectionContext = (0, react_1.useMemo)(() => ({ collection }), [collection]);
|
|
15
16
|
// sync with controller
|
|
16
17
|
(0, components_1.useCollectionController)(controller);
|
|
17
18
|
// load
|
|
18
19
|
(0, react_1.useEffect)(() => {
|
|
19
|
-
controller.load().catch(
|
|
20
|
-
|
|
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]);
|
|
21
38
|
// render
|
|
22
|
-
return ((0, jsx_runtime_1.jsx)(components_1.
|
|
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 }) }) }) }) }) }) }) }));
|
|
23
40
|
}
|
|
24
41
|
function renderContent({ controller }) {
|
|
25
42
|
// render loader if not ready
|
|
@@ -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,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
|
+
}
|
|
@@ -8,12 +8,12 @@ export type ContentfulAsset = Contentful.Base & {
|
|
|
8
8
|
description?: string | null;
|
|
9
9
|
brand?: string | null;
|
|
10
10
|
type: string;
|
|
11
|
-
media: Contentful.Link.Asset;
|
|
12
|
-
metadata: object;
|
|
13
11
|
scope: string;
|
|
14
|
-
tags?: string
|
|
12
|
+
tags?: string[];
|
|
15
13
|
url: string;
|
|
16
14
|
size: number;
|
|
15
|
+
metadata: object;
|
|
16
|
+
media: Contentful.Link.Asset;
|
|
17
17
|
};
|
|
18
18
|
export declare namespace ContentfulAsset {
|
|
19
19
|
const contentTypeId: "asset";
|