chore: listen backend notification from Tauri (#1743)

This commit is contained in:
Nathan.fooo 2023-01-27 17:17:51 +08:00 committed by GitHub
parent 243a781b6c
commit 7a750e5255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 237 additions and 58 deletions

View File

@ -106,6 +106,16 @@
"preLaunchTask": "AF: Tauri UI Dev",
"cwd": "${workspaceRoot}/appflowy_tauri/"
},
// {
// "type": "lldb",
// "request": "launch",
// "name": "AF-tauri: Production Debug",
// "cargo": {
// "args": ["build", "--release", "--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml"]
// },
// "preLaunchTask": "AF: Tauri UI Build",
// "cwd": "${workspaceRoot}/appflowy_tauri/"
// },
{
"name": "AF: Debug Rust",
"request": "attach",

View File

@ -9,6 +9,16 @@
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": false,
},
"[javascript]": {
"editor.formatOnSave": true,
"editor.rulers": [80],
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.rulers": [80],
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"svgviewer.enableautopreview": true,
"svgviewer.previewcolumn": "Active",
"svgviewer.showzoominout": true,

View File

@ -190,7 +190,16 @@
"label": "AF: Tauri UI Dev",
"type": "shell",
"isBackground": true,
"command": "yarn dev",
"command": "npm run dev",
"problemMatcher": ["$tsc"],
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri UI Build",
"type": "shell",
"command": "npm run build",
"problemMatcher": ["$tsc"],
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"

View File

@ -29,9 +29,10 @@ fn main() {
});
})
.setup(|app| {
let window = app.get_window("main").unwrap();
#[cfg(debug_assertions)]
window.open_devtools();
if cfg!(debug_assertions) {
let window = app.get_window("main").unwrap();
window.open_devtools();
}
Ok(())
})
.run(tauri::generate_context!())

View File

@ -5,7 +5,6 @@ use tauri::{AppHandle, Event, Manager, Wry};
#[allow(dead_code)]
pub const AF_EVENT: &str = "af-event";
#[allow(dead_code)]
pub const AF_NOTIFICATION: &str = "af-notification";
#[tracing::instrument(level = "trace")]

View File

