From e98c511d595f42a69f234ef8702b3a5d89d457cb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 06:54:07 +0800 Subject: [PATCH] Stage Release (#3054) Created by Github action --------- Co-authored-by: Stuart Runyan Co-authored-by: Nar -- <28705606+finnar-bin@users.noreply.github.com> --- cypress/e2e/content/meta.spec.js | 28 +++++ .../src/app/components/Editor/Editor.js | 1 + .../SocialMediaPreview/TwitterPreview.tsx | 42 ++++++- .../Meta/SocialMediaPreview/index.tsx | 1 + .../Meta/SocialMediaPreview/useImageURL.ts | 1 + .../src/app/views/ItemEdit/Meta/index.tsx | 16 ++- .../ItemEdit/Meta/settings/MetaImage.tsx | 1 + .../views/ItemEdit/Meta/settings/TCImage.tsx | 119 ++++++++++++++++++ 8 files changed, 201 insertions(+), 8 deletions(-) create mode 100644 src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/TCImage.tsx diff --git a/cypress/e2e/content/meta.spec.js b/cypress/e2e/content/meta.spec.js index 6a3f6720f5..93935aa27d 100644 --- a/cypress/e2e/content/meta.spec.js +++ b/cypress/e2e/content/meta.spec.js @@ -123,4 +123,32 @@ describe("Content Meta", () => { cy.contains("/page/otherpage/all-field-types/").should("exist"); }); + + it("Supports a dedicated Twitter title, description and image", () => { + cy.waitOn("/v1/content/models*", () => { + cy.waitOn("/v1/env/nav", () => { + cy.waitOn("/v1/search/items*", () => { + cy.visit("/content/6-b6cde1aa9f-wftv50/7-92ab81c5a8-bhvb0l/meta"); + }); + }); + }); + + const title = `Twitter title ${today}`; + const description = `Twitter description ${today}`; + + cy.getBySelector("TCTitle").find("input").type(`{selectAll}{del}${title}`); + cy.getBySelector("TCDescription") + .find("textarea") + .first() + .type(`{selectAll}{del}${description}`); + cy.getBySelector("SocialMediaPreviewTwitter").click(); + + cy.getBySelector("TwitterCardTitle").contains(title); + cy.getBySelector("TwitterCardDescription").contains(description); + cy.getBySelector("TwitterCardImage").should( + "have.attr", + "src", + "https://wave-trial.getbynder.com/m/45b0d3ba0b271504/original/kim-cruickshanks-176374.jpg" + ); + }); }); diff --git a/src/apps/content-editor/src/app/components/Editor/Editor.js b/src/apps/content-editor/src/app/components/Editor/Editor.js index defa5c92a5..1e5be2ef6a 100644 --- a/src/apps/content-editor/src/app/components/Editor/Editor.js +++ b/src/apps/content-editor/src/app/components/Editor/Editor.js @@ -76,6 +76,7 @@ export default memo(function Editor({ "og_description", "tc_title", "tc_description", + "tc_image", ].includes(field.name) ); } diff --git a/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/TwitterPreview.tsx b/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/TwitterPreview.tsx index ac34cac0e8..8457c463a3 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/TwitterPreview.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/TwitterPreview.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { Typography, Box, Stack } from "@mui/material"; import { ImageRounded } from "@mui/icons-material"; import { useLocation, useParams } from "react-router"; @@ -17,12 +17,38 @@ export const TwitterPreview = ({ imageURL }: TwitterPreviewProps) => { }>(); const domain = useDomain(); const location = useLocation(); + const [tcImageURL, setTcImageURL] = useState(null); const isCreateItemPage = location?.pathname?.split("/")?.pop() === "new"; const item = useSelector( (state: AppState) => state.content[isCreateItemPage ? `new:${modelZUID}` : itemZUID] ); + useEffect(() => { + if (!!item?.data?.tc_image) { + const tcImage = String(item?.data?.tc_image) + ?.split(",") + ?.filter((el: string) => el)?.[0]; + + if (tcImage.startsWith("3-")) { + setTcImageURL( + `${ + // @ts-ignore + CONFIG.SERVICE_MEDIA_RESOLVER + }/resolve/${ + item?.data?.tc_image + }/getimage/?w=${128}&h=${128}&type=fit` + ); + } else { + setTcImageURL(tcImage); + } + } else { + setTcImageURL( + !!imageURL ? `${imageURL}?width=128&height=128&fit=cover` : null + ); + } + }, [item?.data?.tc_image]); + return ( { border={1} borderColor="border" > - {!!imageURL ? ( + {!!tcImageURL ? ( theme.palette.grey[100], @@ -40,9 +67,16 @@ export const TwitterPreview = ({ imageURL }: TwitterPreviewProps) => { }} width={128} height={128} - src={`${imageURL}?width=128&height=128&fit=cover`} + src={tcImageURL} flexShrink={0} borderRadius="8px 0 0 8px" + onError={(evt: any) => { + if (!!imageURL) { + evt.currentTarget.src = `${imageURL}?width=128&height=128&fit=cover`; + } else { + setTcImageURL(null); + } + }} /> ) : ( { {domain.replace(/http:\/\/|https:\/\//gm, "")} { {item?.data?.tc_title || item?.web?.metaTitle || "Meta Title"} { iconPosition="start" label="Twitter (X)" value={SocialMediaTab.Twitter} + data-cy="SocialMediaPreviewTwitter" /> } diff --git a/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/useImageURL.ts b/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/useImageURL.ts index 72213c3193..456ad4ce41 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/useImageURL.ts +++ b/src/apps/content-editor/src/app/views/ItemEdit/Meta/SocialMediaPreview/useImageURL.ts @@ -33,6 +33,7 @@ export const useImageURL: () => string = () => { !field.deletedAt && field.datatype === "images" && field?.name !== "og_image" && + field?.name !== "tc_image" && !!item?.data?.[field.name] ) { if ( diff --git a/src/apps/content-editor/src/app/views/ItemEdit/Meta/index.tsx b/src/apps/content-editor/src/app/views/ItemEdit/Meta/index.tsx index 4cba984f71..72740c3e03 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/Meta/index.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/Meta/index.tsx @@ -33,10 +33,7 @@ import { import { AppState } from "../../../../../../../shell/store/types"; import { Error } from "../../../components/Editor/Field/FieldShell"; import { fetchGlobalItem } from "../../../../../../../shell/store/content"; -import { - ContentModelField, - Web, -} from "../../../../../../../shell/services/types"; +import { ContentModelField } from "../../../../../../../shell/services/types"; import { SocialMediaPreview } from "./SocialMediaPreview"; import { validateMetaDescription } from "./settings/util"; @@ -54,7 +51,7 @@ import { OGTitle } from "./settings/OGTitle"; import { OGDescription } from "./settings/OGDescription"; import { TCTitle } from "./settings/TCTitle"; import { TCDescription } from "./settings/TCDescription"; -import { FieldError } from "../../../components/Editor/FieldError"; +import { TCImage } from "./settings/TCImage"; const rotateAnimation = keyframes` 0% { @@ -99,6 +96,7 @@ export const DYNAMIC_META_FIELD_NAMES = [ "og_description", "tc_title", "tc_description", + "tc_image", ]; type Errors = Record; @@ -514,6 +512,14 @@ export const Meta = forwardRef( field={metaFields.tc_description} /> )} + {"tc_image" in metaFields && ( + + )} {model?.type !== "dataset" && web?.pathPart !== "zesty_home" && ( diff --git a/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/MetaImage.tsx b/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/MetaImage.tsx index 40f5ee4817..75013f70ff 100644 --- a/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/MetaImage.tsx +++ b/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/MetaImage.tsx @@ -70,6 +70,7 @@ export const MetaImage = ({ onChange }: MetaImageProps) => { !field.deletedAt && field.datatype === "images" && field?.name !== "og_image" && + field?.name !== "tc_image" && !!item?.data?.[field.name] ) { if ( diff --git a/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/TCImage.tsx b/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/TCImage.tsx new file mode 100644 index 0000000000..e5003b49aa --- /dev/null +++ b/src/apps/content-editor/src/app/views/ItemEdit/Meta/settings/TCImage.tsx @@ -0,0 +1,119 @@ +import { useState, useMemo } from "react"; +import { MemoryRouter } from "react-router"; +import { Dialog } from "@mui/material"; +import { IconButton } from "@zesty-io/material"; +import { Close } from "@mui/icons-material"; + +import { FieldShell } from "../../../../components/Editor/Field/FieldShell"; +import { FieldTypeMedia } from "../../../../components/FieldTypeMedia"; +import { MediaApp } from "../../../../../../../media/src/app"; +import { ContentModelField } from "../../../../../../../../shell/services/types"; +import { Error } from "../../../../components/Editor/Field/FieldShell"; +import { hasErrors } from "./util"; + +type TCImageProps = { + field: ContentModelField; + error: Error; + onChange: (value: string, name: string) => void; + value: string; +}; +export const TCImage = ({ field, error, onChange, value }: TCImageProps) => { + const [imageModal, setImageModal] = useState(null); + + const imagesArray = useMemo(() => { + if (!value) return []; + + return ((value as string) || "").split(",").filter((el: string) => el); + }, [value]); + + return ( + <> + + { + setImageModal({ + ...opts, + locked: Boolean( + field?.settings && + field?.settings.group_id && + field?.settings.group_id != "0" + ), + }); + }} + settings={{ + fileExtensions: [ + ".png", + ".jpg", + ".jpeg", + ".svg", + ".gif", + ".tif", + ".webp", + ], + fileExtensionsErrorMessage: + "Only files with the following extensions are allowed: .png, .jpg, .jpeg, .svg, .gif, .tif, .webp", + }} + name={field?.name} + onChange={onChange} + lockedToGroupId={ + field?.settings?.group_id && field?.settings?.group_id !== "0" + ? field?.settings.group_id + : null + } + /> + + {imageModal && ( + + setImageModal(null)} + > + setImageModal(null)} + > + + + { + imageModal.callback(images); + setImageModal(null); + }} + isReplace={imageModal.isReplace} + /> + + + )} + + ); +};