Design & Implement Electron provider in AFFiNE
Per client PRD, the desktop data provider should be able to do this:
- CRUD workspaces and sync data into local db (SQLite db files)
- this workspace can opt in with AFFiNE cloud
After the refactor of AFFiNE, there are two main data abstraction layers: WorkspacePlugin and blocksuite Provider. To add a Electron datasource we need to add one Electron workspace plugin & provider as well.
Some findings so far:
There are two WorkspacePlugin flavours for now: local and affine, and it will be picked based on current active workspace. Each plugin will be responsible for managing workspaces.
export interface WorkspacePlugin<Flavour extends WorkspaceFlavour> {
flavour: Flavour;
// Plugin will be loaded according to the priority
loadPriority: LoadPriority;
// fixme: this is a hack
cleanup?: () => void;
// Fetch necessary data for the first render
CRUD: WorkspaceCRUD<Flavour>;
UI: WorkspaceUISchema<Flavour>;
}
Each WorkspacePlugin have a list of providers. The providers are mainly for initializing connections for collaboration.
For the Electron app, the local type may refer to Electron instead, and we may not need IDB provider. In this case, the provider will call the Electron commands instead. The required electron commands are pending for design.
Also, the commands will also invoke the octobase-node bindings. We need another ticket for this one.
For this case, you should implement a new workspace called desktop workspace or something else. And use the onTransformWorkspace function to trans data from affine or local to desktop.
Dynamic mount or unmount provider(s) will add extra logic for each workspace. Which makes test, UI/UX harder
The design of this infra is to remind you that each workspace type should have its logic and UI. not suggest you make flywire or re-use old workspace
The implementation workflow should be
- implement a
LocalDatabaseProvider(id, yDoc)using octobase-node, and test it invitest - implement
DesktopWorkspaceCRUDusing octobase-node, and test it invitest - implement
DesktopWorkspaceUIand test it instorybook - combine them all into a new workspace plugin
- add
transformWorkspace(from=Local/Affine, to=Desktop)and test it inplaywright,vitest - test case for all parts
@Himself65 Thanks for your suggestions! I think the general requirements for a desktop (provider) are:
- every workspace will be saved locally, which could replace IDB on desktop
- a workspace can be prompted to cloud
- a workspace can also be de-synced and make it offline again by logging off. We should keep it locally other than removing it
- be able to load workspace offline even if user logs in
- as a sub requirement: loading local provider (let's call it SQLite provider?) & cloud provider simultaneously. Cloud provider should not block loading
I believe that these rules can also be applied to IDB. In my opinion, IDB should work similarly to IDB in many aspects that they do not need to watch for DB changes and only need to write to it after first download, with the exception that the user could choose where to save the database file in the filesystem for SQLite.
By the way, I think that the design of the onTransformWorkspace function does not fit well with YJS's philosophy of plug-and-play providers. I guess the reason of having it is because when prompting a workspace to cloud we will always create a new workspace with different ID?