@ -4,25 +4,33 @@ import {
SignInPayloadPB,
} from "../services/backend/events/flowy-user/index";
import { nanoid } from "nanoid";
import { UserNotificationListener } from "./components/user/application/notifications";
function App() {
async function greet() {
async function sendSignInEvent() {
let make_payload = () =>
SignInPayloadPB.fromObject({
email: nanoid(4) + "@gmail.com",
password: "A!@123abc",
name: "abc",
});
let listener = await new UserNotificationListener("", (userProfile) => {
console.log(userProfile);
listener.stop();
});
listener.start();
await UserEventSignIn(make_payload());
}
return (
<div className="container">
<h1>Welcome to AppFlowy!</h1>
<button type="button" onClick={() => greet()}>
Sign in
<button type="button" onClick={() => sendSignInEvent()}>
Test Sign In Event
</button>
</div>
);

View File

@ -0,0 +1 @@
export * from "./user_listener";

View File

@ -0,0 +1,27 @@
import { Result } from "ts-results/result";
import { UserNotification, FlowyError } from "../../../../../services/backend";
import { NotificationParser, OnNotificationError } from "../../../../../services/backend/notifications/parser";
declare type UserNotificationCallback = (ty: UserNotification, payload: Uint8Array) => void;
export class UserNotificationParser extends NotificationParser<UserNotification> {
constructor(callback: UserNotificationCallback, id?: String, onError?: OnNotificationError) {
super(
callback,
(ty) => {
let notification = UserNotification[ty];
if (isUserNotification(notification)) {
return UserNotification[notification];
} else {
return UserNotification.Unknown;
}
},
id,
onError
);
}
}
const isUserNotification = (notification: string): notification is keyof typeof UserNotification => {
return Object.values(UserNotification).indexOf(notification) !== -1;
};

View File

@ -0,0 +1,34 @@
import { FlowyError, UserNotification, UserProfilePB } from "../../../../../services/backend";
import { AFNotificationListener, OnNotificationError } from "../../../../../services/backend/notifications";
import { UserNotificationParser } from "./parser";
declare type OnUserProfileUpdate = (userProfile: UserProfilePB) => void;
export class UserNotificationListener extends AFNotificationListener<UserNotification> {
onProfileUpdate?: OnUserProfileUpdate;
constructor(userId?: String, onProfileUpdate?: OnUserProfileUpdate, onError?: OnNotificationError) {
let parser = new UserNotificationParser(
(notification, payload) => {
switch (notification) {
case UserNotification.UserAuthChanged:
break;
case UserNotification.UserProfileUpdated:
break;
case UserNotification.UserUnauthorized:
break;
case UserNotification.UserSignIn:
let userProfile = UserProfilePB.deserializeBinary(payload);
this.onProfileUpdate?.(userProfile);
break;
default:
break;
}
},
userId,
onError
);
super(parser);
this.onProfileUpdate = onProfileUpdate;
}
}

View File

@ -0,0 +1,2 @@
export * from "./listener";
export * from "./parser";

View File

@ -0,0 +1,29 @@
import { listen, UnlistenFn } from "@tauri-apps/api/event";
import { FlowyError } from "../classes/flowy-error";
import { SubscribeObject } from "../classes/flowy-notification";
import { NotificationParser } from "./parser";
declare type OnError = (error: FlowyError) => void;
export abstract class AFNotificationListener<T> {
parser?: NotificationParser<T> | null;
private _listener?: UnlistenFn;
protected constructor(parser?: NotificationParser<T>) {
this.parser = parser;
}
async start() {
this._listener = await listen("af-notification", (notification) => {
let object = SubscribeObject.fromObject(notification.payload as {});
this.parser?.parse(object);
});
}
async stop() {
if (this._listener != null) {
this._listener();
}
this.parser = null;
}
}

View File

@ -0,0 +1,42 @@
import { Ok, Err, Result } from "ts-results/result";
import { FlowyError } from "../classes/flowy-error";
import { SubscribeObject } from "../classes/flowy-notification";
export declare type OnNotificationPayload<T> = (ty: T, payload: Uint8Array) => void;
export declare type OnNotificationError = (error: FlowyError) => void;
export declare type NotificationTyParser<T> = (num: number) => T | null;
export declare type ErrParser<E> = (data: Uint8Array) => E;
export abstract class NotificationParser<T> {
id?: String;
onPayload: OnNotificationPayload<T>;
onError?: OnNotificationError;
tyParser: NotificationTyParser<T>;
constructor(onPayload: OnNotificationPayload<T>, tyParser: NotificationTyParser<T>, id?: String, onError?: OnNotificationError) {
this.id = id;
this.onPayload = onPayload;
this.onError = onError;
this.tyParser = tyParser;
}
parse(subject: SubscribeObject) {
if (typeof this.id !== "undefined" && this.id.length == 0) {
if (subject.id != this.id) {
return;
}
}
let ty = this.tyParser(subject.ty);
if (ty == null) {
return;
}
if (subject.has_error) {
let error = FlowyError.deserializeBinary(subject.error);
this.onError?.(error);
} else {
this.onPayload(ty, subject.payload);
}
}
}

View File

@ -5,7 +5,7 @@ use crate::{
entities::workspace::RepeatedWorkspacePB,
errors::FlowyResult,
event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
notification::{send_dart_notification, FolderNotification},
notification::{send_notification, FolderNotification},
services::{
folder_editor::FolderEditor, persistence::FolderPersistence, set_current_workspace, AppController,
TrashController, ViewController, WorkspaceController,
@ -249,7 +249,7 @@ impl DefaultFolderBuilder {
let repeated_workspace = RepeatedWorkspacePB {
items: vec![workspace_rev.into()],
};
send_dart_notification(token, FolderNotification::UserCreateWorkspace)
send_notification(token, FolderNotification::UserCreateWorkspace)
.payload(repeated_workspace)
.send();
Ok(())

View File

@ -33,11 +33,11 @@ impl std::convert::From<FolderNotification> for i32 {
}
#[tracing::instrument(level = "trace")]
pub(crate) fn send_dart_notification(id: &str, ty: FolderNotification) -> NotificationBuilder {
pub(crate) fn send_notification(id: &str, ty: FolderNotification) -> NotificationBuilder {
NotificationBuilder::new(id, ty, OBSERVABLE_CATEGORY)
}
#[tracing::instrument(level = "trace")]
pub(crate) fn send_anonymous_dart_notification(ty: FolderNotification) -> NotificationBuilder {
pub(crate) fn send_anonymous_notification(ty: FolderNotification) -> NotificationBuilder {
NotificationBuilder::new("", ty, OBSERVABLE_CATEGORY)
}

View File

@ -91,7 +91,7 @@ impl AppController {
})
.await?
.into();
send_dart_notification(&app_id, FolderNotification::AppUpdated)
send_notification(&app_id, FolderNotification::AppUpdated)
.payload(app)
.send();
self.update_app_on_server(params)?;
@ -163,7 +163,7 @@ impl AppController {
{
Ok(_) => {
let app: AppPB = app_rev.into();
send_dart_notification(&app.id, FolderNotification::AppUpdated)
send_notification(&app.id, FolderNotification::AppUpdated)
.payload(app)
.send();
}
@ -248,7 +248,7 @@ fn notify_apps_changed<'a>(
.map(|app_rev| app_rev.into())
.collect();
let repeated_app = RepeatedAppPB { items };
send_dart_notification(workspace_id, FolderNotification::WorkspaceAppsChanged)
send_notification(workspace_id, FolderNotification::WorkspaceAppsChanged)
.payload(repeated_app)
.send();
Ok(())

View File

@ -2,7 +2,7 @@ use crate::{
entities::trash::{RepeatedTrashIdPB, RepeatedTrashPB, TrashIdPB, TrashPB, TrashType},
errors::{FlowyError, FlowyResult},
event_map::{FolderCouldServiceV1, WorkspaceUser},
notification::{send_anonymous_dart_notification, FolderNotification},
notification::{send_anonymous_notification, FolderNotification},
services::persistence::{FolderPersistence, FolderPersistenceTransaction},
};
@ -283,7 +283,7 @@ impl TrashController {
fn notify_trash_changed<T: Into<RepeatedTrashPB>>(repeated_trash: T) {
let repeated_trash = repeated_trash.into();
tracing::Span::current().record("n_trash", repeated_trash.len());
send_anonymous_dart_notification(FolderNotification::TrashUpdated)
send_anonymous_notification(FolderNotification::TrashUpdated)
.payload(repeated_trash)
.send();
}

View File

@ -8,7 +8,7 @@ use crate::{
},
errors::{FlowyError, FlowyResult},
event_map::{FolderCouldServiceV1, WorkspaceUser},
notification::{send_dart_notification, FolderNotification},
notification::{send_notification, FolderNotification},
services::{
persistence::{FolderPersistence, FolderPersistenceTransaction, ViewChangeset},
TrashController, TrashEvent,
@ -225,7 +225,7 @@ impl ViewController {
})
.await?;
send_dart_notification(&view_id, FolderNotification::ViewMoveToTrash)
send_notification(&view_id, FolderNotification::ViewMoveToTrash)
.payload(deleted_view)
.send();
@ -291,7 +291,7 @@ impl ViewController {
transaction.update_view(changeset)?;
let view_rev = transaction.read_view(&view_id)?;
let view: ViewPB = view_rev.clone().into();
send_dart_notification(&view_id, FolderNotification::ViewUpdated)
send_notification(&view_id, FolderNotification::ViewUpdated)
.payload(view)
.send();
notify_views_changed(&view_rev.app_id, self.trash_controller.clone(), &transaction)?;
@ -356,7 +356,7 @@ impl ViewController {
{
Ok(_) => {
let view: ViewPB = view_rev.into();
send_dart_notification(&view.id, FolderNotification::ViewUpdated)
send_notification(&view.id, FolderNotification::ViewUpdated)
.payload(view)
.send();
}
@ -518,7 +518,7 @@ fn read_local_views_with_transaction<'a>(
}
fn notify_dart(view: ViewPB, notification: FolderNotification) {
send_dart_notification(&view.id, notification).payload(view).send();
send_notification(&view.id, notification).payload(view).send();
}
#[tracing::instrument(
@ -537,7 +537,7 @@ fn notify_views_changed<'a>(
app_rev.belongings.retain(|view| !trash_ids.contains(&view.id));
let app: AppPB = app_rev.into();
send_dart_notification(belong_to_id, FolderNotification::AppUpdated)
send_notification(belong_to_id, FolderNotification::AppUpdated)
.payload(app)
.send();

View File

@ -53,7 +53,7 @@ impl WorkspaceController {
.map(|workspace_rev| workspace_rev.into())
.collect();
let repeated_workspace = RepeatedWorkspacePB { items: workspaces };
send_dart_notification(&token, FolderNotification::UserCreateWorkspace)
send_notification(&token, FolderNotification::UserCreateWorkspace)
.payload(repeated_workspace)
.send();
set_current_workspace(&user_id, &workspace.id);
@ -73,7 +73,7 @@ impl WorkspaceController {
})
.await?;
send_dart_notification(&workspace_id, FolderNotification::WorkspaceUpdated)
send_notification(&workspace_id, FolderNotification::WorkspaceUpdated)
.payload(workspace)
.send();
self.update_workspace_on_server(params)?;
@ -92,7 +92,7 @@ impl WorkspaceController {
self.read_local_workspaces(None, &user_id, &transaction)
})
.await?;
send_dart_notification(&token, FolderNotification::UserDeleteWorkspace)
send_notification(&token, FolderNotification::UserDeleteWorkspace)
.payload(repeated_workspace)
.send();
self.delete_workspace_on_server(workspace_id)?;
@ -236,7 +236,7 @@ pub async fn notify_workspace_setting_did_change(
})
.await?;
send_dart_notification(&token, FolderNotification::WorkspaceSetting)
send_notification(&token, FolderNotification::WorkspaceSetting)
.payload(workspace_setting)
.send();
Ok(())

View File

@ -6,7 +6,7 @@ use crate::entities::{
use crate::{
errors::FlowyError,
manager::FolderManager,
notification::{send_dart_notification, FolderNotification},
notification::{send_notification, FolderNotification},
services::{get_current_workspace, read_local_workspace_apps, WorkspaceController},
};
use lib_dispatch::prelude::{data_result, AFPluginData, AFPluginState, DataResult};
@ -151,7 +151,7 @@ fn read_workspaces_on_server(
.collect(),
};
send_dart_notification(&token, FolderNotification::WorkspaceListUpdated)
send_notification(&token, FolderNotification::WorkspaceListUpdated)
.payload(repeated_workspace)
.send();
Result::<(), FlowyError>::Ok(())

View File

@ -35,6 +35,6 @@ impl std::convert::From<GridDartNotification> for i32 {
}
#[tracing::instrument(level = "trace")]
pub fn send_dart_notification(id: &str, ty: GridDartNotification) -> NotificationBuilder {
pub fn send_notification(id: &str, ty: GridDartNotification) -> NotificationBuilder {
NotificationBuilder::new(id, ty, OBSERVABLE_CATEGORY)
}

View File

@ -1,6 +1,6 @@
use crate::entities::{CellChangesetPB, InsertedRowPB, UpdatedRowPB};
use crate::manager::GridUser;
use crate::notification::{send_dart_notification, GridDartNotification};
use crate::notification::{send_notification, GridDartNotification};
use crate::services::block_editor::{GridBlockRevisionEditor, GridBlockRevisionMergeable};
use crate::services::persistence::block_index::BlockIndexCache;
use crate::services::persistence::rev_sqlite::{
@ -262,7 +262,7 @@ impl GridBlockManager {
async fn notify_did_update_cell(&self, changeset: CellChangesetPB) -> FlowyResult<()> {
let id = format!("{}:{}", changeset.row_id, changeset.field_id);
send_dart_notification(&id, GridDartNotification::DidUpdateCell).send();
send_notification(&id, GridDartNotification::DidUpdateCell).send();
Ok(())
}
}

View File

@ -1,7 +1,7 @@
use crate::entities::CellPathParams;
use crate::entities::*;
use crate::manager::GridUser;
use crate::notification::{send_dart_notification, GridDartNotification};
use crate::notification::{send_notification, GridDartNotification};
use crate::services::block_manager::GridBlockManager;
use crate::services::cell::{
apply_cell_data_changeset, decode_type_cell_data, stringify_cell_data, AnyTypeCache, AtomicCellDataCache,
@ -852,7 +852,7 @@ impl GridRevisionEditor {
let notified_changeset = GridFieldChangesetPB::update(&self.grid_id, vec![updated_field.clone()]);
self.notify_did_update_grid(notified_changeset).await?;
send_dart_notification(field_id, GridDartNotification::DidUpdateField)
send_notification(field_id, GridDartNotification::DidUpdateField)
.payload(updated_field)
.send();
}
@ -861,7 +861,7 @@ impl GridRevisionEditor {
}
async fn notify_did_update_grid(&self, changeset: GridFieldChangesetPB) -> FlowyResult<()> {
send_dart_notification(&self.grid_id, GridDartNotification::DidUpdateGridFields)
send_notification(&self.grid_id, GridDartNotification::DidUpdateGridFields)
.payload(changeset)
.send();
Ok(())

View File

@ -1,5 +1,5 @@
use crate::entities::{GridRowsVisibilityChangesetPB, ReorderAllRowsPB, ReorderSingleRowPB};
use crate::notification::{send_dart_notification, GridDartNotification};
use crate::notification::{send_notification, GridDartNotification};
use crate::services::filter::FilterResultNotification;
use crate::services::sort::{ReorderAllRowsResult, ReorderSingleRowResult};
use async_stream::stream;
@ -37,7 +37,7 @@ impl GridViewChangedReceiverRunner {
invisible_rows: notification.invisible_rows,
};
send_dart_notification(
send_notification(
&changeset.view_id,
GridDartNotification::DidUpdateGridViewRowsVisibility,
)
@ -48,7 +48,7 @@ impl GridViewChangedReceiverRunner {
let row_orders = ReorderAllRowsPB {
row_orders: notification.row_orders,
};
send_dart_notification(&notification.view_id, GridDartNotification::DidReorderRows)
send_notification(&notification.view_id, GridDartNotification::DidReorderRows)
.payload(row_orders)
.send()
}
@ -58,7 +58,7 @@ impl GridViewChangedReceiverRunner {
old_index: notification.old_index as i32,
new_index: notification.new_index as i32,
};
send_dart_notification(&notification.view_id, GridDartNotification::DidReorderSingleRow)
send_notification(&notification.view_id, GridDartNotification::DidReorderSingleRow)
.payload(reorder_row)
.send()
}

View File

@ -1,5 +1,5 @@
use crate::entities::*;
use crate::notification::{send_dart_notification, GridDartNotification};
use crate::notification::{send_notification, GridDartNotification};
use crate::services::block_manager::GridBlockEvent;
use crate::services::cell::{AtomicCellDataCache, TypeCellData};
use crate::services::field::{RowSingleCellData, TypeOptionCellDataHandler};
@ -184,7 +184,7 @@ impl GridViewRevisionEditor {
}
};
send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGridViewRows)
send_notification(&self.view_id, GridDartNotification::DidUpdateGridViewRows)
.payload(changeset)
.send();
}
@ -616,7 +616,7 @@ impl GridViewRevisionEditor {
debug_assert!(!changeset.is_empty());
if !changeset.is_empty() {
send_dart_notification(&changeset.view_id, GridDartNotification::DidGroupByNewField)
send_notification(&changeset.view_id, GridDartNotification::DidGroupByNewField)
.payload(changeset)
.send();
}
@ -630,33 +630,33 @@ impl GridViewRevisionEditor {
async fn notify_did_update_setting(&self) {
let setting = self.get_view_setting().await;
send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGridSetting)
send_notification(&self.view_id, GridDartNotification::DidUpdateGridSetting)
.payload(setting)
.send();
}
pub async fn notify_did_update_group_rows(&self, payload: GroupRowsNotificationPB) {
send_dart_notification(&payload.group_id, GridDartNotification::DidUpdateGroup)
send_notification(&payload.group_id, GridDartNotification::DidUpdateGroup)
.payload(payload)
.send();
}
pub async fn notify_did_update_filter(&self, notification: FilterChangesetNotificationPB) {
send_dart_notification(&notification.view_id, GridDartNotification::DidUpdateFilter)
send_notification(&notification.view_id, GridDartNotification::DidUpdateFilter)
.payload(notification)
.send();
}
pub async fn notify_did_update_sort(&self, notification: SortChangesetNotificationPB) {
if !notification.is_empty() {
send_dart_notification(&notification.view_id, GridDartNotification::DidUpdateSort)
send_notification(&notification.view_id, GridDartNotification::DidUpdateSort)
.payload(notification)
.send();
}
}
async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
send_dart_notification(&self.view_id, GridDartNotification::DidUpdateGroupView)
send_notification(&self.view_id, GridDartNotification::DidUpdateGroupView)
.payload(changeset)
.send();
}

View File

@ -9,6 +9,7 @@ pub(crate) enum UserNotification {
UserProfileUpdated = 2,
UserUnauthorized = 3,
UserWsConnectStateChanged = 4,
UserSignIn = 5,
}
impl std::default::Default for UserNotification {
@ -23,6 +24,10 @@ impl std::convert::From<UserNotification> for i32 {
}
}
pub(crate) fn dart_notify(id: &str, ty: UserNotification) -> NotificationBuilder {
pub(crate) fn send_notification(id: &str, ty: UserNotification) -> NotificationBuilder {
NotificationBuilder::new(id, ty, OBSERVABLE_CATEGORY)
}
pub(crate) fn send_sign_in_notification() -> NotificationBuilder {
NotificationBuilder::new("", UserNotification::UserSignIn, OBSERVABLE_CATEGORY)
}

View File

@ -84,7 +84,13 @@ impl UserSession {
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_in(&self, params: SignInParams) -> Result<UserProfilePB, FlowyError> {
if self.is_user_login(&params.email) {
self.get_user_profile().await
match self.get_user_profile().await {
Ok(profile) => {
send_sign_in_notification().payload(profile.clone()).send();
Ok(profile)
}
Err(err) => Err(err),
}
} else {
let resp = self.cloud_service.sign_in(params).await?;
let session: Session = resp.clone().into();
@ -92,6 +98,7 @@ impl UserSession {
let user_table = self.save_user(resp.into()).await?;
let user_profile: UserProfilePB = user_table.into();
self.notifier.notify_login(&user_profile.token, &user_profile.id);
send_sign_in_notification().payload(user_profile.clone()).send();
Ok(user_profile)
}
}
@ -134,7 +141,7 @@ impl UserSession {
diesel_update_table!(user_table, changeset, &*self.db_connection()?);
let user_profile = self.get_user_profile().await?;
dart_notify(&session.token, UserNotification::UserProfileUpdated)
send_notification(&session.token, UserNotification::UserProfileUpdated)
.payload(user_profile)
.send();
self.update_user_on_server(&session.token, params).await?;

View File

@ -1,11 +1,5 @@
[tasks.tauri_build]
script = ["""
cd appflowy_tauri/src-tauri
npm run tauri build
"""]
script_runner = "@shell"
[tasks.tauri_pb]
description = "Build the Tauri backend & Run Code Generation"
script = ["""
cd appflowy_tauri/src-tauri
cargo build
@ -20,6 +14,7 @@ script = ["""
script_runner = "@shell"
[tasks.tauri_clean]
description = "Remove all the building artifacts"
run_task = { name = [
"rust_lib_clean",
"rm_macro_build_cache",