mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-1135] chore: store page full width information in local storage (#4327)
* chore: store page full width information in local storage * chore: update page types
This commit is contained in:
parent
73fd6e641c
commit
eb0877a3c8
5
packages/types/src/pages.d.ts
vendored
5
packages/types/src/pages.d.ts
vendored
@ -16,14 +16,9 @@ export type TPage = {
|
|||||||
project: string | undefined;
|
project: string | undefined;
|
||||||
updated_at: Date | undefined;
|
updated_at: Date | undefined;
|
||||||
updated_by: string | undefined;
|
updated_by: string | undefined;
|
||||||
view_props: TPageViewProps | undefined;
|
|
||||||
workspace: string | undefined;
|
workspace: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TPageViewProps = {
|
|
||||||
full_width?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
// page filters
|
// page filters
|
||||||
export type TPageNavigationTabs = "public" | "private" | "archived";
|
export type TPageNavigationTabs = "public" | "private" | "archived";
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import { PageContentBrowser, PageEditorTitle } from "@/components/pages";
|
|||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMember, useMention, useUser, useWorkspace } from "@/hooks/store";
|
import { useMember, useMention, useUser, useWorkspace } from "@/hooks/store";
|
||||||
|
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||||
import useReloadConfirmations from "@/hooks/use-reload-confirmation";
|
import useReloadConfirmations from "@/hooks/use-reload-confirmation";
|
||||||
// services
|
// services
|
||||||
import { FileService } from "@/services/file.service";
|
import { FileService } from "@/services/file.service";
|
||||||
@ -68,7 +69,6 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||||||
const workspaceId = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "" : "";
|
const workspaceId = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString())?.id ?? "" : "";
|
||||||
const pageTitle = pageStore?.name ?? "";
|
const pageTitle = pageStore?.name ?? "";
|
||||||
const pageDescription = pageStore?.description_html ?? "<p></p>";
|
const pageDescription = pageStore?.description_html ?? "<p></p>";
|
||||||
const isFullWidth = !!pageStore?.view_props?.full_width;
|
|
||||||
const { description_html, isContentEditable, updateTitle, isSubmitting, setIsSubmitting } = pageStore;
|
const { description_html, isContentEditable, updateTitle, isSubmitting, setIsSubmitting } = pageStore;
|
||||||
const projectMemberIds = projectId ? getProjectMemberIds(projectId.toString()) : [];
|
const projectMemberIds = projectId ? getProjectMemberIds(projectId.toString()) : [];
|
||||||
const projectMemberDetails = projectMemberIds?.map((id) => getUserDetails(id) as IUserLite);
|
const projectMemberDetails = projectMemberIds?.map((id) => getUserDetails(id) as IUserLite);
|
||||||
@ -79,6 +79,8 @@ export const PageEditorBody: React.FC<Props> = observer((props) => {
|
|||||||
members: projectMemberDetails,
|
members: projectMemberDetails,
|
||||||
user: currentUser ?? undefined,
|
user: currentUser ?? undefined,
|
||||||
});
|
});
|
||||||
|
// page filters
|
||||||
|
const { isFullWidth } = usePageFilters();
|
||||||
|
|
||||||
const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting");
|
const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting");
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ import { observer } from "mobx-react";
|
|||||||
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/document-editor";
|
import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/document-editor";
|
||||||
// components
|
// components
|
||||||
import { PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
import { PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
||||||
|
// hooks
|
||||||
|
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||||
// store
|
// store
|
||||||
import { IPageStore } from "@/store/pages/page.store";
|
import { IPageStore } from "@/store/pages/page.store";
|
||||||
|
|
||||||
@ -34,8 +36,9 @@ export const PageEditorMobileHeaderRoot: React.FC<Props> = observer((props) => {
|
|||||||
setSidePeekVisible,
|
setSidePeekVisible,
|
||||||
} = props;
|
} = props;
|
||||||
// derived values
|
// derived values
|
||||||
const { isContentEditable, view_props } = pageStore;
|
const { isContentEditable } = pageStore;
|
||||||
const isFullWidth = !!view_props?.full_width;
|
// page filters
|
||||||
|
const { isFullWidth } = usePageFilters();
|
||||||
|
|
||||||
if (!editorRef.current && !readOnlyEditorRef.current) return null;
|
if (!editorRef.current && !readOnlyEditorRef.current) return null;
|
||||||
|
|
||||||
|
@ -4,13 +4,11 @@ import { ArchiveRestoreIcon, Clipboard, Copy, Link, Lock, LockOpen } from "lucid
|
|||||||
import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/document-editor";
|
import { EditorReadOnlyRefApi, EditorRefApi } from "@plane/document-editor";
|
||||||
// ui
|
// ui
|
||||||
import { ArchiveIcon, CustomMenu, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui";
|
import { ArchiveIcon, CustomMenu, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui";
|
||||||
// constants
|
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
|
||||||
import { copyTextToClipboard, copyUrlToClipboard } from "@/helpers/string.helper";
|
import { copyTextToClipboard, copyUrlToClipboard } from "@/helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useApplication, useUser } from "@/hooks/store";
|
import { useApplication } from "@/hooks/store";
|
||||||
|
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||||
// store
|
// store
|
||||||
import { IPageStore } from "@/store/pages/page.store";
|
import { IPageStore } from "@/store/pages/page.store";
|
||||||
|
|
||||||
@ -34,18 +32,13 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
|
|||||||
canCurrentUserDuplicatePage,
|
canCurrentUserDuplicatePage,
|
||||||
canCurrentUserLockPage,
|
canCurrentUserLockPage,
|
||||||
restore,
|
restore,
|
||||||
view_props,
|
|
||||||
updateViewProps,
|
|
||||||
} = pageStore;
|
} = pageStore;
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
router: { workspaceSlug, projectId },
|
router: { workspaceSlug, projectId },
|
||||||
} = useApplication();
|
} = useApplication();
|
||||||
const {
|
// page filters
|
||||||
membership: { currentProjectRole },
|
const { isFullWidth, handleFullWidth } = usePageFilters();
|
||||||
} = useUser();
|
|
||||||
// auth
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
|
||||||
|
|
||||||
const handleArchivePage = async () =>
|
const handleArchivePage = async () =>
|
||||||
await archive().catch(() =>
|
await archive().catch(() =>
|
||||||
@ -149,22 +142,10 @@ export const PageOptionsDropdown: React.FC<Props> = observer((props) => {
|
|||||||
<CustomMenu maxHeight="md" placement="bottom-start" verticalEllipsis closeOnSelect>
|
<CustomMenu maxHeight="md" placement="bottom-start" verticalEllipsis closeOnSelect>
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
className="hidden md:flex w-full items-center justify-between gap-2"
|
className="hidden md:flex w-full items-center justify-between gap-2"
|
||||||
onClick={() =>
|
onClick={() => handleFullWidth(!isFullWidth)}
|
||||||
updateViewProps({
|
|
||||||
full_width: !view_props?.full_width,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
disabled={!isEditingAllowed}
|
|
||||||
>
|
>
|
||||||
Full width
|
Full width
|
||||||
<ToggleSwitch
|
<ToggleSwitch value={isFullWidth} onChange={() => {}} />
|
||||||
value={!!view_props?.full_width}
|
|
||||||
onChange={() => {}}
|
|
||||||
className={cn({
|
|
||||||
"opacity-40": !isEditingAllowed,
|
|
||||||
})}
|
|
||||||
disabled={!isEditingAllowed}
|
|
||||||
/>
|
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
{MENU_ITEMS.map((item) => {
|
{MENU_ITEMS.map((item) => {
|
||||||
if (!item.shouldRender) return null;
|
if (!item.shouldRender) return null;
|
||||||
|
@ -4,6 +4,8 @@ import { EditorReadOnlyRefApi, EditorRefApi, IMarking } from "@plane/document-ed
|
|||||||
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
import { PageEditorMobileHeaderRoot, PageExtraOptions, PageSummaryPopover, PageToolbar } from "@/components/pages";
|
||||||
// helpers
|
// helpers
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
// hooks
|
||||||
|
import { usePageFilters } from "@/hooks/use-page-filters";
|
||||||
// store
|
// store
|
||||||
import { IPageStore } from "@/store/pages/page.store";
|
import { IPageStore } from "@/store/pages/page.store";
|
||||||
|
|
||||||
@ -36,8 +38,9 @@ export const PageEditorHeaderRoot: React.FC<Props> = observer((props) => {
|
|||||||
setSidePeekVisible,
|
setSidePeekVisible,
|
||||||
} = props;
|
} = props;
|
||||||
// derived values
|
// derived values
|
||||||
const { isContentEditable, view_props } = pageStore;
|
const { isContentEditable } = pageStore;
|
||||||
const isFullWidth = !!view_props?.full_width;
|
// page filters
|
||||||
|
const { isFullWidth } = usePageFilters();
|
||||||
|
|
||||||
if (!editorRef.current && !readOnlyEditorRef.current) return null;
|
if (!editorRef.current && !readOnlyEditorRef.current) return null;
|
||||||
|
|
||||||
|
12
web/hooks/use-page-filters.ts
Normal file
12
web/hooks/use-page-filters.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// hooks
|
||||||
|
import useLocalStorage from "@/hooks/use-local-storage";
|
||||||
|
|
||||||
|
export const usePageFilters = () => {
|
||||||
|
const { storedValue: isFullWidth, setValue: setFullWidth } = useLocalStorage<boolean>("page_full_width", true);
|
||||||
|
const handleFullWidth = (value: boolean) => setFullWidth(value);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isFullWidth: !!isFullWidth,
|
||||||
|
handleFullWidth,
|
||||||
|
};
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import set from "lodash/set";
|
import set from "lodash/set";
|
||||||
import { action, computed, makeObservable, observable, reaction, runInAction } from "mobx";
|
import { action, computed, makeObservable, observable, reaction, runInAction } from "mobx";
|
||||||
// types
|
// types
|
||||||
import { TPage, TPageViewProps } from "@plane/types";
|
import { TPage } from "@plane/types";
|
||||||
// constants
|
// constants
|
||||||
import { EPageAccess } from "@/constants/page";
|
import { EPageAccess } from "@/constants/page";
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
import { EUserProjectRoles } from "@/constants/project";
|
||||||
@ -33,7 +33,6 @@ export interface IPageStore extends TPage {
|
|||||||
cleanup: () => void;
|
cleanup: () => void;
|
||||||
// actions
|
// actions
|
||||||
update: (pageData: Partial<TPage>) => Promise<TPage | undefined>;
|
update: (pageData: Partial<TPage>) => Promise<TPage | undefined>;
|
||||||
updateViewProps: (viewProps: Partial<TPageViewProps>) => void;
|
|
||||||
makePublic: () => Promise<void>;
|
makePublic: () => Promise<void>;
|
||||||
makePrivate: () => Promise<void>;
|
makePrivate: () => Promise<void>;
|
||||||
lock: () => Promise<void>;
|
lock: () => Promise<void>;
|
||||||
@ -65,7 +64,6 @@ export class PageStore implements IPageStore {
|
|||||||
updated_by: string | undefined;
|
updated_by: string | undefined;
|
||||||
created_at: Date | undefined;
|
created_at: Date | undefined;
|
||||||
updated_at: Date | undefined;
|
updated_at: Date | undefined;
|
||||||
view_props: TPageViewProps | undefined;
|
|
||||||
// helpers
|
// helpers
|
||||||
oldName: string = "";
|
oldName: string = "";
|
||||||
// reactions
|
// reactions
|
||||||
@ -93,7 +91,6 @@ export class PageStore implements IPageStore {
|
|||||||
this.updated_by = page?.updated_by || undefined;
|
this.updated_by = page?.updated_by || undefined;
|
||||||
this.created_at = page?.created_at || undefined;
|
this.created_at = page?.created_at || undefined;
|
||||||
this.updated_at = page?.updated_at || undefined;
|
this.updated_at = page?.updated_at || undefined;
|
||||||
this.view_props = page?.view_props || undefined;
|
|
||||||
this.oldName = page?.name || "";
|
this.oldName = page?.name || "";
|
||||||
|
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
@ -117,7 +114,6 @@ export class PageStore implements IPageStore {
|
|||||||
updated_by: observable.ref,
|
updated_by: observable.ref,
|
||||||
created_at: observable.ref,
|
created_at: observable.ref,
|
||||||
updated_at: observable.ref,
|
updated_at: observable.ref,
|
||||||
view_props: observable,
|
|
||||||
// helpers
|
// helpers
|
||||||
oldName: observable,
|
oldName: observable,
|
||||||
// computed
|
// computed
|
||||||
@ -137,7 +133,6 @@ export class PageStore implements IPageStore {
|
|||||||
cleanup: action,
|
cleanup: action,
|
||||||
// actions
|
// actions
|
||||||
update: action,
|
update: action,
|
||||||
updateViewProps: action,
|
|
||||||
makePublic: action,
|
makePublic: action,
|
||||||
makePrivate: action,
|
makePrivate: action,
|
||||||
lock: action,
|
lock: action,
|
||||||
@ -216,7 +211,6 @@ export class PageStore implements IPageStore {
|
|||||||
updated_by: this.updated_by,
|
updated_by: this.updated_by,
|
||||||
created_at: this.created_at,
|
created_at: this.created_at,
|
||||||
updated_at: this.updated_at,
|
updated_at: this.updated_at,
|
||||||
view_props: this.view_props,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,38 +332,6 @@ export class PageStore implements IPageStore {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @description update the page view props
|
|
||||||
* @param {Partial<TPageViewProps>} updatedProps
|
|
||||||
*/
|
|
||||||
updateViewProps = async (updatedProps: Partial<TPageViewProps>) => {
|
|
||||||
const { workspaceSlug, projectId } = this.store.app.router;
|
|
||||||
if (!workspaceSlug || !projectId || !this.id) return undefined;
|
|
||||||
|
|
||||||
const currentViewProps = { ...this.view_props };
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
Object.keys(updatedProps).forEach((key) => {
|
|
||||||
const currentPageKey = key as keyof TPageViewProps;
|
|
||||||
if (this.view_props) set(this.view_props, key, updatedProps[currentPageKey]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.pageService.update(workspaceSlug, projectId, this.id, {
|
|
||||||
view_props: {
|
|
||||||
...this.view_props,
|
|
||||||
...updatedProps,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
runInAction(() => {
|
|
||||||
this.view_props = currentViewProps;
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description make the page public
|
* @description make the page public
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user