Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import { Doc } from '../types';

import { KEY_LIST_DOC } from './useDocs';

export const createDoc = async (): Promise<Doc> => {
type CreateDocParams = {
title?: string;
} | void;

export const createDoc = async (params: CreateDocParams): Promise<Doc> => {
const response = await fetchAPI(`documents/`, {
method: 'POST',
body: JSON.stringify({ title: params?.title }),
});

if (!response.ok) {
Expand All @@ -25,7 +30,7 @@ interface CreateDocProps {

export function useCreateDoc({ onSuccess, onError }: CreateDocProps) {
const queryClient = useQueryClient();
return useMutation<Doc, APIError>({
return useMutation<Doc, APIError, CreateDocParams>({
mutationFn: createDoc,
onSuccess: (data) => {
void queryClient.resetQueries({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { VariantType, useToastProvider } from '@openfun/cunningham-react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import {
UseMutationOptions,
useMutation,
useQueryClient,
} from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';

import { APIError, errorCauses, fetchAPI } from '@/api';
import { Doc } from '@/docs/doc-management';
import { Doc, LinkReach, LinkRole } from '@/docs/doc-management';

export type UpdateDocLinkParams = Pick<Doc, 'id' | 'link_reach'> &
Partial<Pick<Doc, 'link_role'>>;

type UpdateDocLinkResponse = { link_role: LinkRole; link_reach: LinkReach };

export const updateDocLink = async ({
id,
...params
}: UpdateDocLinkParams): Promise<Doc> => {
}: UpdateDocLinkParams): Promise<UpdateDocLinkResponse> => {
const response = await fetchAPI(`documents/${id}/link-configuration/`, {
method: 'PUT',
body: JSON.stringify({
Expand All @@ -26,26 +32,27 @@ export const updateDocLink = async ({
);
}

return response.json() as Promise<Doc>;
return response.json() as Promise<UpdateDocLinkResponse>;
};

interface UpdateDocLinkProps {
onSuccess?: (data: Doc) => void;
type UseUpdateDocLinkOptions = UseMutationOptions<
UpdateDocLinkResponse,
APIError,
UpdateDocLinkParams
> & {
listInvalidQueries?: string[];
}
};

export function useUpdateDocLink({
onSuccess,
listInvalidQueries,
}: UpdateDocLinkProps = {}) {
export function useUpdateDocLink(options?: UseUpdateDocLinkOptions) {
const queryClient = useQueryClient();
const { toast } = useToastProvider();
const { t } = useTranslation();

return useMutation<Doc, APIError, UpdateDocLinkParams>({
return useMutation<UpdateDocLinkResponse, APIError, UpdateDocLinkParams>({
mutationFn: updateDocLink,
onSuccess: (data) => {
listInvalidQueries?.forEach((queryKey) => {
...options,
onSuccess: (data, variables, onMutateResult, context) => {
options?.listInvalidQueries?.forEach((queryKey) => {
void queryClient.invalidateQueries({
queryKey: [queryKey],
});
Expand All @@ -59,7 +66,7 @@ export function useUpdateDocLink({
},
);

onSuccess?.(data);
options?.onSuccess?.(data, variables, onMutateResult, context);
},
});
}
243 changes: 243 additions & 0 deletions src/frontend/apps/impress/src/pages/docs/new/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import { captureException } from '@sentry/nextjs';
import Head from 'next/head';
import { useSearchParams } from 'next/navigation';
import { useRouter } from 'next/router';
import { ReactElement, useCallback, useEffect } from 'react';

import { Loading } from '@/components';
import {
LinkReach,
LinkRole,
Role,
useCreateDoc,
} from '@/features/docs/doc-management';
import {
KEY_LIST_USER,
getUsers,
useCreateDocAccess,
useCreateDocInvitation,
useDocAccesses,
useUsers,
} from '@/features/docs/doc-share';
import { useUpdateDocLink } from '@/features/docs/doc-share/api/useUpdateDocLink';
import { useSkeletonStore } from '@/features/skeletons';
import { MainLayout } from '@/layouts';
import { NextPageWithLayout } from '@/types/next';

const Page: NextPageWithLayout = () => {
const { setIsSkeletonVisible } = useSkeletonStore();
const router = useRouter();
const searchParams = useSearchParams();
const linkReach = searchParams.get('link-reach');
const linkRole = searchParams.get('link-role');
const title = searchParams.get('title');
const members = searchParams.get('members');
const { mutateAsync: createInvitation } = useCreateDocInvitation();
const { mutateAsync: createDocAccess } = useCreateDocAccess();

const {
mutate: createDoc,
//isSuccess: isDocCreated,
data: doc,
} = useCreateDoc({
onSuccess: (doc) => {
if ((linkReach && linkRole) || linkReach || members) {
return;
}

redirectToDoc(doc.id);
},
onError: () => {},
});

const { mutate: updateDocLink } = useUpdateDocLink({
onSuccess: (_, params) => {
if (members || !params.id) {
return;
}

redirectToDoc(params.id);
},
onError: (error, params) => {
captureException(error, {
extra: {
docId: params.id,
linkReach,
linkRole,
},
});

if (params.id) {
redirectToDoc(params.id);
}
},
});

const redirectToDoc = useCallback(
(docId: string) => {
void router.push(`/docs/${docId}`);
},
[router],
);

useEffect(() => {
setIsSkeletonVisible(true);
}, [setIsSkeletonVisible]);

// Doc creation effect
useEffect(() => {
if (doc) {
return;
}

createDoc({
title: title || undefined,
});
}, [createDoc, doc, title]);

// Doc link update effect
useEffect(() => {
if (!linkReach || !doc) {
return;
}

updateDocLink({
id: doc.id,
link_reach: linkReach as LinkReach,
link_role: (linkRole as LinkRole | undefined) || undefined,
});
}, [linkReach, doc, updateDocLink, redirectToDoc, linkRole]);

// const onInvite = async () => {
// setIsLoading(true);
// const promises = selectedUsers.map((user) => {
// const isInvitationMode = user.id === user.email;

// const payload = {
// role: invitationRole,
// docId: doc.id,
// };

// return isInvitationMode
// ? createInvitation({
// ...payload,
// email: user.email.toLowerCase(),
// })
// : createDocAccess({
// ...payload,
// memberId: user.id,
// });
// });

// const settledPromises = await Promise.allSettled(promises);
// settledPromises.forEach((settledPromise) => {
// if (settledPromise.status === 'rejected') {
// onError(settledPromise.reason as APIErrorUser);
// }
// });
// afterInvite?.();
// setIsLoading(false);
// };

// members=user%40example.org%2Ceditor%7Cuser2%40example.org%2Creader
// members=user@example.org,editor|user2@example.org,reader
useEffect(() => {
if (!members || !doc) {
return;
}

console.log('members', members);
const membersList = members.split('|').map((memberStr) => {
const [email, role] = memberStr.split(',');
return { email, role: role as Role };
});

console.log('membersList', membersList);

for (const member of membersList) {
getUsers({
query: member.email,
docId: doc.id,
})
.then((users) => {
if (users.length > 0) {
console.log('User exists:', users);
// User exists, create doc access
// createDocAccess({
// role: member.role,
// docId: doc.id,
// memberId: users[0].id,
// }).catch(() => {
// // Ignore errors
// });
} else {
console.log('User does not exist:', {
role: member.role,
docId: doc.id,
email: member.email.toLowerCase(),
});
// User does not exist, create invitation
// createInvitation({
// role: member.role,
// docId: doc.id,
// email: member.email.toLowerCase(),
// }).catch(() => {
// // Ignore errors
// });
}
})
.catch(() => {
// Ignore errors
});

// const isInvitationMode = !member.email.match(
// /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
// );

// const payload = {
// role: member.role,
// docId: doc.id,
// };

// if (isInvitationMode) {
// createInvitation({
// ...payload,
// email: member.email.toLowerCase(),
// }).catch(() => {
// // Ignore errors
// });
// } else {
// createDocAccess({
// ...payload,
// memberId: member.email,
// }).catch(() => {
// // Ignore errors
// });
// }
}

//redirectToDoc(doc.id);

//getUsers
}, [createDocAccess, createInvitation, doc, members]);

if (!linkReach && linkRole) {
console.warn('link-reach parameter is missing');
}

return <Loading />;
};

Page.getLayout = function getLayout(page: ReactElement) {
return (
<>
<Head>
<meta name="robots" content="noindex" />
</Head>

<MainLayout enableResizablePanel={false}>{page}</MainLayout>
</>
);
};

export default Page;
Loading