[WEB-1116] fix: page outline not reflecting changes in realtime (#5567)

* fix: svg not supported in image uploads

* fix: svg image file error message fixed

* fix: heading not updating with realtime

* chore: add read-only editor support

* fix: headings show on initial render

* fix: types and imports

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
This commit is contained in:
M. Palanikannan 2024-09-23 14:44:27 +05:30 committed by GitHub
parent b6e813cb9a
commit a05876552c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 145 additions and 57 deletions

View file

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useMemo } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// document-editor
@ -38,14 +38,13 @@ const fileService = new FileService();
type Props = {
editorRef: React.RefObject<EditorRefApi>;
editorReady: boolean;
handleConnectionStatus: (status: boolean) => void;
handleEditorReady: (value: boolean) => void;
handleReadOnlyEditorReady: (value: boolean) => void;
markings: IMarking[];
page: IPage;
readOnlyEditorRef: React.RefObject<EditorReadOnlyRefApi>;
sidePeekVisible: boolean;
updateMarkings: (description_html: string) => void;
};
export const PageEditorBody: React.FC<Props> = observer((props) => {
@ -54,11 +53,9 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
handleConnectionStatus,
handleEditorReady,
handleReadOnlyEditorReady,
markings,
page,
readOnlyEditorRef,
sidePeekVisible,
updateMarkings,
} = props;
// router
const { workspaceSlug, projectId } = useParams();
@ -70,10 +67,9 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
project: { getProjectMemberIds },
} = useMember();
// derived values
const workspaceId = workspaceSlug ? (getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "") : "";
const workspaceId = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "" : "";
const pageId = page?.id;
const pageTitle = page?.name ?? "";
const pageDescription = page?.description_html;
const { isContentEditable, updateTitle, setIsSubmitting } = page;
const projectMemberIds = projectId ? getProjectMemberIds(projectId.toString()) : [];
const projectMemberDetails = projectMemberIds?.map((id) => getUserDetails(id) as IUserLite);
@ -104,6 +100,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
const handleServerConnect = useCallback(() => {
handleConnectionStatus(false);
}, []);
const handleServerError = useCallback(() => {
handleConnectionStatus(true);
}, []);
@ -116,10 +113,6 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
[]
);
useEffect(() => {
updateMarkings(pageDescription ?? "<p></p>");
}, [pageDescription, updateMarkings]);
const realtimeConfig: TRealtimeConfig = useMemo(
() => ({
url: `${LIVE_URL}/collaboration`,
@ -144,10 +137,7 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
})}
>
{!isFullWidth && (
<PageContentBrowser
editorRef={(isContentEditable ? editorRef : readOnlyEditorRef)?.current}
markings={markings}
/>
<PageContentBrowser editorRef={(isContentEditable ? editorRef : readOnlyEditorRef)?.current} />
)}
</Row>
<div

View file

