diff --git a/space/app/layout.tsx b/space/app/layout.tsx index 86e51266f..b2bbd1227 100644 --- a/space/app/layout.tsx +++ b/space/app/layout.tsx @@ -6,6 +6,7 @@ import { InstanceProvider } from "@/lib/instance-provider"; import { StoreProvider } from "@/lib/store-provider"; // styles import "@/styles/globals.css"; +import { ToastProvider } from "@/lib/toast-provider"; export const metadata: Metadata = { title: "Plane Deploy | Make your Plane boards public with one-click", @@ -34,7 +35,9 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - {children} + + {children} + diff --git a/space/components/issues/peek-overview/comment/add-comment.tsx b/space/components/issues/peek-overview/comment/add-comment.tsx index 96366eadd..a1647c9c5 100644 --- a/space/components/issues/peek-overview/comment/add-comment.tsx +++ b/space/components/issues/peek-overview/comment/add-comment.tsx @@ -1,12 +1,14 @@ import React, { useRef } from "react"; import { observer } from "mobx-react-lite"; import { useForm, Controller } from "react-hook-form"; -// components +// editor import { EditorRefApi } from "@plane/lite-text-editor"; +// ui +import { TOAST_TYPE, setToast } from "@plane/ui"; +// editor components import { LiteTextEditor } from "@/components/editor/lite-text-editor"; // hooks import { useIssueDetails, useProject, useUser } from "@/hooks/store"; -import useToast from "@/hooks/use-toast"; // types import { Comment } from "@/types/issue"; @@ -39,8 +41,6 @@ export const AddComment: React.FC = observer((props) => { formState: { isSubmitting }, reset, } = useForm({ defaultValues }); - // toast alert - const { setToastAlert } = useToast(); const onSubmit = async (formData: Comment) => { if (!workspaceSlug || !projectId || !issueId || isSubmitting || !formData.comment_html) return; @@ -51,8 +51,8 @@ export const AddComment: React.FC = observer((props) => { editorRef.current?.clearEditor(); }) .catch(() => - setToastAlert({ - type: "error", + setToast({ + type: TOAST_TYPE.ERROR, title: "Error!", message: "Comment could not be posted. Please try again.", }) diff --git a/space/components/issues/peek-overview/header.tsx b/space/components/issues/peek-overview/header.tsx index 80e2f785f..0e9b93ab9 100644 --- a/space/components/issues/peek-overview/header.tsx +++ b/space/components/issues/peek-overview/header.tsx @@ -3,17 +3,15 @@ import { observer } from "mobx-react-lite"; import { MoveRight } from "lucide-react"; import { Listbox, Transition } from "@headlessui/react"; // ui +import { setToast, TOAST_TYPE } from "@plane/ui"; import { Icon } from "@/components/ui"; // helpers import { copyTextToClipboard } from "@/helpers/string.helper"; // hooks import { useIssueDetails } from "@/hooks/store"; import useClipboardWritePermission from "@/hooks/use-clipboard-write-permission"; -import useToast from "@/hooks/use-toast"; -// store -import { IPeekMode } from "@/store/issue-detail.store"; // types -import { IIssue } from "@/types/issue"; +import { IIssue, IPeekMode } from "@/types/issue"; type Props = { handleClose: () => void; @@ -44,14 +42,12 @@ export const PeekOverviewHeader: React.FC = observer((props) => { const { peekMode, setPeekMode } = useIssueDetails(); const isClipboardWriteAllowed = useClipboardWritePermission(); - const { setToastAlert } = useToast(); - const handleCopyLink = () => { const urlToCopy = window.location.href; copyTextToClipboard(urlToCopy).then(() => { - setToastAlert({ - type: "success", + setToast({ + type: TOAST_TYPE.INFO, title: "Link copied!", message: "Issue link copied to clipboard", }); diff --git a/space/components/issues/peek-overview/issue-properties.tsx b/space/components/issues/peek-overview/issue-properties.tsx index 32d39b540..08d22b312 100644 --- a/space/components/issues/peek-overview/issue-properties.tsx +++ b/space/components/issues/peek-overview/issue-properties.tsx @@ -1,17 +1,15 @@ -// hooks // ui -import { StateGroupIcon } from "@plane/ui"; +import { StateGroupIcon, TOAST_TYPE, setToast } from "@plane/ui"; // icons import { Icon } from "@/components/ui"; -// helpers +// constants import { issueGroupFilter, issuePriorityFilter } from "@/constants/issue"; +// helpers import { renderFullDate } from "@/helpers/date-time.helper"; import { copyTextToClipboard, addSpaceIfCamelCase } from "@/helpers/string.helper"; // types -import { IPeekMode } from "@/store/issue-detail.store"; -// constants -import useToast from "hooks/use-toast"; -import { IIssue } from "types/issue"; +import { IIssue, IPeekMode } from "@/types/issue"; +// components import { dueDateIconDetails } from "../board-views/block-due-date"; type Props = { @@ -20,8 +18,6 @@ type Props = { }; export const PeekOverviewIssueProperties: React.FC = ({ issueDetails, mode }) => { - const { setToastAlert } = useToast(); - const state = issueDetails.state_detail; const stateGroup = issueGroupFilter(state.group); @@ -33,8 +29,8 @@ export const PeekOverviewIssueProperties: React.FC = ({ issueDetails, mod const urlToCopy = window.location.href; copyTextToClipboard(urlToCopy).then(() => { - setToastAlert({ - type: "success", + setToast({ + type: TOAST_TYPE.INFO, title: "Link copied!", message: "Issue link copied to clipboard", }); diff --git a/space/components/ui/toast-alert.tsx b/space/components/ui/toast-alert.tsx deleted file mode 100644 index 7e7804d32..000000000 --- a/space/components/ui/toast-alert.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { AlertTriangle, CheckCircle, Info, X, XCircle } from "lucide-react"; -// hooks -import useToast from "hooks/use-toast"; -// icons - -const ToastAlerts = () => { - const { alerts, removeAlert } = useToast(); - - if (!alerts) return null; - - return ( -
- {alerts.map((alert) => ( -
-
- -
-
-
-
- {alert.type === "success" ? ( -
-
-

{alert.title}

- {alert.message &&

{alert.message}

} -
-
-
-
- ))} -
- ); -}; - -export default ToastAlerts; diff --git a/space/contexts/toast.context.tsx b/space/contexts/toast.context.tsx deleted file mode 100644 index 86608f02c..000000000 --- a/space/contexts/toast.context.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { createContext, useCallback, useReducer } from "react"; -// uuid -import { v4 as uuid } from "uuid"; -// components -import ToastAlert from "@/components/ui/toast-alert"; - -export const toastContext = createContext({} as ContextType); - -// types -type ToastAlert = { - id: string; - title: string; - message?: string; - type: "success" | "error" | "warning" | "info"; -}; - -type ReducerActionType = { - type: "SET_TOAST_ALERT" | "REMOVE_TOAST_ALERT"; - payload: ToastAlert; -}; - -type ContextType = { - alerts?: ToastAlert[]; - removeAlert: (id: string) => void; - setToastAlert: (data: { - title: string; - type?: "success" | "error" | "warning" | "info" | undefined; - message?: string | undefined; - }) => void; -}; - -type StateType = { - toastAlerts?: ToastAlert[]; -}; - -type ReducerFunctionType = (state: StateType, action: ReducerActionType) => StateType; - -export const initialState: StateType = { - toastAlerts: [], -}; - -export const reducer: ReducerFunctionType = (state, action) => { - const { type, payload } = action; - - switch (type) { - case "SET_TOAST_ALERT": - return { - ...state, - toastAlerts: [...(state.toastAlerts ?? []), payload], - }; - - case "REMOVE_TOAST_ALERT": - return { - ...state, - toastAlerts: state.toastAlerts?.filter((toastAlert) => toastAlert.id !== payload.id), - }; - - default: { - return state; - } - } -}; - -export const ToastContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [state, dispatch] = useReducer(reducer, initialState); - - const removeAlert = useCallback((id: string) => { - dispatch({ - type: "REMOVE_TOAST_ALERT", - payload: { id, title: "", message: "", type: "success" }, - }); - }, []); - - const setToastAlert = useCallback( - (data: { title: string; type?: "success" | "error" | "warning" | "info"; message?: string }) => { - const id = uuid(); - const { title, type, message } = data; - dispatch({ - type: "SET_TOAST_ALERT", - payload: { id, title, message, type: type ?? "success" }, - }); - - const timer = setTimeout(() => { - removeAlert(id); - clearTimeout(timer); - }, 3000); - }, - [removeAlert] - ); - - return ( - - - {children} - - ); -}; diff --git a/space/helpers/common.helper.ts b/space/helpers/common.helper.ts index b2101e3c4..52c61e4e2 100644 --- a/space/helpers/common.helper.ts +++ b/space/helpers/common.helper.ts @@ -15,3 +15,6 @@ export const GOD_MODE_URL = encodeURI(`${ADMIN_BASE_URL}${ADMIN_BASE_PATH}`); export const ASSET_PREFIX = SPACE_BASE_PATH; export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs)); + +export const resolveGeneralTheme = (resolvedTheme: string | undefined) => + resolvedTheme?.includes("light") ? "light" : resolvedTheme?.includes("dark") ? "dark" : "system"; diff --git a/space/hooks/use-toast.tsx b/space/hooks/use-toast.tsx deleted file mode 100644 index 13f598f88..000000000 --- a/space/hooks/use-toast.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useContext } from "react"; -import { toastContext } from "@/contexts/toast.context"; - -const useToast = () => { - const toastContextData = useContext(toastContext); - return toastContextData; -}; - -export default useToast; diff --git a/space/lib/toast-provider.tsx b/space/lib/toast-provider.tsx new file mode 100644 index 000000000..1083cb6af --- /dev/null +++ b/space/lib/toast-provider.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { ReactNode } from "react"; +import { useTheme } from "next-themes" +// ui +import { Toast } from "@plane/ui"; +// helpers +import { resolveGeneralTheme } from "@/helpers/common.helper"; + +export const ToastProvider = ({ children }: { children: ReactNode }) => { + // themes + const { resolvedTheme } = useTheme(); + + return ( + <> + + {children} + + ); +}; diff --git a/space/store/issue-detail.store.ts b/space/store/issue-detail.store.ts index b6734640b..03f611cc0 100644 --- a/space/store/issue-detail.store.ts +++ b/space/store/issue-detail.store.ts @@ -5,9 +5,7 @@ import IssueService from "@/services/issue.service"; // store types import { RootStore } from "@/store/root.store"; // types -import { IIssue, IVote } from "@/types/issue"; - -export type IPeekMode = "side" | "modal" | "full"; +import { IIssue, IPeekMode, IVote } from "@/types/issue"; export interface IIssueDetailStore { loader: boolean; diff --git a/space/types/issue.d.ts b/space/types/issue.d.ts index 9735bedc2..f2625fb76 100644 --- a/space/types/issue.d.ts +++ b/space/types/issue.d.ts @@ -66,6 +66,8 @@ export interface IIssue { votes: IVote[]; } +export type IPeekMode = "side" | "modal" | "full"; + export interface IIssueState { id: string; name: string;