mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-06 11:59:51 +08:00
feat: create workspace from loading existing exported file (#2122)
Co-authored-by: Himself65 <himself65@outlook.com>
This commit is contained in:
@@ -18,7 +18,7 @@ export const updateAvailableAtom = atomWithObservable<boolean>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
const isMacosDesktop = environment.isDesktop && environment.isMacOs;
|
||||
if (isMacosDesktop) {
|
||||
const dispose = window.apis?.onClientUpdateAvailable(() => {
|
||||
const dispose = window.events?.updater.onClientUpdateReady(() => {
|
||||
subscriber.next(true);
|
||||
});
|
||||
return () => {
|
||||
|
||||
@@ -124,7 +124,7 @@ export const AppSidebar = forwardRef<HTMLElement, AppSidebarProps>(
|
||||
{clientUpdateAvailable && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.apis?.onClientUpdateInstall();
|
||||
window.apis?.updater.updateClient();
|
||||
}}
|
||||
noBorder
|
||||
className={updaterButtonStyle}
|
||||
|
||||
@@ -186,6 +186,7 @@ export const StyledButton = styled('button', {
|
||||
border: noBorder ? 'none' : '1px solid',
|
||||
WebkitAppRegion: 'no-drag',
|
||||
...displayInlineFlex('center', 'center'),
|
||||
gap: '10px',
|
||||
position: 'relative',
|
||||
// TODO: disabled color is not decided
|
||||
...(disabled
|
||||
|
||||
@@ -7,7 +7,7 @@ import { StyledMenuWrapper } from './styles';
|
||||
export type MenuProps = {
|
||||
width?: CSSProperties['width'];
|
||||
} & PopperProps &
|
||||
Omit<TooltipProps, 'title' | 'content'>;
|
||||
Omit<TooltipProps, 'title' | 'content' | 'placement'>;
|
||||
export const Menu = (props: MenuProps) => {
|
||||
const { width, content, placement = 'bottom-start', children } = props;
|
||||
return content ? (
|
||||
|
||||
@@ -16,7 +16,6 @@ function getAtom(w: Workspace, pageId: string): Atom<string> {
|
||||
baseAtom.onMount = set => {
|
||||
const disposable = w.meta.pageMetasUpdated.on(() => {
|
||||
const page = w.getPage(pageId);
|
||||
assertExists(page);
|
||||
set(page?.meta.title || 'Untitled');
|
||||
});
|
||||
return () => {
|
||||
|
||||
@@ -81,8 +81,14 @@
|
||||
"Jump to": "Jump to",
|
||||
"404 - Page Not Found": "404 - Page Not Found",
|
||||
"New Workspace": "New Workspace",
|
||||
"Add Workspace": "Add Workspace",
|
||||
"Add Workspace Hint": "Select where you already have",
|
||||
"Create": "Create",
|
||||
"Create your own workspace": "Create your own workspace",
|
||||
"Select": "Select",
|
||||
"Save": "Save",
|
||||
"Customize": "Customize",
|
||||
"Continue": "Continue",
|
||||
"Text": "Text (coming soon)",
|
||||
"Shape": "Shape",
|
||||
"Sticky": "Sticky (coming soon)",
|
||||
@@ -94,9 +100,20 @@
|
||||
"TrashButtonGroupDescription": "Once deleted, you can't undo this action. Do you confirm?",
|
||||
"Delete permanently": "Delete permanently",
|
||||
"Link": "Hyperlink (with selected text)",
|
||||
"Name Your Workspace": "Name Your Workspace",
|
||||
"Workspace description": "A workspace is your virtual space to capture, create and plan as just one person or together as a team.",
|
||||
"Set database location": "Set database location",
|
||||
"Default Location": "Default Location",
|
||||
"Default db location hint": "By default will be saved to {{location}}",
|
||||
"Workspace database storage description": "Select where you want to create your workspace. The data of workspace is saved locally by default.",
|
||||
"Created Successfully": "Created Successfully",
|
||||
"Added Successfully": "Added Successfully",
|
||||
"Quick search placeholder": "Quick Search...",
|
||||
"Quick search placeholder2": "Search in {{workspace}}",
|
||||
"Use on current device only": "Use on current device only",
|
||||
"Sync across devices with AFFiNE Cloud": "Sync across devices with AFFiNE Cloud",
|
||||
"Export success": "Export success",
|
||||
"Update workspace name success": "Update workspace name success",
|
||||
"Settings": "Settings",
|
||||
"Recent": "Recent",
|
||||
"recommendBrowser": " We recommend the <1>Chrome</1> browser for optimal experience.",
|
||||
@@ -125,6 +142,7 @@
|
||||
"login success": "Login success",
|
||||
"Create Or Import": "Create or Import",
|
||||
"Delete Workspace": "Delete Workspace",
|
||||
"Delete Workspace Label Hint": "After deleting this Workspace, you will permanently delete all of its content for everyone. No one will be able to recover the content of this Workspace",
|
||||
"Delete Workspace Description2": "Deleting (<1>{{workspace}}</1>) will delete both local and cloud data, this operation cannot be undone, please proceed with caution.",
|
||||
"Placeholder of delete workspace": "Please type workspace name to confirm",
|
||||
"Leave Workspace": "Leave Workspace",
|
||||
@@ -232,5 +250,18 @@
|
||||
"Page is Loading": "Page is Loading",
|
||||
"Loading All Workspaces": "Loading All Workspaces",
|
||||
"Loading Page": "Loading Page",
|
||||
"You cannot delete the last workspace": "You cannot delete the last workspace"
|
||||
"You cannot delete the last workspace": "You cannot delete the last workspace",
|
||||
"Open folder": "Open folder",
|
||||
"Open folder hint": "Check the where the storage folder is located.",
|
||||
"Move folder": "Move folder",
|
||||
"Move folder hint": "Select a new storage location.",
|
||||
"Change avatar hint": "Change avatar for all members.",
|
||||
"Change workspace name hint": "Change name for all members.",
|
||||
"Storage Folder": "Storage Folder",
|
||||
"Storage Folder Hint": "Check or change storage location.",
|
||||
"Move folder success": "Move folder success",
|
||||
"DB_FILE_PATH_INVALID": "Database file path invalid",
|
||||
"DB_FILE_ALREADY_LOADED": "Database file already loaded",
|
||||
"UNKNOWN_ERROR": "Unknown error",
|
||||
"DB_FILE_INVALID": "Invalid Database file"
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export const setLoginStorage = (login: LoginResponse) => {
|
||||
|
||||
const signInWithElectron = async (firebaseAuth: FirebaseAuth) => {
|
||||
if (window.apis) {
|
||||
const { url, requestInit } = await window.apis.getGoogleOauthCode();
|
||||
const { url, requestInit } = await window.apis.ui.getGoogleOauthCode();
|
||||
const { id_token } = await fetch(url, requestInit).then(res => res.json());
|
||||
const credential = GoogleAuthProvider.credential(id_token);
|
||||
const user = await signInWithCredential(firebaseAuth, credential);
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import type { BlobStorage } from '@blocksuite/store';
|
||||
import { assertExists } from '@blocksuite/store';
|
||||
|
||||
export const createSQLiteStorage = (workspaceId: string): BlobStorage => {
|
||||
const apis = window.apis;
|
||||
assertExists(apis);
|
||||
return {
|
||||
crud: {
|
||||
get: async (key: string) => {
|
||||
const buffer = await window.apis.db.getBlob(workspaceId, key);
|
||||
const buffer = await apis.db.getBlob(workspaceId, key);
|
||||
return buffer ? new Blob([buffer]) : null;
|
||||
},
|
||||
set: async (key: string, value: Blob) => {
|
||||
return window.apis.db.addBlob(
|
||||
await apis.db.addBlob(
|
||||
workspaceId,
|
||||
key,
|
||||
new Uint8Array(await value.arrayBuffer())
|
||||
);
|
||||
return key;
|
||||
},
|
||||
delete: async (key: string) => {
|
||||
return window.apis.db.deleteBlob(workspaceId, key);
|
||||
return apis.db.deleteBlob(workspaceId, key);
|
||||
},
|
||||
list: async () => {
|
||||
return window.apis.db.getPersistedBlobs(workspaceId);
|
||||
return apis.db.getPersistedBlobs(workspaceId);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -102,7 +102,7 @@ export const CRUD: WorkspaceCRUD<WorkspaceFlavour.LOCAL> = {
|
||||
|
||||
// workspaces in desktop
|
||||
if (window.apis && environment.isDesktop) {
|
||||
const desktopIds = await window.apis.workspace.list();
|
||||
const desktopIds = (await window.apis.workspace.list()).map(([id]) => id);
|
||||
// the ids maybe a subset of the local storage
|
||||
const moreWorkspaces = desktopIds.filter(
|
||||
id => !allWorkspaceIDs.includes(id)
|
||||
|
||||
@@ -21,7 +21,7 @@ const mockedAddBlob = vi.fn();
|
||||
vi.stubGlobal('window', {
|
||||
apis: {
|
||||
db: {
|
||||
getDoc: async () => {
|
||||
getDocAsUpdates: async () => {
|
||||
return Y.encodeStateAsUpdate(offlineYdoc);
|
||||
},
|
||||
applyDocUpdate: async (id: string, update: Uint8Array) => {
|
||||
@@ -31,15 +31,24 @@ vi.stubGlobal('window', {
|
||||
// todo: may need to hack the way to get hash keys of blobs
|
||||
return [];
|
||||
},
|
||||
onDBUpdate: (fn: (id: string) => void) => {
|
||||
addBlob: mockedAddBlob,
|
||||
} satisfies Partial<NonNullable<typeof window.apis>['db']>,
|
||||
},
|
||||
events: {
|
||||
db: {
|
||||
onDbFileUpdate: (fn: (id: string) => void) => {
|
||||
triggerDBUpdate = fn;
|
||||
return () => {
|
||||
triggerDBUpdate = null;
|
||||
};
|
||||
},
|
||||
addBlob: mockedAddBlob,
|
||||
} satisfies Partial<typeof window.apis.db>,
|
||||
},
|
||||
|
||||
// not used in this test
|
||||
onDbFileMissing: () => {
|
||||
return () => {};
|
||||
},
|
||||
},
|
||||
} satisfies Partial<NonNullable<typeof window.events>>,
|
||||
});
|
||||
|
||||
vi.stubGlobal('environment', {
|
||||
|
||||
@@ -162,18 +162,21 @@ const createSQLiteProvider = (
|
||||
blockSuiteWorkspace: BlockSuiteWorkspace
|
||||
): SQLiteProvider => {
|
||||
const sqliteOrigin = Symbol('sqlite-provider-origin');
|
||||
const apis = window.apis!;
|
||||
const events = window.events!;
|
||||
// make sure it is being used in Electron with APIs
|
||||
assertExists(environment.isDesktop && window.apis);
|
||||
assertExists(apis);
|
||||
assertExists(events);
|
||||
|
||||
function handleUpdate(update: Uint8Array, origin: unknown) {
|
||||
if (origin === sqliteOrigin) {
|
||||
return;
|
||||
}
|
||||
window.apis.db.applyDocUpdate(blockSuiteWorkspace.id, update);
|
||||
apis.db.applyDocUpdate(blockSuiteWorkspace.id, update);
|
||||
}
|
||||
|
||||
async function syncBlobIntoSQLite(bs: BlobManager) {
|
||||
const persistedKeys = await window.apis.db.getPersistedBlobs(
|
||||
const persistedKeys = await apis.db.getPersistedBlobs(
|
||||
blockSuiteWorkspace.id
|
||||
);
|
||||
|
||||
@@ -188,7 +191,7 @@ const createSQLiteProvider = (
|
||||
logger.warn('blob not found for', k);
|
||||
return;
|
||||
}
|
||||
return window.apis.db.addBlob(
|
||||
return window.apis?.db.addBlob(
|
||||
blockSuiteWorkspace.id,
|
||||
k,
|
||||
new Uint8Array(await blob.arrayBuffer())
|
||||
@@ -199,7 +202,7 @@ const createSQLiteProvider = (
|
||||
|
||||
async function syncUpdates() {
|
||||
logger.info('syncing updates from sqlite', blockSuiteWorkspace.id);
|
||||
const updates = await window.apis.db.getDoc(blockSuiteWorkspace.id);
|
||||
const updates = await apis.db.getDocAsUpdates(blockSuiteWorkspace.id);
|
||||
|
||||
if (updates) {
|
||||
Y.applyUpdate(blockSuiteWorkspace.doc, updates, sqliteOrigin);
|
||||
@@ -208,7 +211,7 @@ const createSQLiteProvider = (
|
||||
const mergeUpdates = Y.encodeStateAsUpdate(blockSuiteWorkspace.doc);
|
||||
|
||||
// also apply updates to sqlite
|
||||
window.apis.db.applyDocUpdate(blockSuiteWorkspace.id, mergeUpdates);
|
||||
apis.db.applyDocUpdate(blockSuiteWorkspace.id, mergeUpdates);
|
||||
|
||||
const bs = blockSuiteWorkspace.blobs;
|
||||
|
||||
@@ -240,7 +243,7 @@ const createSQLiteProvider = (
|
||||
blockSuiteWorkspace.doc.on('update', handleUpdate);
|
||||
|
||||
let timer = 0;
|
||||
unsubscribe = window.apis.db.onDBUpdate(workspaceId => {
|
||||
unsubscribe = events.db.onDbFileUpdate(workspaceId => {
|
||||
if (workspaceId === blockSuiteWorkspace.id) {
|
||||
// throttle
|
||||
logger.debug('on db update', workspaceId);
|
||||
|
||||
Reference in New Issue
Block a user