@ -13,7 +13,6 @@ type Props = {
editorRef: React.RefObject<EditorRefApi>;
handleDuplicatePage: () => void;
hasConnectionFailed: boolean;
markings: IMarking[];
page: IPage;
readOnlyEditorReady: boolean;
readOnlyEditorRef: React.RefObject<EditorReadOnlyRefApi>;
@ -27,7 +26,6 @@ export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
editorRef,
handleDuplicatePage,
hasConnectionFailed,
markings,
page,
readOnlyEditorReady,
readOnlyEditorRef,
@ -48,7 +46,6 @@ export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
<PageSummaryPopover
editorRef={isContentEditable ? editorRef.current : readOnlyEditorRef.current}
isFullWidth={isFullWidth}
markings={markings}
sidePeekVisible={sidePeekVisible}
setSidePeekVisible={setSidePeekVisible}
/>

View file

@ -1,5 +1,5 @@
import { observer } from "mobx-react";
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/editor";
import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
// components
import { Header, EHeaderVariant } from "@plane/ui";
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
@ -15,7 +15,6 @@ type Props = {
editorRef: React.RefObject<EditorRefApi>;
handleDuplicatePage: () => void;
hasConnectionFailed: boolean;
markings: IMarking[];
page: IPage;
readOnlyEditorReady: boolean;
readOnlyEditorRef: React.RefObject<EditorReadOnlyRefApi>;
@ -29,7 +28,6 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
editorRef,
handleDuplicatePage,
hasConnectionFailed,
markings,
page,
readOnlyEditorReady,
readOnlyEditorRef,
@ -47,20 +45,21 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
<>
<Header variant={EHeaderVariant.SECONDARY} showOnMobile={false}>
<Header.LeftItem className="gap-0 w-full">
<div
className={cn("flex-shrink-0 my-auto", {
"w-40 lg:w-56": !isFullWidth,
"w-[5%]": isFullWidth,
})}
>
<PageSummaryPopover
editorRef={isContentEditable ? editorRef.current : readOnlyEditorRef.current}
isFullWidth={isFullWidth}
markings={markings}
sidePeekVisible={sidePeekVisible}
setSidePeekVisible={setSidePeekVisible}
/>
</div>
{(editorReady || readOnlyEditorReady) && (
<div
className={cn("flex-shrink-0 my-auto", {
"w-40 lg:w-56": !isFullWidth,
"w-[5%]": isFullWidth,
})}
>
<PageSummaryPopover
editorRef={isContentEditable ? editorRef.current : readOnlyEditorRef.current}
isFullWidth={isFullWidth}
sidePeekVisible={sidePeekVisible}
setSidePeekVisible={setSidePeekVisible}
/>
</div>
)}
{(editorReady || readOnlyEditorReady) && isContentEditable && editorRef.current && (
<PageToolbar editorRef={editorRef?.current} />
)}
@ -79,7 +78,6 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
readOnlyEditorRef={readOnlyEditorRef}
editorReady={editorReady}
readOnlyEditorReady={readOnlyEditorReady}
markings={markings}
handleDuplicatePage={handleDuplicatePage}
hasConnectionFailed={hasConnectionFailed}
page={page}

View file

@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
import { observer } from "mobx-react";
import { useSearchParams } from "next/navigation";
// editor
import { EditorReadOnlyRefApi, EditorRefApi, useEditorMarkings } from "@plane/editor";
import { EditorRefApi } from "@plane/editor";
// types
import { TPage } from "@plane/types";
// ui
@ -44,8 +44,7 @@ export const PageRoot = observer((props: TPageRootProps) => {
const { createPage } = useProjectPages();
// derived values
const { access, description_html, name, isContentEditable } = page;
// editor markings hook
const { markings, updateMarkings } = useEditorMarkings();
// update query params
const { updateQueryParams } = useQueryParams();
@ -127,7 +126,6 @@ export const PageRoot = observer((props: TPageRootProps) => {
editorRef={editorRef}
handleDuplicatePage={handleDuplicatePage}
hasConnectionFailed={hasConnectionFailed}
markings={markings}
page={page}
readOnlyEditorReady={readOnlyEditorReady}
readOnlyEditorRef={readOnlyEditorRef}
@ -135,15 +133,14 @@ export const PageRoot = observer((props: TPageRootProps) => {
sidePeekVisible={sidePeekVisible}
/>
<PageEditorBody
editorReady={editorReady}
editorRef={editorRef}
handleConnectionStatus={(status) => setHasConnectionFailed(status)}
handleEditorReady={(val) => setEditorReady(val)}
handleReadOnlyEditorReady={() => setReadOnlyEditorReady(true)}
markings={markings}
page={page}
readOnlyEditorRef={readOnlyEditorRef}
sidePeekVisible={sidePeekVisible}
updateMarkings={updateMarkings}
/>
</>
);

View file

@ -1,15 +1,27 @@
// types
import { useState, useEffect } from "react";
// plane editor
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/editor";
// components
import { OutlineHeading1, OutlineHeading2, OutlineHeading3 } from "./heading-components";
type Props = {
editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
markings: IMarking[];
setSidePeekVisible?: (sidePeekState: boolean) => void;
};
export const PageContentBrowser: React.FC<Props> = (props) => {
const { editorRef, markings, setSidePeekVisible } = props;
const { editorRef, setSidePeekVisible } = props;
// states
const [headings, setHeadings] = useState<IMarking[]>([]);
useEffect(() => {
const unsubscribe = editorRef?.onHeadingChange(setHeadings);
// for initial render of this component to get the editor headings
setHeadings(editorRef?.getHeadings() ?? []);
return () => {
if (unsubscribe) unsubscribe();
};
}, [editorRef]);
const handleOnClick = (marking: IMarking) => {
editorRef?.scrollSummary(marking);
@ -27,8 +39,8 @@ export const PageContentBrowser: React.FC<Props> = (props) => {
return (
<div className="h-full flex flex-col overflow-hidden">
<div className="h-full flex flex-col items-start gap-y-2 overflow-y-auto mt-2">
{markings.length !== 0 ? (
markings.map((marking) => {
{headings && headings.length !== 0 ? (
headings.map((marking) => {
const Component = HeadingComponent[marking.level];
if (!Component) return null;
return (

View file

@ -2,7 +2,7 @@ import { useState } from "react";
import { usePopper } from "react-popper";
import { List } from "lucide-react";
// document editor
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/editor";
import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/editor";
// helpers
import { cn } from "@/helpers/common.helper";
// components
@ -11,13 +11,12 @@ import { PageContentBrowser } from "./content-browser";
type Props = {
editorRef: EditorRefApi | EditorReadOnlyRefApi | null;
isFullWidth: boolean;
markings: IMarking[];
sidePeekVisible: boolean;
setSidePeekVisible: (sidePeekState: boolean) => void;
};
export const PageSummaryPopover: React.FC<Props> = (props) => {
const { editorRef, markings, sidePeekVisible, setSidePeekVisible } = props;
const { editorRef, sidePeekVisible, setSidePeekVisible } = props;
// refs
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
@ -54,7 +53,7 @@ export const PageSummaryPopover: React.FC<Props> = (props) => {
style={summaryPopoverStyles.popper}
{...summaryPopoverAttributes.popper}
>
<PageContentBrowser setSidePeekVisible={setSidePeekVisible} editorRef={editorRef} markings={markings} />
<PageContentBrowser setSidePeekVisible={setSidePeekVisible} editorRef={editorRef} />
</div>
)}
</div>
@ -66,7 +65,7 @@ export const PageSummaryPopover: React.FC<Props> = (props) => {
style={summaryPopoverStyles.popper}
{...summaryPopoverAttributes.popper}
>
<PageContentBrowser editorRef={editorRef} markings={markings} />
<PageContentBrowser editorRef={editorRef} />
</div>
)}
</div>