diff --git a/core-web/libs/dotcms-models/src/lib/meta-tags-model.ts b/core-web/libs/dotcms-models/src/lib/meta-tags-model.ts index f8f22e3d438c..d0cf4e4d8ae7 100644 --- a/core-web/libs/dotcms-models/src/lib/meta-tags-model.ts +++ b/core-web/libs/dotcms-models/src/lib/meta-tags-model.ts @@ -132,11 +132,21 @@ export interface MetaTagsPreview { twitterImage?: string; } -export enum SEO_MEDIA_TYPES { - GOOGLE = 'Google', +export enum SOCIAL_MEDIA_TYPES { + FACEBOOK = 'Facebook', TWITTER = 'Twitter', - LINKEDIN = 'LinkedIn', - FACEBOOK = 'Facebook' + LINKEDIN = 'LinkedIn' +} + +export enum SEARCH_ENGINE_TYPES { + GOOGLE = 'Google' +} + +export enum SEO_MEDIA_TYPES { + FACEBOOK = SOCIAL_MEDIA_TYPES.FACEBOOK, + TWITTER = SOCIAL_MEDIA_TYPES.TWITTER, + LINKEDIN = SOCIAL_MEDIA_TYPES.LINKEDIN, + GOOGLE = SEARCH_ENGINE_TYPES.GOOGLE } export const SEO_TAGS = [ @@ -149,7 +159,7 @@ export const SEO_TAGS = [ SEO_OPTIONS.TWITTER_IMAGE ]; -export const socialMediaTiles: Record = { +export const socialMediaTiles: Record = { [SEO_MEDIA_TYPES.FACEBOOK]: { label: 'Facebook', value: SEO_MEDIA_TYPES.FACEBOOK, @@ -167,7 +177,10 @@ export const socialMediaTiles: Record = { value: SEO_MEDIA_TYPES.LINKEDIN, icon: 'pi pi-linkedin', description: 'seo.rules.media.preview.tile' - }, + } +}; + +export const searchEngineTile: Record = { [SEO_MEDIA_TYPES.GOOGLE]: { label: 'Google', value: SEO_MEDIA_TYPES.GOOGLE, @@ -176,6 +189,11 @@ export const socialMediaTiles: Record = { } }; +export const seoTiles: Record = { + ...socialMediaTiles, + ...searchEngineTile +}; + export interface SocialMediaOption { label: string; value: SEO_MEDIA_TYPES; diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-uve-device-selector/dot-uve-device-selector.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-uve-device-selector/dot-uve-device-selector.component.ts index ab31ad9339ae..cab89a837444 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-uve-device-selector/dot-uve-device-selector.component.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-uve-device-selector/dot-uve-device-selector.component.ts @@ -15,7 +15,7 @@ import { import { DotDevice, DotDeviceListItem, - SEO_MEDIA_TYPES, + searchEngineTile, socialMediaTiles } from '@dotcms/dotcms-models'; import { DotMessagePipe } from '@dotcms/ui'; @@ -68,29 +68,20 @@ export class DotUveDeviceSelectorComponent implements OnInit { const socialMediaMenu = { label: 'Social Media Tiles', - items: Object.values(socialMediaTiles) - .filter( - (item) => - item.value === SEO_MEDIA_TYPES.FACEBOOK || - item.value === SEO_MEDIA_TYPES.TWITTER || - item.value === SEO_MEDIA_TYPES.LINKEDIN - ) - .map((item) => ({ - label: item.label, - command: () => this.onSocialMediaSelect(item.value), - styleClass: this.$currentSocialMedia() === item.value ? 'active' : '' - })) + items: Object.values(socialMediaTiles).map((item) => ({ + label: item.label, + command: () => this.onSocialMediaSelect(item.value), + styleClass: this.$currentSocialMedia() === item.value ? 'active' : '' + })) }; const searchEngineMenu = { label: 'Search Engine', - items: Object.values(socialMediaTiles) - .filter((item) => item.value === SEO_MEDIA_TYPES.GOOGLE) - .map((item) => ({ - label: item.label, - command: () => this.onSocialMediaSelect(item.value), - styleClass: this.$currentSocialMedia() === item.value ? 'active' : '' - })) + items: Object.values(searchEngineTile).map((item) => ({ + label: item.label, + command: () => this.onSocialMediaSelect(item.value), + styleClass: this.$currentSocialMedia() === item.value ? 'active' : '' + })) }; const menu = []; diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html index 2dabee87fcf8..97c34ccc440f 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html @@ -1,5 +1,7 @@ @let ogTagsResults = ogTagsResults$; @let showSEOTool = $editorProps().seoResults && ogTagsResults; + +@let iframeDisplay = $editorProps().showEditorContent ? 'block' : 'none'; @if ($previewMode()) { @@ -19,9 +21,7 @@
{ - const { code, isTraditionalPage, enableInlineEdit, isClientReady } = - this.uveStore.$reloadEditorContent(); + readonly $handleReloadContentEffect = effect(() => { + // We should not depend on this `$reloadEditorContent` computed to `resetEditorProperties` or `resetDialog` + // This depends on the `code` with each the page renders code. This reset should be done in `widthLoad` signal feature but we can't do it yet + const { isTraditionalPage, isClientReady } = this.uveStore.$reloadEditorContent(); - this.uveStore.resetEditorProperties(); - this.dialog?.resetDialog(); - - if (!isTraditionalPage) { - if (isClientReady) { - // This should have another name. - return this.reloadIframeContent(); - } - - return; - } - - this.setIframeContent(code, enableInlineEdit); - - /** - * The status of isClientReady is changed outside of editor - * so we need to set it to true here to avoid the editor to be in a loading state - * This is only for traditional pages. For Headless, the isClientReady is set from the client application - */ - this.uveStore.setIsClientReady(true); + this.uveStore.resetEditorProperties(); + this.dialog?.resetDialog(); + if (isTraditionalPage || !isClientReady) { return; - }, - { - allowSignalWrites: true } - ); + + this.reloadIframeContent(); + }); readonly $handleIsDraggingEffect = effect(() => { const isDragging = this.uveStore.$editorIsInDraggingState(); @@ -464,9 +446,23 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy { * @memberof EditEmaEditorComponent */ onIframePageLoad() { + if (!this.uveStore.isTraditionalPage()) { + return; + } + + this.#insertPageContent(); + this.#setSeoData(); + if (this.uveStore.state() === EDITOR_STATE.INLINE_EDITING) { this.inlineEditingService.initEditor(); } + + /** + * The status of isClientReady is changed outside of editor + * so we need to set it to true here to avoid the editor to be in a loading state + * This is only for traditional pages. For Headless, the isClientReady is set from the client application + */ + this.uveStore.setIsClientReady(true); } /** @@ -675,50 +671,24 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy { * @param code - The code to be added to the iframe. * @memberof EditEmaEditorComponent */ - setIframeContent(code: string, enableInlineEdit = false): void { - // requestAnimationFrame(() => { - // const iframeElement = this.iframe?.nativeElement; - - // if (!iframeElement) { - // return; - // } - - // const doc = iframeElement.contentDocument; - // const newDoc = this.inyectCodeToVTL(code); - - // if (!doc) { - // return; - // } - - // doc.open(); - // doc.write(newDoc); - // doc.close(); + #insertPageContent(): void { + const iframeElement = this.iframe?.nativeElement; + const doc = iframeElement.contentDocument; - // this.uveStore.setOgTags(this.dotSeoMetaTagsUtilService.getMetaTags(doc)); - // this.ogTagsResults$ = this.dotSeoMetaTagsService.getMetaTagsResults(doc).pipe(take(1)); - // this.handleInlineScripts(enableInlineEdit); - // }) + const enableInlineEdit = this.uveStore.$enableInlineEdit(); + const pageRender = this.uveStore.$pageRender(); - const iframeElement = this.iframe?.nativeElement; + const newDoc = this.inyectCodeToVTL(pageRender); - if (!iframeElement) { + if (!doc) { return; } - iframeElement.addEventListener('load', () => { - const doc = iframeElement.contentDocument; - const newDoc = this.inyectCodeToVTL(code); + doc.open(); + doc.write(newDoc); + doc.close(); - if (!doc) { - return; - } - - doc.open(); - doc.write(newDoc); - doc.close(); - - this.handleInlineScripts(enableInlineEdit); - }); + this.handleInlineScripts(enableInlineEdit); } /** @@ -757,6 +727,7 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy { ...actionPayload, newContentletId: detail.data.identifier }); + if (!didInsert) { this.handleDuplicatedContentlet(); @@ -1487,4 +1458,14 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy { #goBackToCurrentLanguage(): void { this.uveStore.loadPageAsset({ language_id: '1' }); } + + #setSeoData() { + const iframeElement = this.iframe?.nativeElement; + const doc = iframeElement.contentDocument; + this.dotSeoMetaTagsService.getMetaTagsResults(doc).subscribe((results) => { + const ogTags = this.dotSeoMetaTagsUtilService.getMetaTags(doc); + this.uveStore.setOgTags(ogTags); + this.uveStore.setOGTagResults(results); + }); + } } diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts index 616acbce704f..7194f965abf0 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/models.ts @@ -49,9 +49,7 @@ export interface PageData { } export interface ReloadEditorContent { - code: string; isTraditionalPage: boolean; - enableInlineEdit: boolean; isClientReady: boolean; } diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts index 1204723710b0..51013513a4c8 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts @@ -51,7 +51,6 @@ const buildIframeURL = ({ pageURI, params, isTraditionalPage }) => { if (isTraditionalPage) { // Force iframe reload on every page load to avoid caching issues and window dirty state return `about:blank?t=${Date.now()}`; - // return ''; } const pageAPIQueryParams = createPageApiUrlWithQueryParams(pageURI, params); @@ -111,6 +110,12 @@ export function withEditor() { store.isEditState() && untracked(() => store.isEnterprise()) }; }), + $pageRender: computed(() => { + return store.pageAPIResponse()?.page?.rendered; + }), + $enableInlineEdit: computed(() => { + return store.isEditState() && untracked(() => store.isEnterprise()); + }), $editorIsInDraggingState: computed( () => store.state() === EDITOR_STATE.DRAGGING ), diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-device-selector-seo/dot-device-selector-seo.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-device-selector-seo/dot-device-selector-seo.component.ts index 6f0d3325ac1e..ef02f41c1781 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-device-selector-seo/dot-device-selector-seo.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-device-selector-seo/dot-device-selector-seo.component.ts @@ -27,7 +27,6 @@ import { DotCurrentUser, DotDevice, DotDeviceListItem, - SEO_MEDIA_TYPES, SocialMediaOption, socialMediaTiles } from '@dotcms/dotcms-models'; @@ -139,12 +138,7 @@ export class DotDeviceSelectorSeoComponent implements OnInit { ngOnInit() { this.options$ = this.getOptions(); this.isCMSAdmin$ = this.checkIfCMSAdmin(); - this.socialMediaTiles = Object.values(socialMediaTiles).filter( - (item) => - item.value === SEO_MEDIA_TYPES.FACEBOOK || - item.value === SEO_MEDIA_TYPES.TWITTER || - item.value === SEO_MEDIA_TYPES.LINKEDIN - ); + this.socialMediaTiles = Object.values(socialMediaTiles); } /** diff --git a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-select-seo-tool/dot-select-seo-tool.component.ts b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-select-seo-tool/dot-select-seo-tool.component.ts index ad13ad8e3aca..871a524fc6b1 100644 --- a/core-web/libs/portlets/edit-ema/ui/src/lib/dot-select-seo-tool/dot-select-seo-tool.component.ts +++ b/core-web/libs/portlets/edit-ema/ui/src/lib/dot-select-seo-tool/dot-select-seo-tool.component.ts @@ -1,7 +1,7 @@ import { DecimalPipe, NgClass, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; -import { DotDeviceListItem, socialMediaTiles } from '@dotcms/dotcms-models'; +import { DotDeviceListItem, seoTiles } from '@dotcms/dotcms-models'; import { DotMessagePipe } from '@dotcms/ui'; @Component({ @@ -16,7 +16,7 @@ export class DotSelectSeoToolComponent implements OnChanges { @Input() socialMedia: string; @Input() device: DotDeviceListItem; socialMediaIconClass: string; - socialMediaTiles = socialMediaTiles; + socialMediaTiles = seoTiles; ngOnChanges() { this.socialMediaIconClass = `pi pi-${this.socialMedia?.toLowerCase()}`;