From f791959d7f49bd19f59c8964a4f0da69233eb22c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Fri, 19 Jan 2024 15:20:21 +0000 Subject: [PATCH] feat: Wire up favicon form (#2680) * feat: Allow custom types in ImgInput * feat: Wire up favicon form * feat: Update favicon on preview and unpublished if present --- editor.planx.uk/public/index.html | 2 +- .../Settings/DesignSettings/FaviconForm.tsx | 55 ++++++++++++++----- .../Settings/DesignSettings/index.tsx | 4 +- .../src/pages/FlowEditor/lib/store/team.ts | 10 +++- editor.planx.uk/src/ui/editor/ImgInput.tsx | 5 +- .../src/ui/shared/PublicFileUploadButton.tsx | 15 +++-- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/editor.planx.uk/public/index.html b/editor.planx.uk/public/index.html index 9e84e83265..448f2a6f58 100644 --- a/editor.planx.uk/public/index.html +++ b/editor.planx.uk/public/index.html @@ -2,7 +2,7 @@ - + { - const formik = useFormik<{ - textLinkColor: string; - }>({ - initialValues: { - textLinkColor: EXAMPLE_COLOUR, +type FormValues = Pick; + +export const FaviconForm: React.FC<{ team: Team, onSuccess: () => void }> = ({ team, onSuccess }) => { + useEffect(() => { + setInitialValues({ favicon: team.theme?.favicon || "" }); + }, [team]); + + const [initialValues, setInitialValues] = useState({ + favicon: "", + }); + + const formik = useFormik({ + initialValues, + validateOnBlur: false, + validateOnChange: false, + enableReinitialize: true, + onSubmit: async (values, { resetForm }) => { + const isSuccess = await useStore.getState().updateTeamTheme(values); + if (isSuccess) { + onSuccess(); + // Reset "dirty" status to disable Save & Reset buttons + resetForm({ values }); + } }, - onSubmit: () => {}, - validate: () => {}, }); + const updateFavicon = (newFile: string | undefined) => newFile + ? formik.setFieldValue("favicon", newFile) + : formik.setFieldValue("favicon", null); + return ( { input={ Favicon: - - + + { setOpen(true)} /> setOpen(true)} /> setOpen(true)} /> - + setOpen(true)} /> + setTeam: (team) => { set({ teamId: team.id, teamTheme: team.theme, @@ -50,7 +50,13 @@ export const teamStore: StateCreator< teamSlug: team.slug, notifyPersonalisation: team.notifyPersonalisation, boundaryBBox: team.boundaryBBox, - }), + }); + + if (team.theme?.favicon) { + const favicon = document.getElementById("favicon") as HTMLLinkElement; + favicon.href = team.theme.favicon; + } + }, getTeam: () => ({ id: get().teamId, diff --git a/editor.planx.uk/src/ui/editor/ImgInput.tsx b/editor.planx.uk/src/ui/editor/ImgInput.tsx index a5401d8ead..702121f31c 100644 --- a/editor.planx.uk/src/ui/editor/ImgInput.tsx +++ b/editor.planx.uk/src/ui/editor/ImgInput.tsx @@ -8,7 +8,7 @@ import Tooltip from "@mui/material/Tooltip"; import { useStore } from "pages/FlowEditor/lib/store"; import React, { useMemo, useState } from "react"; -import PublicFileUploadButton from "../shared/PublicFileUploadButton"; +import PublicFileUploadButton, { AcceptedFileTypes } from "../shared/PublicFileUploadButton"; const ImageUploadContainer = styled(Box)(() => ({ height: 50, @@ -29,9 +29,11 @@ const StyledIconButton = styled(IconButton)(({ theme }) => ({ export default function ImgInput({ img, onChange, + acceptedFileTypes, }: { img?: string; onChange?: (newUrl?: string) => void; + acceptedFileTypes?: AcceptedFileTypes }): FCReturn { const [anchorEl, setAnchorEl] = useState(null); @@ -88,6 +90,7 @@ export default function ImgInput({ onChange && onChange(newUrl); }} disabled={!useStore.getState().canUserEditTeam(teamSlug)} + acceptedFileTypes={acceptedFileTypes} /> diff --git a/editor.planx.uk/src/ui/shared/PublicFileUploadButton.tsx b/editor.planx.uk/src/ui/shared/PublicFileUploadButton.tsx index 41cf283d4d..45cce3d4cb 100644 --- a/editor.planx.uk/src/ui/shared/PublicFileUploadButton.tsx +++ b/editor.planx.uk/src/ui/shared/PublicFileUploadButton.tsx @@ -8,10 +8,19 @@ import { uploadPublicFile } from "api/upload"; import React, { useCallback, useEffect, useState } from "react"; import { FileWithPath, useDropzone } from "react-dropzone"; +export type AcceptedFileTypes = Record; + +export type ImageFileExtensions = ".jpg" | ".jpeg" | ".png" | ".svg" | ".ico"; + +export const DEFAULT_FILETYPES: AcceptedFileTypes = { + "image/*": [".jpg", ".jpeg", ".png", ".svg"], +}; + export interface Props { onChange?: (image: string) => void; variant?: "tooltip"; disabled?: boolean; + acceptedFileTypes?: AcceptedFileTypes; } interface RootProps extends ButtonBaseProps { @@ -41,7 +50,7 @@ const Root = styled(ButtonBase, { })); export default function PublicFileUploadButton(props: Props): FCReturn { - const { onChange, variant, disabled } = props; + const { onChange, variant, disabled, acceptedFileTypes } = props; const [status, setStatus] = useState< { type: "none" } | { type: "loading" } | { type: "error"; msg: string } @@ -86,9 +95,7 @@ export default function PublicFileUploadButton(props: Props): FCReturn { const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, - accept: { - "image/*": [".jpg", ".jpeg", ".png", ".svg"], - }, + accept: acceptedFileTypes || DEFAULT_FILETYPES, }); if (status.type === "loading") {