@nascentdigital/funnel-editor-contentful 4.6.7 → 5.0.1
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/Editor.d.ts +6 -1
- package/dist/Editor.js +17 -1
- package/dist/EditorApp.js +1 -1
- package/dist/EntryEditor.js +42 -1
- package/dist/EntrySidebar.js +18 -1
- package/dist/collection/controller/LocalCollectionController.d.ts +35 -0
- package/dist/collection/controller/LocalCollectionController.js +200 -0
- package/dist/collection/controller/LocalCollectionControllerContainer.d.ts +8 -0
- package/dist/collection/controller/LocalCollectionControllerContainer.js +36 -0
- package/dist/collection/controller/LocalCollectionControllerContext.d.ts +7 -0
- package/dist/collection/controller/LocalCollectionControllerContext.js +6 -0
- package/dist/collection/controller/index.d.ts +4 -0
- package/dist/collection/controller/index.js +20 -0
- package/dist/collection/controller/useLocalCollectionController.d.ts +2 -0
- package/dist/collection/controller/useLocalCollectionController.js +10 -0
- package/dist/collection/index.d.ts +2 -0
- package/dist/collection/index.js +18 -0
- package/dist/collection/screens/CollectionEditor.d.ts +1 -0
- package/dist/collection/screens/CollectionEditor.js +42 -0
- package/dist/collection/screens/index.d.ts +1 -0
- package/dist/{page/hooks → collection/screens}/index.js +1 -1
- package/dist/core/bridge/AppBridgeContainer.d.ts +9 -0
- package/dist/core/bridge/AppBridgeContainer.js +55 -0
- package/dist/core/bridge/AppBridgeContext.d.ts +8 -0
- package/dist/core/bridge/AppBridgeContext.js +11 -0
- package/dist/core/bridge/AppMessage.d.ts +19 -0
- package/dist/core/bridge/AppMessage.js +19 -0
- package/dist/core/bridge/index.d.ts +4 -0
- package/dist/core/bridge/index.js +20 -0
- package/dist/core/bridge/useAppBridge.d.ts +8 -0
- package/dist/core/bridge/useAppBridge.js +24 -0
- package/dist/core/contentful/Contentful.d.ts +33 -0
- package/dist/core/contentful/Contentful.js +67 -0
- package/dist/core/contentful/ContentfulAsset.d.ts +23 -0
- package/dist/core/contentful/ContentfulAsset.js +88 -0
- package/dist/core/contentful/ContentfulCollection.d.ts +29 -0
- package/dist/core/contentful/ContentfulCollection.js +97 -0
- package/dist/core/contentful/ContentfulPage.d.ts +31 -0
- package/dist/core/contentful/ContentfulPage.js +84 -0
- package/dist/core/contentful/ContentfulRefs.d.ts +16 -0
- package/dist/core/contentful/ContentfulRefs.js +152 -0
- package/dist/core/contentful/index.d.ts +5 -0
- package/dist/core/contentful/index.js +21 -0
- package/dist/core/domain/Collection.d.ts +99 -0
- package/dist/core/domain/Collection.js +311 -0
- package/dist/core/domain/Page.d.ts +58 -8
- package/dist/core/domain/Page.js +329 -16
- package/dist/core/domain/index.d.ts +1 -0
- package/dist/core/domain/index.js +1 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core/repository/AssetRepository.d.ts +10 -0
- package/dist/core/repository/AssetRepository.js +33 -0
- package/dist/core/repository/CollectionSerializer.d.ts +9 -0
- package/dist/core/repository/CollectionSerializer.js +41 -0
- package/dist/core/repository/LocalCollectionRepository.d.ts +27 -0
- package/dist/core/repository/LocalCollectionRepository.js +143 -0
- package/dist/core/repository/LocalPageRepository.d.ts +16 -0
- package/dist/core/repository/LocalPageRepository.js +153 -0
- package/dist/core/repository/PageSerializer.d.ts +2 -2
- package/dist/core/repository/PageSerializer.js +16 -13
- package/dist/core/repository/RemoteCollectionRepository.d.ts +19 -0
- package/dist/core/repository/RemoteCollectionRepository.js +236 -0
- package/dist/core/repository/RemotePageRepository.d.ts +14 -0
- package/dist/core/repository/RemotePageRepository.js +113 -0
- package/dist/core/repository/index.d.ts +5 -1
- package/dist/core/repository/index.js +5 -1
- package/dist/page/{hooks/PageController.d.ts → controller/LocalPageController.d.ts} +11 -6
- package/dist/page/controller/LocalPageController.js +190 -0
- package/dist/page/controller/LocalPageControllerContainer.d.ts +8 -0
- package/dist/page/controller/LocalPageControllerContainer.js +36 -0
- package/dist/page/controller/LocalPageControllerContext.d.ts +7 -0
- package/dist/page/controller/LocalPageControllerContext.js +6 -0
- package/dist/page/controller/index.d.ts +3 -0
- package/dist/page/controller/index.js +19 -0
- package/dist/page/controller/useLocalPageController.d.ts +2 -0
- package/dist/page/controller/useLocalPageController.js +10 -0
- package/dist/page/index.d.ts +1 -0
- package/dist/page/index.js +1 -0
- package/dist/page/screens/PageEditor.js +9 -11
- package/package.json +5 -3
- package/dist/core/repository/PageRepository.d.ts +0 -12
- package/dist/core/repository/PageRepository.js +0 -23
- package/dist/page/hooks/PageController.js +0 -107
- package/dist/page/hooks/index.d.ts +0 -1
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RemoteCollectionRepository = void 0;
|
|
4
|
+
const funnel_core_1 = require("@nascentdigital/funnel-core");
|
|
5
|
+
const components_1 = require("@nascentdigital/funnel-editor/components");
|
|
6
|
+
const contentful_1 = require("../contentful");
|
|
7
|
+
// class
|
|
8
|
+
class RemoteCollectionRepository {
|
|
9
|
+
constructor(sdk) {
|
|
10
|
+
this.sdk = sdk;
|
|
11
|
+
}
|
|
12
|
+
async create(args) {
|
|
13
|
+
throw new funnel_core_1.NotImplementedError();
|
|
14
|
+
}
|
|
15
|
+
async createRaw(args) {
|
|
16
|
+
// locale
|
|
17
|
+
const locale = this.sdk.locales.default;
|
|
18
|
+
const groupId = funnel_core_1.Value.uuid();
|
|
19
|
+
const brand = funnel_core_1.AppConfig.brand();
|
|
20
|
+
const name = args.name;
|
|
21
|
+
// create entry
|
|
22
|
+
const entry = await this.sdk.cma.entry.create({ contentTypeId: contentful_1.ContentfulCollection.contentTypeId }, {
|
|
23
|
+
fields: {
|
|
24
|
+
groupId: { [locale]: groupId },
|
|
25
|
+
type: { [locale]: args.template.type },
|
|
26
|
+
version: { [locale]: args.template.version },
|
|
27
|
+
brand: { [locale]: brand },
|
|
28
|
+
name: { [locale]: name },
|
|
29
|
+
tags: { [locale]: JSON.stringify(args.tags) },
|
|
30
|
+
language: { [locale]: args.language },
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// serialize entry to data
|
|
34
|
+
const data = contentful_1.ContentfulCollection.serializeRemote(entry, locale);
|
|
35
|
+
// return as group data
|
|
36
|
+
return {
|
|
37
|
+
collection: {
|
|
38
|
+
data,
|
|
39
|
+
collections: {},
|
|
40
|
+
files: {}
|
|
41
|
+
},
|
|
42
|
+
group: {
|
|
43
|
+
groupId,
|
|
44
|
+
type: args.template.type,
|
|
45
|
+
brand,
|
|
46
|
+
name,
|
|
47
|
+
instances: [{
|
|
48
|
+
id: data.id,
|
|
49
|
+
version: data.version,
|
|
50
|
+
language: data.language,
|
|
51
|
+
tags: data.tags
|
|
52
|
+
}]
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async createLanguage(args) {
|
|
57
|
+
throw new funnel_core_1.NotImplementedError();
|
|
58
|
+
}
|
|
59
|
+
async save(collection) {
|
|
60
|
+
throw new funnel_core_1.NotImplementedError();
|
|
61
|
+
}
|
|
62
|
+
async unpublish(collection) {
|
|
63
|
+
throw new funnel_core_1.NotImplementedError();
|
|
64
|
+
}
|
|
65
|
+
async getOverview(args) {
|
|
66
|
+
// locale
|
|
67
|
+
const locale = this.sdk.locales.default;
|
|
68
|
+
// collection templates
|
|
69
|
+
const templates = funnel_core_1.Schema.Site.current().collections;
|
|
70
|
+
// set counts to zero
|
|
71
|
+
const collectionCounts = new Map();
|
|
72
|
+
templates.forEach(template => collectionCounts.set(template.type, 0));
|
|
73
|
+
// fetch thin collections
|
|
74
|
+
const collections = await contentful_1.Contentful.allEntries(this.sdk, {
|
|
75
|
+
content_type: contentful_1.ContentfulCollection.contentTypeId,
|
|
76
|
+
select: 'fields.type'
|
|
77
|
+
});
|
|
78
|
+
// count each type
|
|
79
|
+
collections.forEach(collection => {
|
|
80
|
+
const type = collection.fields.type[locale];
|
|
81
|
+
const count = collectionCounts.get(type);
|
|
82
|
+
if (count !== undefined) {
|
|
83
|
+
collectionCounts.set(type, count + 1);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// return decorated templates
|
|
87
|
+
return templates.map(template => {
|
|
88
|
+
var _a;
|
|
89
|
+
return ({
|
|
90
|
+
template,
|
|
91
|
+
type: template.type,
|
|
92
|
+
count: (_a = collectionCounts.get(template.type)) !== null && _a !== void 0 ? _a : 0
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async getItems({ type, tags, language }) {
|
|
97
|
+
// locale
|
|
98
|
+
const locale = this.sdk.locales.default;
|
|
99
|
+
// query entries
|
|
100
|
+
// TODO: implement tags filter
|
|
101
|
+
const items = await contentful_1.Contentful.allEntries(this.sdk, {
|
|
102
|
+
content_type: contentful_1.ContentfulCollection.contentTypeId,
|
|
103
|
+
[`fields.type.${locale}`]: type
|
|
104
|
+
});
|
|
105
|
+
// group entries by groupId
|
|
106
|
+
const groups = new Map();
|
|
107
|
+
items.forEach(item => {
|
|
108
|
+
const groupId = item.fields.groupId[locale];
|
|
109
|
+
const group = groups.get(groupId);
|
|
110
|
+
if (group) {
|
|
111
|
+
group.push(item);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
groups.set(groupId, [item]);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
// create items
|
|
118
|
+
const result = [];
|
|
119
|
+
for (const [groupId, entries] of groups) {
|
|
120
|
+
// find entry matching arg language
|
|
121
|
+
const mainEntry = entries.find(entry => entry.fields.language[locale] === language);
|
|
122
|
+
if (!mainEntry) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// create item
|
|
126
|
+
result.push({
|
|
127
|
+
id: mainEntry.sys.id,
|
|
128
|
+
type: mainEntry.fields.type[locale],
|
|
129
|
+
groupId: groupId,
|
|
130
|
+
name: mainEntry.fields.name[locale],
|
|
131
|
+
refs: entries.reduce((refs, ref) => {
|
|
132
|
+
// serialize ref
|
|
133
|
+
const refLang = ref.fields.language[locale];
|
|
134
|
+
refs[refLang] = contentful_1.ContentfulCollection.serializeRemoteRef(ref, locale);
|
|
135
|
+
// keep reducing
|
|
136
|
+
return refs;
|
|
137
|
+
}, {})
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
async get({ id }) {
|
|
143
|
+
// locale
|
|
144
|
+
const locale = this.sdk.locales.default;
|
|
145
|
+
// search by id (expect 0 or 1 items)
|
|
146
|
+
const response = await this.sdk.cma.entry.getMany({
|
|
147
|
+
query: { 'sys.id': id, include: 1 }
|
|
148
|
+
});
|
|
149
|
+
// no results
|
|
150
|
+
if (response.items.length === 0) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
// bad state
|
|
154
|
+
else if (response.items.length > 1) {
|
|
155
|
+
throw new funnel_core_1.IllegalStateError(`There is more than one entry with id "${id}"`);
|
|
156
|
+
}
|
|
157
|
+
// serialize single item
|
|
158
|
+
else {
|
|
159
|
+
return contentful_1.ContentfulCollection.serializeRemoteWithRefs(response.items[0], locale);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async update(args) {
|
|
163
|
+
// locale
|
|
164
|
+
const locale = this.sdk.locales.default;
|
|
165
|
+
// fetch latest version of collection
|
|
166
|
+
const entry = await this.sdk.cma.entry.get({ entryId: args.id });
|
|
167
|
+
const data = contentful_1.ContentfulCollection.serializeRemote(entry, locale);
|
|
168
|
+
// apply updates
|
|
169
|
+
const model = new funnel_core_1.Schema.CollectionModel(data);
|
|
170
|
+
model.update(args.updates);
|
|
171
|
+
// update fields
|
|
172
|
+
entry.fields.name = { [locale]: data.name };
|
|
173
|
+
entry.fields.fields = { [locale]: data.fields };
|
|
174
|
+
// update entry
|
|
175
|
+
const updatedEntry = await this.sdk.cma.entry.update({ entryId: args.id }, entry);
|
|
176
|
+
const updatedOn = new Date(updatedEntry.sys.updatedAt).getTime();
|
|
177
|
+
// return
|
|
178
|
+
return {
|
|
179
|
+
// TODO: get latest references
|
|
180
|
+
collection: { data, collections: {}, files: {} },
|
|
181
|
+
updatedOn,
|
|
182
|
+
changeSetId: ''
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
serializeUpdate(collection, savables) {
|
|
186
|
+
// add name updates
|
|
187
|
+
const { id, nameField, name, initialTags, tags, fields, initialErrors, valid } = collection;
|
|
188
|
+
const updates = [];
|
|
189
|
+
if (nameField.modified) {
|
|
190
|
+
updates.push({
|
|
191
|
+
type: 'collection',
|
|
192
|
+
property: 'name',
|
|
193
|
+
value: name
|
|
194
|
+
});
|
|
195
|
+
// track change
|
|
196
|
+
savables.push(nameField);
|
|
197
|
+
}
|
|
198
|
+
// add tag updates
|
|
199
|
+
if (initialTags.symmetricDifference(tags).size > 0) {
|
|
200
|
+
updates.push({
|
|
201
|
+
type: 'collection',
|
|
202
|
+
property: 'tags',
|
|
203
|
+
value: Array.from(tags).map(tag => tag.data)
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// add errors updates
|
|
207
|
+
if (initialErrors !== !valid) {
|
|
208
|
+
updates.push({
|
|
209
|
+
type: 'collection',
|
|
210
|
+
property: 'errors',
|
|
211
|
+
value: !valid
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// add fields
|
|
215
|
+
components_1.SerializeCollectionUpdate.serializeFieldsUpdate(updates, fields, ['fields'], savables);
|
|
216
|
+
// return updates
|
|
217
|
+
return {
|
|
218
|
+
id,
|
|
219
|
+
updates,
|
|
220
|
+
updatedOn: Date.now()
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
async getReferences(collectionId) {
|
|
224
|
+
// setup
|
|
225
|
+
const { sdk } = this;
|
|
226
|
+
const locale = sdk.locales.default;
|
|
227
|
+
// fetch references
|
|
228
|
+
const references = await sdk.cma.entry.references({
|
|
229
|
+
entryId: collectionId,
|
|
230
|
+
include: 10
|
|
231
|
+
});
|
|
232
|
+
// serialize and return
|
|
233
|
+
return contentful_1.ContentfulRefs.serialize(references, locale);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
exports.RemoteCollectionRepository = RemoteCollectionRepository;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { EditorAppSDK } from '@contentful/app-sdk';
|
|
2
|
+
import { Schema } from '@nascentdigital/funnel-core';
|
|
3
|
+
import { IPage, IPageRepository } from '@nascentdigital/funnel-editor/components';
|
|
4
|
+
export declare class RemotePageRepository implements IPageRepository {
|
|
5
|
+
readonly sdk: EditorAppSDK;
|
|
6
|
+
constructor(sdk: EditorAppSDK);
|
|
7
|
+
search(args: IPageRepository.Search.Args): Promise<IPageRepository.Search.Result>;
|
|
8
|
+
create(args: IPageRepository.Create.Args): Promise<IPageRepository.Create.Result>;
|
|
9
|
+
createLanguage(args: IPageRepository.CreateLang.Args): Promise<IPageRepository.CreateLang.Result>;
|
|
10
|
+
save(page: IPage): Promise<IPage>;
|
|
11
|
+
unpublish(page: IPage): Promise<void>;
|
|
12
|
+
getOverview(args: IPageRepository.GetOverview.Args): Promise<IPageRepository.GetOverview.Result>;
|
|
13
|
+
getReferences(pageId: Schema.Page.Id): Promise<IPage.References>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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.RemotePageRepository = void 0;
|
|
15
|
+
const funnel_core_1 = require("@nascentdigital/funnel-core");
|
|
16
|
+
const components_1 = require("@nascentdigital/funnel-editor/components");
|
|
17
|
+
const contentful_1 = require("../contentful");
|
|
18
|
+
// class
|
|
19
|
+
class RemotePageRepository {
|
|
20
|
+
constructor(sdk) {
|
|
21
|
+
this.sdk = sdk;
|
|
22
|
+
}
|
|
23
|
+
async search(args) {
|
|
24
|
+
// locale
|
|
25
|
+
const locale = this.sdk.locales.default;
|
|
26
|
+
// templates
|
|
27
|
+
const templates = funnel_core_1.Schema.Site.current().regions[args.region];
|
|
28
|
+
// fetch all pages
|
|
29
|
+
// TODO: implement paging
|
|
30
|
+
// TODO: implement other search args
|
|
31
|
+
const pages = await contentful_1.Contentful.allEntries(this.sdk, {
|
|
32
|
+
content_type: contentful_1.ContentfulPage.contentTypeId,
|
|
33
|
+
[`fields.type`]: args.template.type
|
|
34
|
+
});
|
|
35
|
+
// return result
|
|
36
|
+
return {
|
|
37
|
+
items: pages.map(page => {
|
|
38
|
+
const _a = contentful_1.ContentfulPage.serializeRemoteLink(page, locale), { type, tags } = _a, data = __rest(_a, ["type", "tags"]);
|
|
39
|
+
const template = templates[type];
|
|
40
|
+
return Object.assign({ template, tags: components_1.TagSerializer.deserializeTags({
|
|
41
|
+
groups: template.tags,
|
|
42
|
+
tagsData: tags
|
|
43
|
+
}) }, data);
|
|
44
|
+
}),
|
|
45
|
+
page: 1,
|
|
46
|
+
pageCount: 1,
|
|
47
|
+
totalItemCount: pages.length
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async create(args) {
|
|
51
|
+
throw new funnel_core_1.NotImplementedError();
|
|
52
|
+
}
|
|
53
|
+
async createLanguage(args) {
|
|
54
|
+
throw new funnel_core_1.NotImplementedError();
|
|
55
|
+
}
|
|
56
|
+
async save(page) {
|
|
57
|
+
throw new funnel_core_1.NotImplementedError();
|
|
58
|
+
}
|
|
59
|
+
async unpublish(page) {
|
|
60
|
+
throw new funnel_core_1.NotImplementedError();
|
|
61
|
+
}
|
|
62
|
+
async getOverview(args) {
|
|
63
|
+
// fetch all pages
|
|
64
|
+
const pages = await contentful_1.Contentful.allEntriesNormalized(this.sdk, {
|
|
65
|
+
content_type: contentful_1.ContentfulPage.contentTypeId
|
|
66
|
+
});
|
|
67
|
+
// enrich with templates and flip to array
|
|
68
|
+
const templates = Object.values(funnel_core_1.Schema.Site.current().regions[args.region]);
|
|
69
|
+
return templates.map(template => {
|
|
70
|
+
// extract static page overview
|
|
71
|
+
const { type } = template;
|
|
72
|
+
if (template.variant === 'static') {
|
|
73
|
+
const staticPages = pages.filter(page => page.type === type);
|
|
74
|
+
return new components_1.StaticPageOverview({
|
|
75
|
+
template,
|
|
76
|
+
type,
|
|
77
|
+
refs: staticPages.reduce((refs, page) => {
|
|
78
|
+
// page language (bail if no language)
|
|
79
|
+
const language = page.language;
|
|
80
|
+
if (!language) {
|
|
81
|
+
return refs;
|
|
82
|
+
}
|
|
83
|
+
// add page link
|
|
84
|
+
refs[language] = contentful_1.ContentfulPage.normalizedToLink(page);
|
|
85
|
+
// keep reducing
|
|
86
|
+
return refs;
|
|
87
|
+
}, {})
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// or dynamic one
|
|
91
|
+
else {
|
|
92
|
+
return new components_1.DynamicPageOverview({
|
|
93
|
+
template,
|
|
94
|
+
type,
|
|
95
|
+
count: pages.filter(page => page.type === type).length,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async getReferences(pageId) {
|
|
101
|
+
// setup
|
|
102
|
+
const { sdk } = this;
|
|
103
|
+
const locale = sdk.locales.default;
|
|
104
|
+
// fetch references
|
|
105
|
+
const references = await sdk.cma.entry.references({
|
|
106
|
+
entryId: pageId,
|
|
107
|
+
include: 10
|
|
108
|
+
});
|
|
109
|
+
// serialize and return
|
|
110
|
+
return contentful_1.ContentfulRefs.serialize(references, locale);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.RemotePageRepository = RemotePageRepository;
|
|
@@ -14,4 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./AssetRepository"), exports);
|
|
18
|
+
__exportStar(require("./LocalCollectionRepository"), exports);
|
|
19
|
+
__exportStar(require("./LocalPageRepository"), exports);
|
|
20
|
+
__exportStar(require("./RemoteCollectionRepository"), exports);
|
|
21
|
+
__exportStar(require("./RemotePageRepository"), exports);
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Language } from '@nascentdigital/funnel-core';
|
|
2
2
|
import { IPage, IPageController, PageSummary } from '@nascentdigital/funnel-editor/components';
|
|
3
|
-
import {
|
|
4
|
-
export declare class
|
|
3
|
+
import { Page, LocalPageRepository, RemotePageRepository } from '../../core';
|
|
4
|
+
export declare class LocalPageController implements IPageController {
|
|
5
|
+
readonly localRepository: LocalPageRepository;
|
|
6
|
+
readonly remoteRepository: RemotePageRepository;
|
|
5
7
|
private readonly _eventBus;
|
|
6
8
|
private _state;
|
|
7
9
|
private _pageSummary?;
|
|
8
10
|
private _page?;
|
|
9
|
-
|
|
10
|
-
constructor({
|
|
11
|
+
private _pageSubscription?;
|
|
12
|
+
constructor({ localRepository, remoteRepository }: LocalPageController.ConstructorArgs);
|
|
11
13
|
get events$(): import("rxjs").Observable<IPage.Event | IPageController.Event>;
|
|
12
14
|
get saved$(): import("rxjs").Observable<[IPageController.StateChangedEvent, IPageController.StateChangedEvent]>;
|
|
13
15
|
get state(): IPageController.State;
|
|
@@ -18,14 +20,17 @@ export declare class PageController implements IPageController {
|
|
|
18
20
|
private set pageSummary(value);
|
|
19
21
|
get page(): Page | undefined;
|
|
20
22
|
private set page(value);
|
|
23
|
+
sync(updatedOn: number): Promise<void>;
|
|
21
24
|
load(): Promise<void>;
|
|
22
25
|
create(target: IPageController.Target): Promise<void>;
|
|
26
|
+
save(page: Page): Promise<void>;
|
|
23
27
|
reload(): Promise<void>;
|
|
24
28
|
unpublish(): Promise<void>;
|
|
25
29
|
private raiseEvent;
|
|
26
30
|
}
|
|
27
|
-
export declare namespace
|
|
31
|
+
export declare namespace LocalPageController {
|
|
28
32
|
type ConstructorArgs = {
|
|
29
|
-
|
|
33
|
+
localRepository: LocalPageRepository;
|
|
34
|
+
remoteRepository: RemotePageRepository;
|
|
30
35
|
};
|
|
31
36
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalPageController = 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 LocalPageController {
|
|
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$.pipe((0, rxjs_1.filter)((e) => components_1.IPageController.Event.filter(e) || components_1.IPage.Event.filter(e)));
|
|
18
|
+
}
|
|
19
|
+
get saved$() {
|
|
20
|
+
return this._eventBus.events$.pipe((0, rxjs_1.filter)(components_1.IPageController.StateChangedEvent.filter), (0, rxjs_1.pairwise)(), (0, rxjs_1.filter)(([previous, current]) => previous.state === 'saving' && current.state === 'ready'));
|
|
21
|
+
}
|
|
22
|
+
get state() {
|
|
23
|
+
return this._state;
|
|
24
|
+
}
|
|
25
|
+
set state(value) {
|
|
26
|
+
// skip if unchanged
|
|
27
|
+
if (this._state === value) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// or update
|
|
31
|
+
this._state = value;
|
|
32
|
+
// raise event
|
|
33
|
+
this._eventBus.publish(components_1.IPageController.StateChangedEvent.create({
|
|
34
|
+
state: value,
|
|
35
|
+
controller: this
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
get ready() {
|
|
39
|
+
const { _state } = this;
|
|
40
|
+
return _state !== 'uninitialized' && _state !== 'initializing';
|
|
41
|
+
}
|
|
42
|
+
get languages() {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
get pageSummary() {
|
|
46
|
+
return this._pageSummary;
|
|
47
|
+
}
|
|
48
|
+
set pageSummary(pageSummary) {
|
|
49
|
+
// skip if summary is the same
|
|
50
|
+
if (this._pageSummary === pageSummary) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// update summary
|
|
54
|
+
this._pageSummary = pageSummary;
|
|
55
|
+
// raise event
|
|
56
|
+
this.raiseEvent('pageSummary.changed');
|
|
57
|
+
}
|
|
58
|
+
get page() {
|
|
59
|
+
return this._page;
|
|
60
|
+
}
|
|
61
|
+
set page(page) {
|
|
62
|
+
// skip if page is the same
|
|
63
|
+
if (this._page === page) {
|
|
64
|
+
console.debug('[CE][PageController] skipping page set, same instance');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// unbind previous page subscription (if any)
|
|
68
|
+
if (this._pageSubscription) {
|
|
69
|
+
this._pageSubscription.unsubscribe();
|
|
70
|
+
delete this._pageSubscription;
|
|
71
|
+
}
|
|
72
|
+
console.debug('[CE][PageController] new page instance set');
|
|
73
|
+
// update page
|
|
74
|
+
this._page = page;
|
|
75
|
+
// subscribe to page changes
|
|
76
|
+
if (page) {
|
|
77
|
+
// subscribe to page events
|
|
78
|
+
this._pageSubscription = page.events$
|
|
79
|
+
.pipe((0, rxjs_1.tap)(e => console.debug('[CE][PageController] page event', e)), (0, rxjs_1.filter)(e => {
|
|
80
|
+
// handle page events
|
|
81
|
+
if (components_1.IPage.Event.filter(e)) {
|
|
82
|
+
return e.type === 'modified';
|
|
83
|
+
}
|
|
84
|
+
// or item/object/list events
|
|
85
|
+
else if (components_1.Item.Event.filter(e)
|
|
86
|
+
|| components_1.Item.Event.filter(e)
|
|
87
|
+
|| funnel_core_1.ObjectField.Event.filter(e)
|
|
88
|
+
|| funnel_core_1.FieldList.Event.filter(e)) {
|
|
89
|
+
return e.type === 'changed';
|
|
90
|
+
}
|
|
91
|
+
// or field
|
|
92
|
+
else {
|
|
93
|
+
return funnel_core_1.Field.Event.filter(e) && (e.type === 'changed' || e.type === 'reset');
|
|
94
|
+
}
|
|
95
|
+
}), (0, rxjs_1.debounce)(e => {
|
|
96
|
+
return (0, rxjs_1.interval)(1000);
|
|
97
|
+
}))
|
|
98
|
+
.subscribe(async (e) => {
|
|
99
|
+
if (page.modified && this._page === page) {
|
|
100
|
+
await this.save(page);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// raise state change
|
|
105
|
+
this.raiseEvent('page.changed');
|
|
106
|
+
}
|
|
107
|
+
async sync(updatedOn) {
|
|
108
|
+
// get current page
|
|
109
|
+
const page = this._page;
|
|
110
|
+
// bail if no page
|
|
111
|
+
if (!page) {
|
|
112
|
+
console.warn('[CE][PageController] tried to sync with no page');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// skip if page already in sync
|
|
116
|
+
if (page.updatedOn >= updatedOn) {
|
|
117
|
+
console.debug('[CE][PageController] skipping sync, page already in sync');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// skip if saving already
|
|
121
|
+
if (this.state === 'saving') {
|
|
122
|
+
console.debug('[CE][PageController] skipping sync while saving');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// start fresh load
|
|
126
|
+
console.debug('[CE][PageController] syncing, page out of date...');
|
|
127
|
+
await this.load();
|
|
128
|
+
}
|
|
129
|
+
async load() {
|
|
130
|
+
// mark as loading (or initialing if loading for first time)
|
|
131
|
+
this.state = this._state === 'uninitialized'
|
|
132
|
+
? 'initializing'
|
|
133
|
+
: 'loading';
|
|
134
|
+
// try to load page
|
|
135
|
+
try {
|
|
136
|
+
// load page
|
|
137
|
+
const page = await this.localRepository.load({ eventBus: this._eventBus });
|
|
138
|
+
// get page references
|
|
139
|
+
if (page) {
|
|
140
|
+
const references = await this.remoteRepository.getReferences(page.id);
|
|
141
|
+
page.hydrateStores(references);
|
|
142
|
+
}
|
|
143
|
+
// set page
|
|
144
|
+
// TODO: create real page summary
|
|
145
|
+
this.pageSummary = new components_1.PageSummary({ pages: [{ state: 'draft' }] });
|
|
146
|
+
this.page = page;
|
|
147
|
+
// update state
|
|
148
|
+
this.state = 'ready';
|
|
149
|
+
}
|
|
150
|
+
// handle error
|
|
151
|
+
catch (e) {
|
|
152
|
+
console.error('[CE][PageController] failed to load page', e);
|
|
153
|
+
// set error state
|
|
154
|
+
this.state = 'error';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async create(target) {
|
|
158
|
+
throw new funnel_core_1.NotImplementedError();
|
|
159
|
+
}
|
|
160
|
+
async save(page) {
|
|
161
|
+
// try to save page
|
|
162
|
+
try {
|
|
163
|
+
console.debug('[CE][PageController/save] starting save');
|
|
164
|
+
// update state
|
|
165
|
+
this.state = 'saving';
|
|
166
|
+
// save page
|
|
167
|
+
const updatedPage = await this.localRepository.save(page);
|
|
168
|
+
this.page = updatedPage;
|
|
169
|
+
// update state
|
|
170
|
+
this.state = 'ready';
|
|
171
|
+
console.debug('[CE][PageController/save] save finished');
|
|
172
|
+
}
|
|
173
|
+
// handle error
|
|
174
|
+
catch (e) {
|
|
175
|
+
console.error('[CE][PageController/save] save failed', e);
|
|
176
|
+
// update state
|
|
177
|
+
this.state = 'error';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async reload() {
|
|
181
|
+
throw new funnel_core_1.NotImplementedError();
|
|
182
|
+
}
|
|
183
|
+
async unpublish() {
|
|
184
|
+
throw new funnel_core_1.NotImplementedError();
|
|
185
|
+
}
|
|
186
|
+
raiseEvent(type) {
|
|
187
|
+
this._eventBus.publish(components_1.IPageController.Event.create({ type, controller: this }));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
exports.LocalPageController = LocalPageController;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { RemotePageRepository } from '../../core';
|
|
3
|
+
export declare function LocalPageControllerContainer({ children, remoteRepository }: LocalPageControllerContainer.Props): import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
export declare namespace LocalPageControllerContainer {
|
|
5
|
+
type Props = PropsWithChildren & {
|
|
6
|
+
remoteRepository: RemotePageRepository;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LocalPageControllerContainer = LocalPageControllerContainer;
|
|
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 LocalPageController_1 = require("./LocalPageController");
|
|
9
|
+
const LocalPageControllerContext_1 = require("./LocalPageControllerContext");
|
|
10
|
+
// container
|
|
11
|
+
function LocalPageControllerContainer({ 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][PageControllerContainer] creating page controller');
|
|
16
|
+
const localRepository = new core_1.LocalPageRepository(sdk);
|
|
17
|
+
// return context
|
|
18
|
+
return {
|
|
19
|
+
controller: new LocalPageController_1.LocalPageController({ localRepository, remoteRepository })
|
|
20
|
+
};
|
|
21
|
+
}, [sdk, remoteRepository]);
|
|
22
|
+
// listen to page 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)(LocalPageControllerContext_1.LocalPageControllerContext.Provider, { value: context, children: children }));
|
|
36
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LocalPageController } from './LocalPageController';
|
|
2
|
+
export declare const LocalPageControllerContext: import("react").Context<LocalPageControllerContext.Type>;
|
|
3
|
+
export declare namespace LocalPageControllerContext {
|
|
4
|
+
type Type = {
|
|
5
|
+
controller: LocalPageController;
|
|
6
|
+
};
|
|
7
|
+
}
|