mirror of
https://github.com/docmost/docmost.git
synced 2025-11-22 23:01:10 +10:00
Share - WIP
This commit is contained in:
@ -139,7 +139,7 @@ export default function DrawioView(props: NodeViewProps) {
|
||||
)}
|
||||
/>
|
||||
|
||||
{selected && (
|
||||
{selected && editor.isEditable && (
|
||||
<ActionIcon
|
||||
onClick={handleOpen}
|
||||
variant="default"
|
||||
|
||||
@ -170,7 +170,7 @@ export default function ExcalidrawView(props: NodeViewProps) {
|
||||
)}
|
||||
/>
|
||||
|
||||
{selected && (
|
||||
{selected && editor.isEditable && (
|
||||
<ActionIcon
|
||||
onClick={handleOpen}
|
||||
variant="default"
|
||||
|
||||
52
apps/client/src/features/editor/readonly-page-editor.tsx
Normal file
52
apps/client/src/features/editor/readonly-page-editor.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import "@/features/editor/styles/index.css";
|
||||
import React, { useMemo } from "react";
|
||||
import { EditorProvider } from "@tiptap/react";
|
||||
import { mainExtensions } from "@/features/editor/extensions/extensions";
|
||||
import { Document } from "@tiptap/extension-document";
|
||||
import { Heading } from "@tiptap/extension-heading";
|
||||
import { Text } from "@tiptap/extension-text";
|
||||
import { Placeholder } from "@tiptap/extension-placeholder";
|
||||
|
||||
interface PageEditorProps {
|
||||
title: string;
|
||||
content: any;
|
||||
}
|
||||
|
||||
export default function ReadonlyPageEditor({
|
||||
title,
|
||||
content,
|
||||
}: PageEditorProps) {
|
||||
const extensions = useMemo(() => {
|
||||
return [...mainExtensions];
|
||||
}, []);
|
||||
|
||||
const titleExtensions = [
|
||||
Document.extend({
|
||||
content: "heading",
|
||||
}),
|
||||
Heading,
|
||||
Text,
|
||||
Placeholder.configure({
|
||||
placeholder: "Untitled",
|
||||
showOnlyWhenEditable: false,
|
||||
}),
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<EditorProvider
|
||||
editable={false}
|
||||
immediatelyRender={true}
|
||||
extensions={titleExtensions}
|
||||
content={title}
|
||||
></EditorProvider>
|
||||
|
||||
<EditorProvider
|
||||
editable={false}
|
||||
immediatelyRender={true}
|
||||
extensions={extensions}
|
||||
content={content}
|
||||
></EditorProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
|
||||
const buildPageSlug = (pageSlugId: string, pageTitle?: string): string => {
|
||||
export const buildPageSlug = (pageSlugId: string, pageTitle?: string): string => {
|
||||
const titleSlug = slugify(pageTitle?.substring(0, 70) || "untitled", {
|
||||
customReplacements: [
|
||||
["♥", ""],
|
||||
@ -8,7 +8,7 @@ const buildPageSlug = (pageSlugId: string, pageTitle?: string): string => {
|
||||
],
|
||||
});
|
||||
|
||||
return `p/${titleSlug}-${pageSlugId}`;
|
||||
return `${titleSlug}-${pageSlugId}`;
|
||||
};
|
||||
|
||||
export const buildPageUrl = (
|
||||
@ -17,7 +17,7 @@ export const buildPageUrl = (
|
||||
pageTitle?: string,
|
||||
): string => {
|
||||
if (spaceName === undefined) {
|
||||
return `/${buildPageSlug(pageSlugId, pageTitle)}`;
|
||||
return `/p/${buildPageSlug(pageSlugId, pageTitle)}`;
|
||||
}
|
||||
return `/s/${spaceName}/${buildPageSlug(pageSlugId, pageTitle)}`;
|
||||
return `/s/${spaceName}/p/${buildPageSlug(pageSlugId, pageTitle)}`;
|
||||
};
|
||||
|
||||
65
apps/client/src/features/share/queries/share-query.ts
Normal file
65
apps/client/src/features/share/queries/share-query.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
UseQueryResult,
|
||||
} from "@tanstack/react-query";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { validate as isValidUuid } from "uuid";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ICreateShare,
|
||||
IShareInput,
|
||||
} from "@/features/share/types/share.types.ts";
|
||||
import {
|
||||
createShare,
|
||||
deleteShare,
|
||||
getShare,
|
||||
updateShare,
|
||||
} from "@/features/share/services/share-service.ts";
|
||||
import { IPage } from "@/features/page/types/page.types.ts";
|
||||
|
||||
export function useShareQuery(
|
||||
shareInput: Partial<IShareInput>,
|
||||
): UseQueryResult<IPage, Error> {
|
||||
const query = useQuery({
|
||||
queryKey: ["shares", shareInput],
|
||||
queryFn: () => getShare(shareInput),
|
||||
enabled: !!shareInput.shareId,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
export function useCreateShareMutation() {
|
||||
const { t } = useTranslation();
|
||||
return useMutation<any, Error, ICreateShare>({
|
||||
mutationFn: (data) => createShare(data),
|
||||
onSuccess: (data) => {},
|
||||
onError: (error) => {
|
||||
notifications.show({ message: t("Failed to share page"), color: "red" });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateShareMutation() {
|
||||
return useMutation<any, Error, Partial<IShareInput>>({
|
||||
mutationFn: (data) => updateShare(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteShareMutation() {
|
||||
const { t } = useTranslation();
|
||||
return useMutation({
|
||||
mutationFn: (shareId: string) => deleteShare(shareId),
|
||||
onSuccess: () => {
|
||||
notifications.show({ message: t("Share deleted successfully") });
|
||||
},
|
||||
onError: (error) => {
|
||||
notifications.show({
|
||||
message: t("Failed to delete share"),
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
36
apps/client/src/features/share/services/share-service.ts
Normal file
36
apps/client/src/features/share/services/share-service.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import api from "@/lib/api-client";
|
||||
import {
|
||||
IExportPageParams,
|
||||
IMovePage,
|
||||
IMovePageToSpace,
|
||||
IPage,
|
||||
IPageInput,
|
||||
SidebarPagesParams,
|
||||
} from "@/features/page/types/page.types";
|
||||
import { IAttachment, IPagination } from "@/lib/types.ts";
|
||||
import { saveAs } from "file-saver";
|
||||
import {
|
||||
ICreateShare,
|
||||
IShareInput,
|
||||
} from "@/features/share/types/share.types.ts";
|
||||
|
||||
export async function createShare(data: ICreateShare): Promise<any> {
|
||||
const req = await api.post<any>("/shares/create", data);
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function getShare(
|
||||
shareInput: Partial<IShareInput>,
|
||||
): Promise<IPage> {
|
||||
const req = await api.post<IPage>("/shares/info", shareInput);
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function updateShare(data: Partial<IShareInput>): Promise<any> {
|
||||
const req = await api.post<IPage>("/shares/update", data);
|
||||
return req.data;
|
||||
}
|
||||
|
||||
export async function deleteShare(shareId: string): Promise<void> {
|
||||
await api.post("/shares/delete", { shareId });
|
||||
}
|
||||
12
apps/client/src/features/share/types/share.types.ts
Normal file
12
apps/client/src/features/share/types/share.types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
export interface ICreateShare {
|
||||
slugId: string;
|
||||
pageId: string;
|
||||
}
|
||||
|
||||
|
||||
export interface IShareInput {
|
||||
shareId: string;
|
||||
pageId?: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user