@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.
Files changed (85) hide show
  1. package/dist/Editor.d.ts +6 -1
  2. package/dist/Editor.js +17 -1
  3. package/dist/EditorApp.js +1 -1
  4. package/dist/EntryEditor.js +42 -1
  5. package/dist/EntrySidebar.js +18 -1
  6. package/dist/collection/controller/LocalCollectionController.d.ts +35 -0
  7. package/dist/collection/controller/LocalCollectionController.js +200 -0
  8. package/dist/collection/controller/LocalCollectionControllerContainer.d.ts +8 -0
  9. package/dist/collection/controller/LocalCollectionControllerContainer.js +36 -0
  10. package/dist/collection/controller/LocalCollectionControllerContext.d.ts +7 -0
  11. package/dist/collection/controller/LocalCollectionControllerContext.js +6 -0
  12. package/dist/collection/controller/index.d.ts +4 -0
  13. package/dist/collection/controller/index.js +20 -0
  14. package/dist/collection/controller/useLocalCollectionController.d.ts +2 -0
  15. package/dist/collection/controller/useLocalCollectionController.js +10 -0
  16. package/dist/collection/index.d.ts +2 -0
  17. package/dist/collection/index.js +18 -0
  18. package/dist/collection/screens/CollectionEditor.d.ts +1 -0
  19. package/dist/collection/screens/CollectionEditor.js +42 -0
  20. package/dist/collection/screens/index.d.ts +1 -0
  21. package/dist/{page/hooks → collection/screens}/index.js +1 -1
  22. package/dist/core/bridge/AppBridgeContainer.d.ts +9 -0
  23. package/dist/core/bridge/AppBridgeContainer.js +55 -0
  24. package/dist/core/bridge/AppBridgeContext.d.ts +8 -0
  25. package/dist/core/bridge/AppBridgeContext.js +11 -0
  26. package/dist/core/bridge/AppMessage.d.ts +19 -0
  27. package/dist/core/bridge/AppMessage.js +19 -0
  28. package/dist/core/bridge/index.d.ts +4 -0
  29. package/dist/core/bridge/index.js +20 -0
  30. package/dist/core/bridge/useAppBridge.d.ts +8 -0
  31. package/dist/core/bridge/useAppBridge.js +24 -0
  32. package/dist/core/contentful/Contentful.d.ts +33 -0
  33. package/dist/core/contentful/Contentful.js +67 -0
  34. package/dist/core/contentful/ContentfulAsset.d.ts +23 -0
  35. package/dist/core/contentful/ContentfulAsset.js +88 -0
  36. package/dist/core/contentful/ContentfulCollection.d.ts +29 -0
  37. package/dist/core/contentful/ContentfulCollection.js +97 -0
  38. package/dist/core/contentful/ContentfulPage.d.ts +31 -0
  39. package/dist/core/contentful/ContentfulPage.js +84 -0
  40. package/dist/core/contentful/ContentfulRefs.d.ts +16 -0
  41. package/dist/core/contentful/ContentfulRefs.js +152 -0
  42. package/dist/core/contentful/index.d.ts +5 -0
  43. package/dist/core/contentful/index.js +21 -0
  44. package/dist/core/domain/Collection.d.ts +99 -0
  45. package/dist/core/domain/Collection.js +311 -0
  46. package/dist/core/domain/Page.d.ts +58 -8
  47. package/dist/core/domain/Page.js +329 -16
  48. package/dist/core/domain/index.d.ts +1 -0
  49. package/dist/core/domain/index.js +1 -0
  50. package/dist/core/index.d.ts +2 -0
  51. package/dist/core/index.js +2 -0
  52. package/dist/core/repository/AssetRepository.d.ts +10 -0
  53. package/dist/core/repository/AssetRepository.js +33 -0
  54. package/dist/core/repository/CollectionSerializer.d.ts +9 -0
  55. package/dist/core/repository/CollectionSerializer.js +41 -0
  56. package/dist/core/repository/LocalCollectionRepository.d.ts +27 -0
  57. package/dist/core/repository/LocalCollectionRepository.js +143 -0
  58. package/dist/core/repository/LocalPageRepository.d.ts +16 -0
  59. package/dist/core/repository/LocalPageRepository.js +153 -0
  60. package/dist/core/repository/PageSerializer.d.ts +2 -2
  61. package/dist/core/repository/PageSerializer.js +16 -13
  62. package/dist/core/repository/RemoteCollectionRepository.d.ts +19 -0
  63. package/dist/core/repository/RemoteCollectionRepository.js +236 -0
  64. package/dist/core/repository/RemotePageRepository.d.ts +14 -0
  65. package/dist/core/repository/RemotePageRepository.js +113 -0
  66. package/dist/core/repository/index.d.ts +5 -1
  67. package/dist/core/repository/index.js +5 -1
  68. package/dist/page/{hooks/PageController.d.ts → controller/LocalPageController.d.ts} +11 -6
  69. package/dist/page/controller/LocalPageController.js +190 -0
  70. package/dist/page/controller/LocalPageControllerContainer.d.ts +8 -0
  71. package/dist/page/controller/LocalPageControllerContainer.js +36 -0
  72. package/dist/page/controller/LocalPageControllerContext.d.ts +7 -0
  73. package/dist/page/controller/LocalPageControllerContext.js +6 -0
  74. package/dist/page/controller/index.d.ts +3 -0
  75. package/dist/page/controller/index.js +19 -0
  76. package/dist/page/controller/useLocalPageController.d.ts +2 -0
  77. package/dist/page/controller/useLocalPageController.js +10 -0
  78. package/dist/page/index.d.ts +1 -0
  79. package/dist/page/index.js +1 -0
  80. package/dist/page/screens/PageEditor.js +9 -11
  81. package/package.json +5 -3
  82. package/dist/core/repository/PageRepository.d.ts +0 -12
  83. package/dist/core/repository/PageRepository.js +0 -23
  84. package/dist/page/hooks/PageController.js +0 -107
  85. package/dist/page/hooks/index.d.ts +0 -1
