mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 14:28:51 +08:00
chore: move client folders (#948)
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useAppState, PageMeta } from '@/providers/app-state-provider';
|
||||
|
||||
export type ChangePageMeta = (
|
||||
pageId: string,
|
||||
pageMeta: Partial<PageMeta>
|
||||
) => void;
|
||||
|
||||
export const useChangePageMeta = () => {
|
||||
const { currentWorkspace } = useAppState();
|
||||
|
||||
return useCallback<ChangePageMeta>(
|
||||
(pageId, pageMeta) => {
|
||||
currentWorkspace?.blocksuiteWorkspace?.setPageMeta(pageId, {
|
||||
...pageMeta,
|
||||
});
|
||||
},
|
||||
[currentWorkspace]
|
||||
);
|
||||
};
|
||||
|
||||
export default ChangePageMeta;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useAppState, PageMeta } from '@/providers/app-state-provider';
|
||||
|
||||
export const useCurrentPageMeta = (): PageMeta | null => {
|
||||
const { currentPage, currentWorkspace } = useAppState();
|
||||
|
||||
const pageMetaHandler = useCallback((): PageMeta | null => {
|
||||
if (!currentPage || !currentWorkspace) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
(currentWorkspace.blocksuiteWorkspace?.meta.pageMetas.find(
|
||||
p => p.id === currentPage.id
|
||||
) as PageMeta) ?? null
|
||||
);
|
||||
}, [currentPage, currentWorkspace]);
|
||||
|
||||
const [currentPageMeta, setCurrentPageMeta] = useState<PageMeta | null>(
|
||||
pageMetaHandler
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPageMeta(pageMetaHandler);
|
||||
|
||||
const dispose = currentWorkspace?.blocksuiteWorkspace?.meta.pagesUpdated.on(
|
||||
() => {
|
||||
setCurrentPageMeta(pageMetaHandler);
|
||||
}
|
||||
).dispose;
|
||||
|
||||
return () => {
|
||||
dispose?.();
|
||||
};
|
||||
}, [currentPage, currentWorkspace, pageMetaHandler]);
|
||||
|
||||
return currentPageMeta;
|
||||
};
|
||||
|
||||
export default useCurrentPageMeta;
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { useRouter } from 'next/router';
|
||||
// It is a fully effective hook
|
||||
// Cause it not just ensure workspace loaded, but also have router change.
|
||||
export const useEnsureWorkspace = () => {
|
||||
const [workspaceLoaded, setWorkspaceLoaded] = useState(false);
|
||||
const { workspaceList, loadWorkspace, user } = useAppState();
|
||||
const router = useRouter();
|
||||
const [activeWorkspaceId, setActiveWorkspaceId] = useState(
|
||||
router.query.workspaceId as string
|
||||
);
|
||||
|
||||
// const defaultOutLineWorkspaceId = '99ce7eb7';
|
||||
// console.log(defaultOutLineWorkspaceId);
|
||||
useEffect(() => {
|
||||
// If router.query.workspaceId is not in workspace list, jump to 404 page
|
||||
// If workspaceList is empty, we need to create a default workspace but not jump to 404
|
||||
if (
|
||||
workspaceList.length &&
|
||||
// FIXME: router is not ready when this hook is called
|
||||
location.pathname.startsWith(`/workspace/${router.query.workspaceId}`) &&
|
||||
workspaceList.findIndex(
|
||||
meta => meta.id.toString() === router.query.workspaceId
|
||||
) === -1
|
||||
) {
|
||||
router.push('/404');
|
||||
return;
|
||||
}
|
||||
// If user is not login and input a custom workspaceId, jump to 404 page
|
||||
// if (
|
||||
// !user &&
|
||||
// router.query.workspaceId &&
|
||||
// router.query.workspaceId !== defaultOutLineWorkspaceId
|
||||
// ) {
|
||||
// router.push('/404');
|
||||
// return;
|
||||
// }
|
||||
const workspaceId =
|
||||
(router.query.workspaceId as string) || workspaceList[0]?.id;
|
||||
loadWorkspace.current(workspaceId).finally(() => {
|
||||
setWorkspaceLoaded(true);
|
||||
setActiveWorkspaceId(activeWorkspaceId);
|
||||
});
|
||||
}, [loadWorkspace, router, user, workspaceList, activeWorkspaceId]);
|
||||
|
||||
return {
|
||||
workspaceLoaded,
|
||||
activeWorkspaceId,
|
||||
};
|
||||
};
|
||||
|
||||
export default useEnsureWorkspace;
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Page } from '@blocksuite/store';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export type EventCallBack<T> = (callback: (props: T) => void) => void;
|
||||
export type UseHistoryUpdated = (page?: Page) => EventCallBack<Page>;
|
||||
|
||||
export const useHistoryUpdate: UseHistoryUpdated = () => {
|
||||
const { currentPage } = useAppState();
|
||||
|
||||
const callbackQueue = useRef<((page: Page) => void)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
currentPage.signals.historyUpdated.on(() => {
|
||||
callbackQueue.current.forEach(callback => {
|
||||
callback(currentPage);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
|
||||
return () => {
|
||||
callbackQueue.current = [];
|
||||
currentPage.signals.historyUpdated.dispose();
|
||||
};
|
||||
}, [currentPage]);
|
||||
|
||||
return callback => {
|
||||
callbackQueue.current.push(callback);
|
||||
};
|
||||
};
|
||||
|
||||
export default useHistoryUpdate;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||
|
||||
export type UseLocalStorage = <S>(
|
||||
key: string,
|
||||
initialState: S | (() => S),
|
||||
// If initialState is different from the initial local data, set it
|
||||
initialValue?: S
|
||||
) => [S, Dispatch<SetStateAction<S>>];
|
||||
|
||||
export const useLocalStorage: UseLocalStorage = (
|
||||
key,
|
||||
defaultValue,
|
||||
initialValue
|
||||
) => {
|
||||
const [item, setItem] = useState(defaultValue);
|
||||
|
||||
useEffect(() => {
|
||||
const saved = localStorage.getItem(key);
|
||||
if (saved) {
|
||||
setItem(JSON.parse(saved));
|
||||
} else if (initialValue) {
|
||||
setItem(initialValue);
|
||||
}
|
||||
}, [initialValue, key, setItem]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(key, JSON.stringify(item));
|
||||
}, [item, key]);
|
||||
|
||||
return [item, setItem];
|
||||
};
|
||||
|
||||
export default useLocalStorage;
|
||||
@@ -0,0 +1,51 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Member } from '@affine/datacenter';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
export const useMembers = () => {
|
||||
const { dataCenter, currentWorkspace } = useAppState();
|
||||
const [members, setMembers] = useState<Member[]>([]);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const refreshMembers = useCallback(async () => {
|
||||
if (!currentWorkspace || !dataCenter) return;
|
||||
const members = await dataCenter.getMembers(currentWorkspace.id);
|
||||
setMembers(members);
|
||||
}, [dataCenter, currentWorkspace]);
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
await refreshMembers();
|
||||
setLoaded(true);
|
||||
};
|
||||
init();
|
||||
}, [refreshMembers]);
|
||||
|
||||
const inviteMember = async (email: string) => {
|
||||
currentWorkspace &&
|
||||
dataCenter &&
|
||||
(await dataCenter.inviteMember(currentWorkspace.id, email));
|
||||
};
|
||||
|
||||
const removeMember = async (permissionId: number) => {
|
||||
if (!currentWorkspace || !dataCenter) {
|
||||
return;
|
||||
}
|
||||
setLoaded(false);
|
||||
await dataCenter.removeMember(currentWorkspace.id, permissionId);
|
||||
await refreshMembers();
|
||||
setLoaded(true);
|
||||
};
|
||||
|
||||
const getUserByEmail = async (email: string) => {
|
||||
if (!currentWorkspace) return null;
|
||||
return dataCenter?.getUserByEmail(currentWorkspace.id, email);
|
||||
};
|
||||
return {
|
||||
members,
|
||||
removeMember,
|
||||
inviteMember,
|
||||
getUserByEmail,
|
||||
loaded,
|
||||
};
|
||||
};
|
||||
|
||||
export default useMembers;
|
||||
@@ -0,0 +1,144 @@
|
||||
import { uuidv4, Workspace } from '@blocksuite/store';
|
||||
import { QueryContent } from '@blocksuite/store/dist/workspace/search';
|
||||
import { PageMeta, useAppState } from '@/providers/app-state-provider';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import { useChangePageMeta } from '@/hooks/use-change-page-meta';
|
||||
import { useRouter } from 'next/router';
|
||||
import { WorkspaceUnit } from '@affine/datacenter';
|
||||
|
||||
export type EditorHandlers = {
|
||||
createPage: (params?: {
|
||||
pageId?: string;
|
||||
title?: string;
|
||||
}) => Promise<string | null>;
|
||||
openPage: (
|
||||
pageId: string,
|
||||
query?: { [key: string]: string },
|
||||
newTab?: boolean
|
||||
) => Promise<boolean>;
|
||||
getPageMeta: (pageId: string) => PageMeta | null;
|
||||
toggleDeletePage: (pageId: string) => Promise<boolean>;
|
||||
toggleFavoritePage: (pageId: string) => Promise<boolean>;
|
||||
permanentlyDeletePage: (pageId: string) => void;
|
||||
search: (
|
||||
query: QueryContent,
|
||||
workspace?: Workspace
|
||||
) => Map<string, string | undefined>;
|
||||
// changeEditorMode: (pageId: string) => void;
|
||||
changePageMode: (
|
||||
pageId: string,
|
||||
mode: EditorContainer['mode']
|
||||
) => Promise<EditorContainer['mode']>;
|
||||
};
|
||||
|
||||
const getPageMeta = (workspace: WorkspaceUnit | null, pageId: string) => {
|
||||
return workspace?.blocksuiteWorkspace?.meta.pageMetas.find(
|
||||
p => p.id === pageId
|
||||
);
|
||||
};
|
||||
export const usePageHelper = (): EditorHandlers => {
|
||||
const router = useRouter();
|
||||
const changePageMeta = useChangePageMeta();
|
||||
const { currentWorkspace, editor } = useAppState();
|
||||
|
||||
return {
|
||||
createPage: ({
|
||||
pageId = uuidv4().replaceAll('-', ''),
|
||||
title = '',
|
||||
} = {}) => {
|
||||
return new Promise(resolve => {
|
||||
if (!currentWorkspace) {
|
||||
return resolve(null);
|
||||
}
|
||||
currentWorkspace.blocksuiteWorkspace?.createPage(pageId);
|
||||
currentWorkspace.blocksuiteWorkspace?.signals.pageAdded.once(
|
||||
addedPageId => {
|
||||
currentWorkspace.blocksuiteWorkspace?.setPageMeta(addedPageId, {
|
||||
title,
|
||||
});
|
||||
resolve(addedPageId);
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
toggleFavoritePage: async pageId => {
|
||||
const pageMeta = getPageMeta(currentWorkspace, pageId);
|
||||
if (!pageMeta) {
|
||||
return Promise.reject('No page');
|
||||
}
|
||||
const favorite = !pageMeta.favorite;
|
||||
changePageMeta(pageMeta.id, {
|
||||
favorite,
|
||||
});
|
||||
return favorite;
|
||||
},
|
||||
toggleDeletePage: async pageId => {
|
||||
const pageMeta = getPageMeta(currentWorkspace, pageId);
|
||||
|
||||
if (!pageMeta) {
|
||||
return Promise.reject('No page');
|
||||
}
|
||||
const trash = !pageMeta.trash;
|
||||
|
||||
changePageMeta(pageMeta.id, {
|
||||
trash,
|
||||
trashDate: +new Date(),
|
||||
});
|
||||
return trash;
|
||||
},
|
||||
search: (query: QueryContent, workspace?: Workspace) => {
|
||||
if (workspace) {
|
||||
return workspace.search(query);
|
||||
}
|
||||
if (currentWorkspace) {
|
||||
if (currentWorkspace.blocksuiteWorkspace) {
|
||||
return currentWorkspace.blocksuiteWorkspace.search(query);
|
||||
}
|
||||
}
|
||||
return new Map();
|
||||
},
|
||||
changePageMode: async (pageId, mode) => {
|
||||
const pageMeta = getPageMeta(currentWorkspace, pageId);
|
||||
if (!pageMeta) {
|
||||
return Promise.reject('No page');
|
||||
}
|
||||
|
||||
editor?.setAttribute('mode', mode as string);
|
||||
|
||||
changePageMeta(pageMeta.id, {
|
||||
mode,
|
||||
});
|
||||
return mode;
|
||||
},
|
||||
permanentlyDeletePage: pageId => {
|
||||
// TODO: workspace.meta.removePage or workspace.removePage?
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
currentWorkspace!.blocksuiteWorkspace?.meta.removePage(pageId);
|
||||
},
|
||||
openPage: (pageId, query = {}, newTab = false) => {
|
||||
pageId = pageId.replace('space:', '');
|
||||
|
||||
if (newTab) {
|
||||
window.open(`/workspace/${currentWorkspace?.id}/${pageId}`, '_blank');
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
return router.push({
|
||||
pathname: `/workspace/${currentWorkspace?.id}/${pageId}`,
|
||||
query,
|
||||
});
|
||||
},
|
||||
getPageMeta: pageId => {
|
||||
if (!currentWorkspace) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
(currentWorkspace.blocksuiteWorkspace?.meta.pageMetas.find(
|
||||
page => page.id === pageId
|
||||
) as PageMeta) || null
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default usePageHelper;
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
export type EventCallBack<T> = (callback: (props: T) => void) => void;
|
||||
|
||||
export type UsePropsUpdated = (
|
||||
editor?: EditorContainer
|
||||
) => EventCallBack<EditorContainer>;
|
||||
|
||||
export const usePropsUpdated: UsePropsUpdated = () => {
|
||||
const { editor } = useAppState();
|
||||
|
||||
const callbackQueue = useRef<((editor: EditorContainer) => void)[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
editor.pageBlockModel?.propsUpdated.on(() => {
|
||||
callbackQueue.current.forEach(callback => {
|
||||
callback(editor);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
|
||||
return () => {
|
||||
callbackQueue.current = [];
|
||||
editor?.pageBlockModel?.propsUpdated?.dispose();
|
||||
};
|
||||
}, [editor]);
|
||||
|
||||
return callback => {
|
||||
callbackQueue.current.push(callback);
|
||||
};
|
||||
};
|
||||
|
||||
export default usePropsUpdated;
|
||||
@@ -0,0 +1,58 @@
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { WorkspaceUnit } from '@affine/datacenter';
|
||||
|
||||
export const useWorkspaceHelper = () => {
|
||||
const { dataCenter, currentWorkspace } = useAppState();
|
||||
const createWorkspace = async (name: string) => {
|
||||
const workspaceInfo = await dataCenter.createWorkspace({
|
||||
name: name,
|
||||
});
|
||||
if (workspaceInfo && workspaceInfo.id) {
|
||||
return await dataCenter.loadWorkspace(workspaceInfo.id);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// const updateWorkspace = async (workspace: Workspace) => {};
|
||||
|
||||
const publishWorkspace = async (workspaceId: string, publish: boolean) => {
|
||||
await dataCenter.setWorkspacePublish(workspaceId, publish);
|
||||
};
|
||||
|
||||
const updateWorkspace = async (
|
||||
{ name, avatarBlob }: { name?: string; avatarBlob?: Blob },
|
||||
workspace: WorkspaceUnit
|
||||
) => {
|
||||
if (name) {
|
||||
await dataCenter.updateWorkspaceMeta({ name }, workspace);
|
||||
}
|
||||
if (avatarBlob) {
|
||||
const blobId = await dataCenter.setBlob(workspace, avatarBlob);
|
||||
await dataCenter.updateWorkspaceMeta({ avatar: blobId }, workspace);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteWorkSpace = async () => {
|
||||
currentWorkspace && (await dataCenter.deleteWorkspace(currentWorkspace.id));
|
||||
};
|
||||
const leaveWorkSpace = async () => {
|
||||
currentWorkspace && (await dataCenter.leaveWorkspace(currentWorkspace.id));
|
||||
};
|
||||
|
||||
const acceptInvite = async (inviteCode: string) => {
|
||||
let inviteInfo;
|
||||
if (inviteCode) {
|
||||
inviteInfo = await dataCenter.acceptInvitation(inviteCode);
|
||||
}
|
||||
return inviteInfo;
|
||||
};
|
||||
|
||||
return {
|
||||
createWorkspace,
|
||||
publishWorkspace,
|
||||
updateWorkspace,
|
||||
deleteWorkSpace,
|
||||
leaveWorkSpace,
|
||||
acceptInvite,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user