From a0b15b67235568e407007b9da1c6f90482217178 Mon Sep 17 00:00:00 2001 From: Kudakwashe Mupeni Date: Tue, 14 Nov 2023 13:59:10 +0100 Subject: [PATCH] feat: remove merge function --- packages/live-preview-sdk/package.json | 1 + packages/live-preview-sdk/src/index.ts | 10 +- packages/live-preview-sdk/src/liveUpdates.ts | 168 +------------------ packages/live-preview-sdk/src/messages.ts | 5 +- packages/live-preview-sdk/src/saveEvent.ts | 2 +- yarn.lock | 5 + 6 files changed, 27 insertions(+), 164 deletions(-) diff --git a/packages/live-preview-sdk/package.json b/packages/live-preview-sdk/package.json index a29ea0bb..833794d0 100644 --- a/packages/live-preview-sdk/package.json +++ b/packages/live-preview-sdk/package.json @@ -62,6 +62,7 @@ "@vitejs/plugin-react-swc": "^3.0.0", "contentful": "^10.3.4", "contentful-management": "^11.0.1", + "flatted": "^3.2.9", "graphql": "^16.6.0", "graphql-tag": "^2.12.6", "jsdom": "^22.0.0", diff --git a/packages/live-preview-sdk/src/index.ts b/packages/live-preview-sdk/src/index.ts index fd1c7f31..18c1ee5a 100644 --- a/packages/live-preview-sdk/src/index.ts +++ b/packages/live-preview-sdk/src/index.ts @@ -5,11 +5,11 @@ import { type DocumentNode } from 'graphql'; import { version } from '../package.json'; import { getAllTaggedEntries } from './fieldTaggingUtils'; import { - sendMessageToEditor, - pollUrlChanges, - setDebugMode, debug, isInsideIframe, + pollUrlChanges, + sendMessageToEditor, + setDebugMode, } from './helpers'; import { isValidMessage } from './helpers/validateMessage'; import { InspectorMode } from './inspectorMode'; @@ -268,3 +268,7 @@ export class ContentfulLivePreview { export { LIVE_PREVIEW_EDITOR_SOURCE, LIVE_PREVIEW_SDK_SOURCE } from './constants'; export * from './messages'; + +export const newFunc = () => { + console.log('newFunc'); +}; diff --git a/packages/live-preview-sdk/src/liveUpdates.ts b/packages/live-preview-sdk/src/liveUpdates.ts index 2a66c21a..9198a8da 100644 --- a/packages/live-preview-sdk/src/liveUpdates.ts +++ b/packages/live-preview-sdk/src/liveUpdates.ts @@ -1,5 +1,6 @@ import type { Asset, Entry } from 'contentful'; +import { stringify } from 'flatted'; import type { ContentfulSubscribeConfig, EditorMessage, @@ -9,21 +10,18 @@ import type { PostMessageMethods, SubscribedMessage, } from '.'; -import * as gql from './graphql'; import { parseGraphQLParams } from './graphql/queryUtils'; -import { clone, generateUID, sendMessageToEditor, StorageMap, debug } from './helpers'; +import { StorageMap, debug, generateUID, sendMessageToEditor } from './helpers'; import { validateDataForLiveUpdates } from './helpers/validation'; import { LivePreviewPostMessageMethods } from './messages'; -import * as rest from './rest'; import { Argument, ContentType, Entity, - EntityWithSys, EntityReferenceMap, - hasSysInformation, - Subscription, GraphQLParams, + Subscription, + hasSysInformation, } from './types'; interface MergeEntityProps { @@ -56,170 +54,20 @@ export class LiveUpdates { window.addEventListener('beforeunload', () => this.clearStorage()); } - private async mergeEntity({ - contentType, - dataFromPreviewApp, - entityReferenceMap, - locale, - updateFromEntryEditor, - gqlParams, - }: Omit & { - dataFromPreviewApp: EntityWithSys; - }): Promise<{ - data: Entity; - updated: boolean; - }> { - if ('__typename' in dataFromPreviewApp) { - // GraphQL - const data = await (dataFromPreviewApp.__typename === 'Asset' - ? gql.updateAsset(dataFromPreviewApp, updateFromEntryEditor as Asset, gqlParams) - : gql.updateEntry({ - contentType, - dataFromPreviewApp, - updateFromEntryEditor: updateFromEntryEditor as Entry, - locale, - entityReferenceMap, - gqlParams, - sendMessage: this.sendMessage, - })); - - return { - data, - updated: true, - }; - } - - if (this.isCfEntity(dataFromPreviewApp)) { - // REST - const depth = 0; - const visitedReferenceMap = new Map(); - return { - data: await rest.updateEntity( - contentType, - dataFromPreviewApp as Entry, - updateFromEntryEditor as Entry, - locale, - entityReferenceMap, - depth, - visitedReferenceMap, - this.sendMessage - ), - updated: true, - }; - } - - return { updated: false, data: dataFromPreviewApp }; - } - - /** - * Merges the `dataFromPreviewApp` together with the `updateFromEntryEditor` - * If there is not direct match, it will try to merge things together recursively - * caches the result if cache is enabled and the entity has a `sys.id`. - * Caching should not be enabled for every entry, - * because nested references could be merged differently together and this could solve to data loss. - */ - private async mergeNestedReference( - { dataFromPreviewApp, ...params }: MergeEntityProps, - useCache: boolean - ): Promise<{ data: Entity; updated: boolean }> { - const dataFromPreviewappId = hasSysInformation(dataFromPreviewApp) && dataFromPreviewApp.sys.id; - const isCacheable = useCache && dataFromPreviewappId; - - // Flag to detect if something got updated and trigger only the subscription's if necessary - // TODO: This is still not perfect as it doesn't check if anything got updated, only if something could have been merged. - let updated = false; - // If the entity is cacheable and it was once proceeded we use this one as base - let result: Entity = - (isCacheable ? this.storage.get(dataFromPreviewappId, params.locale) : undefined) || - dataFromPreviewApp; - - if (hasSysInformation(result) && dataFromPreviewappId === params.updateFromEntryEditor.sys.id) { - // Happy path, direct match from received and provided data - // Let's update it - const merged = await this.mergeEntity({ ...params, dataFromPreviewApp: result }); - result = merged.data; - updated = merged.updated; - } else { - // No direct match, let's check if there is a nested reference and then update it - for (const key in result) { - if (result[key] && typeof result[key] === 'object') { - // TODO: set `useCache` to true if none of the parents could be cached - const match = await this.merge( - { ...params, dataFromPreviewApp: result[key] as Argument }, - false - ); - result[key] = match.data; - updated = updated || match.updated; - } - } - } - - if (isCacheable) { - // Cache the updated data for future updates - this.storage.set(dataFromPreviewappId, params.locale, result); - } - - return { data: result, updated }; - } - - private async merge( - { dataFromPreviewApp, ...params }: MergeArgumentProps, - useCache = true - ): Promise<{ - updated: boolean; - data: Argument; - }> { - if (Array.isArray(dataFromPreviewApp)) { - const data: Entity[] = []; - let updated = false; - - for (const d of dataFromPreviewApp) { - const result = await this.mergeNestedReference( - { ...params, dataFromPreviewApp: d }, - useCache - ); - - data.push(result.data); - updated = updated || result.updated; - } - - return { data, updated }; - } - - return this.mergeNestedReference({ ...params, dataFromPreviewApp }, useCache); - } - - private isCfEntity(entity: unknown): entity is Asset | Entry { - return hasSysInformation(entity) && 'fields' in entity; - } - /** Receives the data from the message event handler and calls the subscriptions */ public async receiveMessage(message: MessageFromEditor): Promise { if ( ('action' in message && message.action === 'ENTRY_UPDATED') || message.method === LivePreviewPostMessageMethods.ENTRY_UPDATED ) { - const { entity, contentType, entityReferenceMap } = message as EntryUpdatedMessage; + const { entity } = message as EntryUpdatedMessage; await Promise.all( [...this.subscriptions].map(async ([, s]) => { try { - const { updated, data } = await this.merge({ - // Clone the original data on the top level, - // to prevent cloning multiple times (time) - // or modifying the original data (failure potential) - dataFromPreviewApp: clone(s.data), - locale: s.locale || this.defaultLocale, - updateFromEntryEditor: entity, - contentType: contentType, - entityReferenceMap: entityReferenceMap, - gqlParams: s.gqlParams, - }); - // Only if there was an update, trigger the callback to unnecessary re-renders - if (updated) { - s.callback(data); - } + + s.callback(message.data); } catch (error) { this.sendErrorMessage({ message: (error as Error).message, @@ -321,6 +169,8 @@ export class LiveUpdates { locale, entryId: sysId, event: 'edit', + id, + config: stringify(config), } as SubscribedMessage); return () => { diff --git a/packages/live-preview-sdk/src/messages.ts b/packages/live-preview-sdk/src/messages.ts index 8bad2f64..be53ca3e 100644 --- a/packages/live-preview-sdk/src/messages.ts +++ b/packages/live-preview-sdk/src/messages.ts @@ -35,10 +35,10 @@ enum LivePreviewPostMessageMethods { } export { - StorePostMessageMethods, LivePreviewPostMessageMethods, RequestEntitiesMessage, RequestedEntitiesMessage, + StorePostMessageMethods, }; export type PostMessageMethods = LivePreviewPostMessageMethods | StorePostMessageMethods; @@ -83,6 +83,8 @@ export type SubscribedMessage = { entryId: string; locale: string; event: 'edit' | 'save'; + id: string; + config: string; }; export type ErrorMessage = { @@ -156,6 +158,7 @@ export type MessageFromEditor = ( | DebugModeEnabledMessage | RequestedEntitiesMessage ) & { + data: any; method: PostMessageMethods; /** @deprecated use source instead */ from: 'live-preview'; diff --git a/packages/live-preview-sdk/src/saveEvent.ts b/packages/live-preview-sdk/src/saveEvent.ts index b1413df9..fa4b6168 100644 --- a/packages/live-preview-sdk/src/saveEvent.ts +++ b/packages/live-preview-sdk/src/saveEvent.ts @@ -36,7 +36,7 @@ export class SaveEvent { public receiveMessage(message: Omit): void { if (message.method === LivePreviewPostMessageMethods.ENTRY_SAVED && this.subscription) { - const { entity } = message as EntrySavedMessage; + const { entity } = message as unknown as EntrySavedMessage; const entries = getAllTaggedEntries(); if (entries.includes(entity.sys.id)) { diff --git a/yarn.lock b/yarn.lock index ee8ae7d4..251cfda7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4278,6 +4278,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"