@@ -0,0 +1,152 @@
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.ContentfulRefs = void 0;
15
+ const funnel_core_1 = require("@nascentdigital/funnel-core");
16
+ const components_1 = require("@nascentdigital/funnel-editor/components");
17
+ const ContentfulAsset_1 = require("./ContentfulAsset");
18
+ const ContentfulCollection_1 = require("./ContentfulCollection");
19
+ const ContentfulPage_1 = require("./ContentfulPage");
20
+ // namespace
21
+ var ContentfulRefs;
22
+ (function (ContentfulRefs) {
23
+ function find(data) {
24
+ // prepare sets
25
+ const assets = new Set();
26
+ const pages = new Set();
27
+ const collections = new Set();
28
+ // extract actual refs
29
+ funnel_core_1.ObjectUtils.walk(data, ({ value }) => {
30
+ // track assets
31
+ if (funnel_core_1.Schema.Asset.filter(value)) {
32
+ // track file (if it's real)
33
+ const assetId = funnel_core_1.Schema.Asset.getId(value);
34
+ if (assetId) {
35
+ assets.add(assetId);
36
+ }
37
+ // don't recurse
38
+ return false;
39
+ }
40
+ // track page links
41
+ else if (funnel_core_1.Schema.Field.Data.InternalLink.filter(value)) {
42
+ // track page (only if the id's real)
43
+ const pageId = funnel_core_1.Schema.Field.Data.InternalLink.getId(value);
44
+ if (pageId && pageId !== funnel_core_1.Schema.Page.Id.draft) {
45
+ pages.add(pageId);
46
+ }
47
+ // don't recurse
48
+ return false;
49
+ }
50
+ // transform collection refs
51
+ else if (funnel_core_1.Schema.Field.Data.Collection.Ref.filter(value)) {
52
+ // track collection (only if the id's real)
53
+ const collectionId = funnel_core_1.Schema.Field.Data.Collection.Ref.getId(value);
54
+ if (collectionId && collectionId !== funnel_core_1.Schema.Collection.Id.draft) {
55
+ collections.add(collectionId);
56
+ }
57
+ // don't recurse
58
+ return false;
59
+ }
60
+ // transform page refs
61
+ else if (funnel_core_1.Schema.Field.Data.Page.filter(value)) {
62
+ // track page (only if the id's real)
63
+ const pageId = funnel_core_1.Schema.Field.Data.Page.getId(value);
64
+ if (pageId && pageId !== funnel_core_1.Schema.Page.Id.draft) {
65
+ pages.add(pageId);
66
+ }
67
+ // don't recurse
68
+ return false;
69
+ }
70
+ // otherwise recurse deeper
71
+ else {
72
+ return true;
73
+ }
74
+ });
75
+ // return refs
76
+ return {
77
+ assets,
78
+ pages,
79
+ collections
80
+ };
81
+ }
82
+ ContentfulRefs.find = find;
83
+ function serialize(refs, locale) {
84
+ var _a;
85
+ // create empty refs
86
+ const assets = [];
87
+ const pages = [];
88
+ const collections = [];
89
+ // loop over references and serialize
90
+ const { Entry: entries = [], Asset: files = [] } = (_a = refs.includes) !== null && _a !== void 0 ? _a : {};
91
+ for (const entry of entries) {
92
+ switch (entry.sys.contentType.sys.id) {
93
+ // asset
94
+ case ContentfulAsset_1.ContentfulAsset.contentTypeId: {
95
+ // try to find file
96
+ const fileId = entry.fields.media[locale].sys.id;
97
+ const file = files.find(file => file.sys.id === fileId);
98
+ if (!file) {
99
+ throw new funnel_core_1.NotFoundError(`cannot find Contentful media "${fileId}" for asset "${entry.sys.id}"`);
100
+ }
101
+ assets.push({
102
+ __root: 'asset',
103
+ type: 'asset',
104
+ value: {
105
+ id: entry.sys.id,
106
+ name: entry.fields.name[locale],
107
+ metadata: entry.fields.metadata[locale],
108
+ // TODO: add real fields in contentful
109
+ brand: 'funnel',
110
+ tags: [],
111
+ scope: 'global',
112
+ type: 'image/png',
113
+ url: 'https:' + file.fields.file[locale].url,
114
+ description: '',
115
+ size: 0
116
+ }
117
+ });
118
+ break;
119
+ }
120
+ // page
121
+ case ContentfulPage_1.ContentfulPage.contentTypeId: {
122
+ // convert to link and get template
123
+ const pageEntry = entry;
124
+ const _b = ContentfulPage_1.ContentfulPage.serializeRemoteLink(pageEntry, locale), { type, tags } = _b, data = __rest(_b, ["type", "tags"]);
125
+ const template = funnel_core_1.Schema.Site.getPage(type, data.region);
126
+ // push link
127
+ pages.push(Object.assign({ template, tags: components_1.TagSerializer.deserializeTags({
128
+ groups: template.tags,
129
+ tagsData: tags
130
+ }) }, data));
131
+ break;
132
+ }
133
+ // collection
134
+ case ContentfulCollection_1.ContentfulCollection.contentTypeId: {
135
+ // convert to link and get template
136
+ const collectionEntry = entry;
137
+ const data = ContentfulCollection_1.ContentfulCollection.serializeRemote(collectionEntry, locale);
138
+ // push data
139
+ collections.push(data);
140
+ break;
141
+ }
142
+ }
143
+ }
144
+ // return
145
+ return {
146
+ assets,
147
+ pages,
148
+ collections
149
+ };
150
+ }
151
+ ContentfulRefs.serialize = serialize;
152
+ })(ContentfulRefs || (exports.ContentfulRefs = ContentfulRefs = {}));
@@ -0,0 +1,5 @@
1
+ export * from './Contentful';
2
+ export * from './ContentfulAsset';
3
+ export * from './ContentfulCollection';
4
+ export * from './ContentfulPage';
5
+ export * from './ContentfulRefs';
@@ -0,0 +1,21 @@
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("./Contentful"), exports);
18
+ __exportStar(require("./ContentfulAsset"), exports);
19
+ __exportStar(require("./ContentfulCollection"), exports);
20
+ __exportStar(require("./ContentfulPage"), exports);
21
+ __exportStar(require("./ContentfulRefs"), exports);
@@ -0,0 +1,99 @@
1
+ import { AssetClient } from '@nascentdigital/funnel-cms';
2
+ import { EventBus, Field, FieldModel, Language, Schema } from '@nascentdigital/funnel-core';
3
+ import { Asset, CollectionGroup, ICollection, IPage } from '@nascentdigital/funnel-editor/components';
4
+ import { Observable } from 'rxjs';
5
+ export declare class Collection implements ICollection {
6
+ readonly template: Schema.Collection;
7
+ readonly eventBus: EventBus;
8
+ readonly id: Schema.Collection.Id;
9
+ readonly groupId: Schema.Collection.GroupId;
10
+ readonly brand?: string;
11
+ readonly language: Language;
12
+ readonly version: number;
13
+ readonly nameField: Field<Schema.TextField>;
14
+ readonly pageIds: Schema.Page.Id[];
15
+ private _initialTags;
16
+ private readonly _tags;
17
+ readonly fields: ReadonlyArray<FieldModel>;
18
+ private readonly _assets;
19
+ private readonly _collections;
20
+ private readonly _pages;
21
+ readonly createdOn: number;
22
+ private _updatedOn;
23
+ private _modifiedOn?;
24
+ readonly publishedOn?: number;
25
+ private readonly _subscription;
26
+ private _state;
27
+ private _initialErrors;
28
+ private _syncing;
29
+ private _savingOn?;
30
+ constructor(args: Collection.ConstructorArgs);
31
+ get events$(): Observable<ICollection.Events>;
32
+ get stable$(): Observable<boolean>;
33
+ get state(): Schema.Collection.State;
34
+ private set state(value);
35
+ get syncing(): boolean;
36
+ set syncing(syncing: boolean);
37
+ get type(): Schema.Collection.Type;
38
+ get modified(): boolean;
39
+ get changes(): ICollection.Changes;
40
+ get initialErrors(): boolean;
41
+ get errors(): ICollection.Errors;
42
+ get valid(): boolean;
43
+ get allFields(): FieldModel[];
44
+ get name(): string;
45
+ set name(name: string);
46
+ get published(): boolean;
47
+ get updatedOn(): number;
48
+ get assets(): ReadonlyArray<Asset>;
49
+ get collections(): ReadonlyArray<Schema.Collection.Data.AsLink>;
50
+ get tagGroups(): ReadonlyArray<Schema.TagGroup>;
51
+ get initialTags(): ReadonlySet<Schema.Tag<string, string>>;
52
+ get tags(): ReadonlySet<Schema.Tag>;
53
+ hydrateStores(references: ICollection.References): void;
54
+ getAssetScope(range: Schema.AssetScope.Range): Schema.AssetScope;
55
+ getSearchScope(): AssetClient.GetAssets.Scope;
56
+ getAsset(id: Asset.Id): Asset | undefined;
57
+ addAsset(asset: Asset): void;
58
+ removeAsset(assetId: Asset.Id): boolean;
59
+ getCollection(collectionId: Schema.Collection.Id): Schema.Collection.Data.AsLink | undefined;
60
+ addCollection(collection: Schema.Collection.Data.AsLink): void;
61
+ removeCollection(collectionId: Schema.Collection.Id): boolean;
62
+ getPageRef(pageId: Schema.Page.Id | undefined): IPage.Ref | undefined;
63
+ addPageRef(page: IPage.Ref): void;
64
+ removePageRef(pageId: Schema.Page.Id): boolean;
65
+ addTag(tag: Schema.Tag): void;
66
+ removeTag(tag: Schema.Tag): void;
67
+ onSaveStart(): void;
68
+ onSaveComplete(updatedOn: number): void;
69
+ private raiseEvent;
70
+ }
71
+ export declare namespace Collection {
72
+ type ConstructorArgs = {
73
+ template: Schema.Collection;
74
+ eventBus: EventBus;
75
+ id: Schema.Collection.Id;
76
+ groupId: Schema.Collection.GroupId;
77
+ brand?: string;
78
+ language: Language;
79
+ version: number;
80
+ name: string;
81
+ tags: Schema.Tag[];
82
+ fields: FieldModel[];
83
+ files?: Asset[];
84
+ collections?: Schema.Collection.Data.AsLink[];
85
+ pageIds?: Schema.Page.Id[];
86
+ state: Schema.Collection.State;
87
+ errors: boolean;
88
+ changeSetId?: Schema.ChangeSet.Id;
89
+ snapshotId?: Schema.CollectionSnapshot.Id;
90
+ snapshot?: Schema.CollectionSnapshot;
91
+ createdOn: number;
92
+ updatedOn: number;
93
+ publishedOn?: number;
94
+ };
95
+ type WithGroup = {
96
+ group: CollectionGroup;
97
+ collection?: Collection;
98
+ };
99
+ }
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Collection = 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 Collection {
9
+ constructor(args) {
10
+ var _a, _b, _c, _d;
11
+ // initialize instance variables
12
+ this.template = args.template;
13
+ this.eventBus = args.eventBus;
14
+ this.id = args.id;
15
+ this.groupId = args.groupId;
16
+ this.brand = args.brand;
17
+ this.language = args.language;
18
+ this.version = args.version;
19
+ this.nameField = new funnel_core_1.Field({
20
+ id: 'name',
21
+ name: 'Name',
22
+ schema: funnel_core_1.Schema.Field.text({
23
+ name: 'Name',
24
+ description: `This is the name used to identify this ${args.template.name}.`,
25
+ hint: 'Please provide a name',
26
+ semantics: 'short',
27
+ required: true
28
+ }),
29
+ eventBus: args.eventBus,
30
+ initialValue: funnel_core_1.Schema.TextField.Value.toData(args.name),
31
+ publishedValue: funnel_core_1.Schema.TextField.Value.coerceData((_a = args.snapshot) === null || _a === void 0 ? void 0 : _a.name),
32
+ required: true
33
+ });
34
+ this._initialTags = new Set(args.tags);
35
+ this.pageIds = (_b = args.pageIds) !== null && _b !== void 0 ? _b : [];
36
+ this._tags = new Set(args.tags);
37
+ this.fields = args.fields;
38
+ this._assets = (_c = args.files) !== null && _c !== void 0 ? _c : [];
39
+ this._collections = (_d = args.collections) !== null && _d !== void 0 ? _d : [];
40
+ this._pages = [];
41
+ this.createdOn = args.createdOn;
42
+ this._updatedOn = args.updatedOn;
43
+ this.publishedOn = args.publishedOn;
44
+ this._state = args.state;
45
+ this._initialErrors = args.errors;
46
+ this._syncing = false;
47
+ // listen to block/field changes
48
+ this._subscription = this.eventBus.events$
49
+ .pipe((0, rxjs_1.filter)(e => (funnel_core_1.Field.Event.filter(e) && e.type === 'changed' && !e.field.sandboxed)
50
+ || (funnel_core_1.FieldList.Event.filter(e) && e.type === 'changed')
51
+ || (funnel_core_1.ObjectField.Event.filter(e) && e.type === 'changed')
52
+ || (components_1.Item.Event.filter(e) && e.type === 'changed')))
53
+ .subscribe(() => this.raiseEvent('changed'));
54
+ }
55
+ get events$() {
56
+ return this.eventBus.events$;
57
+ }
58
+ get stable$() {
59
+ return this.eventBus.events$
60
+ .pipe((0, rxjs_1.map)(() => !this._syncing && !this.modified), (0, rxjs_1.distinctUntilChanged)());
61
+ }
62
+ get state() {
63
+ return this._state;
64
+ }
65
+ set state(state) {
66
+ // skip if unchanged
67
+ if (this._state === state) {
68
+ return;
69
+ }
70
+ // update state
71
+ this._state = state;
72
+ this._modifiedOn = Date.now();
73
+ // raise change
74
+ this.raiseEvent('changed');
75
+ }
76
+ get syncing() {
77
+ return this._syncing;
78
+ }
79
+ set syncing(syncing) {
80
+ this._syncing = syncing;
81
+ this.raiseEvent('changed');
82
+ }
83
+ get type() {
84
+ return this.template.type;
85
+ }
86
+ get modified() {
87
+ const { nameField, initialTags, _tags, fields, initialErrors } = this;
88
+ return nameField.modified
89
+ || initialTags.symmetricDifference(_tags).size > 0
90
+ || fields.some(field => field.modified)
91
+ || initialErrors !== (this.errors.fields.length > 0);
92
+ }
93
+ get changes() {
94
+ return {
95
+ collection: this,
96
+ fields: this.allFields.filter(field => field.changed)
97
+ };
98
+ }
99
+ get initialErrors() {
100
+ return this._initialErrors;
101
+ }
102
+ get errors() {
103
+ return {
104
+ collection: this,
105
+ fields: this.allFields.filter(field => !field.valid)
106
+ };
107
+ }
108
+ get valid() {
109
+ return this.errors.fields.length === 0;
110
+ }
111
+ get allFields() {
112
+ const { nameField, fields } = this;
113
+ return [nameField, ...fields];
114
+ }
115
+ get name() {
116
+ var _a;
117
+ return (_a = funnel_core_1.Schema.TextField.Value.fromData(this.nameField.value)) !== null && _a !== void 0 ? _a : '';
118
+ }
119
+ set name(name) {
120
+ this.nameField.value = funnel_core_1.Schema.TextField.Value.toData(name);
121
+ }
122
+ get published() {
123
+ return this.publishedOn !== undefined;
124
+ }
125
+ get updatedOn() {
126
+ return this._updatedOn;
127
+ }
128
+ get assets() {
129
+ return this._assets;
130
+ }
131
+ get collections() {
132
+ return this._collections;
133
+ }
134
+ get tagGroups() {
135
+ return this.template.tags;
136
+ }
137
+ get initialTags() {
138
+ return this._initialTags;
139
+ }
140
+ get tags() {
141
+ return this._tags;
142
+ }
143
+ hydrateStores(references) {
144
+ this._assets.push(...references.assets);
145
+ this._collections.push(...references.collections);
146
+ }
147
+ getAssetScope(range) {
148
+ switch (range) {
149
+ case 'global':
150
+ return funnel_core_1.Schema.AssetScope.global;
151
+ case 'type':
152
+ return new funnel_core_1.Schema.CollectionTypeAssetScope(this.type);
153
+ case 'instance':
154
+ return new funnel_core_1.Schema.CollectionInstanceAssetScope(this.groupId);
155
+ }
156
+ }
157
+ getSearchScope() {
158
+ return {
159
+ target: 'collection',
160
+ type: this.type,
161
+ groupId: this.groupId
162
+ };
163
+ }
164
+ getAsset(id) {
165
+ return this._assets.find(asset => asset.value.id === id);
166
+ }
167
+ addAsset(asset) {
168
+ // replace asset if it exists
169
+ const { _assets } = this;
170
+ const { id } = asset.value;
171
+ const assetIndex = _assets.findIndex(asset => asset.value.id === id);
172
+ if (assetIndex >= 0) {
173
+ _assets[assetIndex] = asset;
174
+ }
175
+ // or add asset
176
+ else {
177
+ _assets.push(asset);
178
+ }
179
+ // raise event
180
+ this.raiseEvent('changed');
181
+ }
182
+ removeAsset(assetId) {
183
+ // skip if asset isn't there
184
+ const { _assets } = this;
185
+ const assetIndex = _assets.findIndex(asset => asset.value.id === assetId);
186
+ if (assetIndex < 0) {
187
+ return false;
188
+ }
189
+ // remove asset
190
+ _assets.splice(assetIndex, 1);
191
+ // raise event
192
+ this.raiseEvent('changed');
193
+ // indicate success
194
+ return true;
195
+ }
196
+ getCollection(collectionId) {
197
+ return this._collections.find(collection => collection.id === collectionId);
198
+ }
199
+ addCollection(collection) {
200
+ // replace collection if it exists
201
+ const { _collections } = this;
202
+ const collectionId = collection.id;
203
+ const collectionIndex = _collections.findIndex(collection => collection.id === collectionId);
204
+ if (collectionIndex >= 0) {
205
+ _collections[collectionIndex] = collection;
206
+ }
207
+ // or add collection
208
+ else {
209
+ _collections.push(collection);
210
+ }
211
+ // raise event
212
+ this.raiseEvent('changed');
213
+ }
214
+ removeCollection(collectionId) {
215
+ // skip if collection isn't there
216
+ const { _collections } = this;
217
+ const collectionIndex = _collections.findIndex(collection => collection.id === collectionId);
218
+ if (collectionIndex < 0) {
219
+ return false;
220
+ }
221
+ // remove collection
222
+ _collections.splice(collectionIndex, 1);
223
+ // raise event
224
+ this.raiseEvent('changed');
225
+ // indicate success
226
+ return true;
227
+ }
228
+ getPageRef(pageId) {
229
+ return pageId && pageId !== funnel_core_1.Schema.Page.Id.draft
230
+ ? this._pages.find(ref => ref.id === pageId)
231
+ : undefined;
232
+ }
233
+ addPageRef(page) {
234
+ // throw if page is a draft
235
+ if (page.id === funnel_core_1.Schema.Page.Id.draft) {
236
+ throw new funnel_core_1.InvalidOperationError('Cannot add draft Page as reference');
237
+ }
238
+ // replace ref if it exists
239
+ const { _pages } = this;
240
+ const pageId = page.id;
241
+ const refIndex = _pages.findIndex(ref => ref.id === pageId);
242
+ if (refIndex >= 0) {
243
+ _pages[refIndex] = page;
244
+ }
245
+ // or add ref
246
+ else {
247
+ _pages.push(page);
248
+ }
249
+ // raise event
250
+ this.raiseEvent('changed');
251
+ }
252
+ removePageRef(pageId) {
253
+ // skip if file isn't there
254
+ const { _pages } = this;
255
+ const refIndex = _pages.findIndex(ref => ref.id === pageId);
256
+ if (refIndex < 0) {
257
+ return false;
258
+ }
259
+ // remove file
260
+ _pages.splice(refIndex, 1);
261
+ // raise event
262
+ this.raiseEvent('changed');
263
+ // indicate success
264
+ return true;
265
+ }
266
+ addTag(tag) {
267
+ // skip if page has tag
268
+ const { _tags } = this;
269
+ if (_tags.has(tag)) {
270
+ return;
271
+ }
272
+ // add tag
273
+ _tags.add(tag);
274
+ this._modifiedOn = Date.now();
275
+ // raise event
276
+ this.raiseEvent('modified');
277
+ }
278
+ removeTag(tag) {
279
+ // skip if tag isn't there
280
+ const { _tags } = this;
281
+ if (!_tags.has(tag)) {
282
+ return;
283
+ }
284
+ // add tag
285
+ _tags.delete(tag);
286
+ this._modifiedOn = Date.now();
287
+ // raise event
288
+ this.raiseEvent('modified');
289
+ }
290
+ onSaveStart() {
291
+ this._savingOn = Date.now();
292
+ }
293
+ onSaveComplete(updatedOn) {
294
+ // update fundamentals
295
+ this._updatedOn = updatedOn;
296
+ this._state = 'draft';
297
+ // reset to values to match save
298
+ const { _modifiedOn, _savingOn } = this;
299
+ if (_savingOn && (!_modifiedOn || _savingOn >= _modifiedOn)) {
300
+ // sync data
301
+ this._initialErrors = !this.valid;
302
+ this._initialTags = new Set(this._tags);
303
+ }
304
+ // clear saving flag
305
+ delete this._savingOn;
306
+ }
307
+ raiseEvent(type) {
308
+ this.eventBus.publish(components_1.ICollection.Event.create({ type, collection: this }));
309
+ }
310
+ }
311
+ exports.Collection = Collection;