feat: folder web (#4580)

* chore: folder wasm

* chore: folder wasm

* chore: resolve deps

* chore: fix trait

* chore: try localset

* chore: fix

* chore: fix

* chore: fix

* chore: async init sdk

* chore: fix test

* chore: fix test
This commit is contained in:
Nathan.fooo 2024-02-04 05:50:23 +08:00 committed by GitHub
parent 08938b8c70
commit fda70ff560
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
65 changed files with 818 additions and 558 deletions

View File

@ -50,6 +50,7 @@ APP_ENVIRONMENT = "local"
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
WEB_LIB_PATH= "appflowy_web/wasm-libs/af-wasm"
# Test default config
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib"

View File

@ -29,8 +29,16 @@ class FlowySDK {
Future<void> init(String configuration) async {
final port = RustStreamReceiver.shared.port;
ffi.set_stream_port(port);
ffi.store_dart_post_cobject(NativeApi.postCObject);
ffi.init_sdk(configuration.toNativeUtf8());
// final completer = Completer<Uint8List>();
// // Create a SendPort that accepts only one message.
// final sendPort = singleCompletePort(completer);
final code = ffi.init_sdk(0, configuration.toNativeUtf8());
if (code != 0) {
throw Exception('Failed to initialize the SDK');
}
// return completer.future;
}
}

View File

@ -77,17 +77,20 @@ typedef _invoke_sync_Dart = Pointer<Uint8> Function(
/// C function `init_sdk`.
int init_sdk(
int port,
Pointer<ffi.Utf8> data,
) {
return _init_sdk(data);
return _init_sdk(port, data);
}
final _init_sdk_Dart _init_sdk =
_dart_ffi_lib.lookupFunction<_init_sdk_C, _init_sdk_Dart>('init_sdk');
typedef _init_sdk_C = Int64 Function(
Int64 port,
Pointer<ffi.Utf8> path,
);
typedef _init_sdk_Dart = int Function(
int port,
Pointer<ffi.Utf8> path,
);

View File

@ -3,7 +3,7 @@
#include <stdint.h>
#include <stdlib.h>
int64_t init_sdk(char *data);
int64_t init_sdk(int64_t port, char *data);
void async_event(int64_t port, const uint8_t *input, uintptr_t len);

View File

@ -1930,6 +1930,7 @@ dependencies = [
name = "flowy-folder"
version = "0.1.0"
dependencies = [
"async-trait",
"bytes",
"chrono",
"collab",

View File

@ -1,5 +1,7 @@
use flowy_core::config::AppFlowyCoreConfig;
use flowy_core::{AppFlowyCore, DEFAULT_NAME};
use lib_dispatch::runtime::AFPluginRuntime;
use std::sync::Arc;
pub fn init_flowy_core() -> AppFlowyCore {
let config_json = include_str!("../tauri.conf.json");
@ -26,5 +28,8 @@ pub fn init_flowy_core() -> AppFlowyCore {
DEFAULT_NAME.to_string(),
)
.log_filter("trace", vec!["appflowy_tauri".to_string()]);
AppFlowyCore::new(config)
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone();
runtime.block_on(async move { AppFlowyCore::new(config, cloned_runtime).await })
}

View File

@ -1,2 +0,0 @@
export * from './observer';
export * from './parser';

View File

@ -1,34 +0,0 @@
import { listen, UnlistenFn } from "@tauri-apps/api/event";
import { SubscribeObject } from "../models/flowy-notification";
import { NotificationParser } from "./parser";
export abstract class AFNotificationObserver<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) => {
const object: SubscribeObject = SubscribeObject.fromObject(notification.payload as {});
if (this.parser?.id !== undefined) {
if (object.id === this.parser.id) {
this.parser?.parse(object);
}
} else {
this.parser?.parse(object);
}
});
}
async stop() {
if (this._listener !== undefined) {
// call the unlisten function before setting it to undefined
this._listener();
this._listener = undefined;
}
this.parser = null;
}
}

View File

@ -1,44 +0,0 @@
import { FlowyError } from "../models/flowy-error";
import { SubscribeObject } from "../models/flowy-notification";
import { Err, Ok, Result } from "ts-results";
export declare type OnNotificationPayload<T> = (ty: T, payload: Result<Uint8Array, FlowyError>) => 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>;
tyParser: NotificationTyParser<T>;
protected constructor(
onPayload: OnNotificationPayload<T>,
tyParser: NotificationTyParser<T>,
id?: string
) {
this.id = id;
this.onPayload = onPayload;
this.tyParser = tyParser;
}
parse(subject: SubscribeObject) {
if (typeof this.id !== "undefined" && this.id.length === 0) {
if (subject.id !== this.id) {
return;
}
}
const ty = this.tyParser(subject.ty);
if (ty === null) {
return;
}
if (subject.has_error) {
const error = FlowyError.deserializeBinary(subject.error);
this.onPayload(ty, Err(error));
} else {
this.onPayload(ty, Ok(subject.payload));
}
}
}

View File

@ -1,18 +1,73 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
// https://eslint.org/docs/latest/use/configure/configuration-files
env: {
browser: true,
es6: true,
node: true,
},
}
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
tsconfigRootDir: __dirname,
extraFileExtensions: ['.json'],
},
plugins: ['@typescript-eslint', "react-hooks"],
rules: {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/no-empty-function': 'error',
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/no-namespace': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/prefer-for-of': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
'@typescript-eslint/unified-signatures': 'error',
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'off',
'constructor-super': 'error',
eqeqeq: ['error', 'always'],
'no-cond-assign': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-empty': [
'error',
{
allowEmptyCatch: true,
},
],
'no-invalid-this': 'error',
'no-new-wrappers': 'error',
'no-param-reassign': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unsafe-finally': 'error',
'no-unused-labels': 'error',
'no-var': 'error',
'no-void': 'off',
'prefer-const': 'error',
'prefer-spread': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
}
],
'padding-line-between-statements': [
"error",
{ blankLine: "always", prev: ["const", "let", "var"], next: "*"},
{ blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]},
{ blankLine: "always", prev: "import", next: "*" },
{ blankLine: "any", prev: "import", next: "import" },
{ blankLine: "always", prev: "block-like", next: "*" },
{ blankLine: "always", prev: "block", next: "*" },
]
},
ignorePatterns: ['src/**/*.test.ts', '**/__tests__/**/*.json', 'package.json']
};

View File

@ -10,6 +10,7 @@
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"clean": "cargo make --cwd .. web_clean",
"test": "cargo test && wasm-pack test --headless",
"preview": "vite preview"
},
"dependencies": {
@ -34,6 +35,7 @@
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.0.8",
"vite-plugin-wasm": "^3.3.0"
"vite-plugin-wasm": "^3.3.0",
"rimraf": "^5.0.5"
}
}

View File

@ -54,6 +54,9 @@ devDependencies:
eslint-plugin-react-refresh:
specifier: ^0.4.5
version: 0.4.5(eslint@8.55.0)
rimraf:
specifier: ^5.0.5
version: 5.0.5
typescript:
specifier: ^5.2.2
version: 5.2.2
@ -559,6 +562,18 @@ packages:
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
dev: true
/@isaacs/cliui@8.0.2:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
string-width-cjs: /string-width@4.2.3
strip-ansi: 7.1.0
strip-ansi-cjs: /strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
dev: true
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
engines: {node: '>=6.0.0'}
@ -610,6 +625,13 @@ packages:
fastq: 1.16.0
dev: true
/@pkgjs/parseargs@0.11.0:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
requiresBuild: true
dev: true
optional: true
/@rollup/rollup-android-arm-eabi@4.9.2:
resolution: {integrity: sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==}
cpu: [arm]
@ -962,6 +984,11 @@ packages:
engines: {node: '>=8'}
dev: true
/ansi-regex@6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
dev: true
/ansi-styles@3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
engines: {node: '>=4'}
@ -976,6 +1003,11 @@ packages:
color-convert: 2.0.1
dev: true
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: true
/argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
dev: true
@ -996,6 +1028,12 @@ packages:
concat-map: 0.0.1
dev: true
/brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: true
/braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
@ -1112,10 +1150,22 @@ packages:
esutils: 2.0.3
dev: true
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
/electron-to-chromium@1.4.620:
resolution: {integrity: sha512-a2fcSHOHrqBJsPNXtf6ZCEZpXrFCcbK1FBxfX3txoqWzNgtEDG1f3M59M98iwxhRW4iMKESnSjbJ310/rkrp0g==}
dev: true
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
/emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
/esbuild@0.19.11:
resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==}
engines: {node: '>=12'}
@ -1341,6 +1391,14 @@ packages:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
dev: true
/foreground-child@3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
dependencies:
cross-spawn: 7.0.3
signal-exit: 4.1.0
dev: true
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
@ -1372,6 +1430,18 @@ packages:
is-glob: 4.0.3
dev: true
/glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.6
minimatch: 9.0.3
minipass: 7.0.4
path-scurry: 1.10.1
dev: true
/glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
dependencies:
@ -1459,6 +1529,11 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: true
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
@ -1480,6 +1555,15 @@ packages:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
/jackspeak@2.3.6:
resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
engines: {node: '>=14'}
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
dev: true
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -1546,6 +1630,11 @@ packages:
js-tokens: 4.0.0
dev: false
/lru-cache@10.2.0:
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
engines: {node: 14 || >=16.14}
dev: true
/lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies:
@ -1578,6 +1667,18 @@ packages:
brace-expansion: 1.1.11
dev: true
/minimatch@9.0.3:
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minipass@7.0.4:
resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
engines: {node: '>=16 || 14 >=14.17'}
dev: true
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
@ -1650,6 +1751,14 @@ packages:
engines: {node: '>=8'}
dev: true
/path-scurry@1.10.1:
resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
engines: {node: '>=16 || 14 >=14.17'}
dependencies:
lru-cache: 10.2.0
minipass: 7.0.4
dev: true
/path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
@ -1731,6 +1840,14 @@ packages:
glob: 7.2.3
dev: true
/rimraf@5.0.5:
resolution: {integrity: sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==}
engines: {node: '>=14'}
hasBin: true
dependencies:
glob: 10.3.10
dev: true
/rollup@4.9.2:
resolution: {integrity: sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -1789,6 +1906,11 @@ packages:
engines: {node: '>=8'}
dev: true
/signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
dev: true
/slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@ -1799,6 +1921,24 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: true
/string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.1.0
dev: true
/strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
@ -1806,6 +1946,13 @@ packages:
ansi-regex: 5.0.1
dev: true
/strip-ansi@7.1.0:
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.0.1
dev: true
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
@ -1950,6 +2097,24 @@ packages:
isexe: 2.0.0
dev: true
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
dev: true
/wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.1.0
dev: true
/wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true

View File

@ -5,9 +5,9 @@ import { useEffect } from "react";
import { initApp } from "@/application/app.ts";
import { subscribeNotification } from "@/application/notification.ts";
import { NotifyArgs } from "./@types/global";
import {init_tracing_log, init_wasm_core} from "../wasm-libs/af-wasm/pkg";
import { init_tracing_log, init_wasm_core } from "../wasm-libs/af-wasm/pkg";
import { v4 as uuidv4 } from 'uuid';
import {AddUserPB, UserWasmEventAddUser} from "@/services/backend/events/af-user";
import {AddUserPB, UserWasmEventAddUser} from "@/services/backend/events/user";
init_tracing_log();
// FIXME: handle the promise that init_wasm_core returns

View File

@ -1,2 +1,5 @@
export * from "./models/af-user";
export * from "./models/flowy-error";
export * from "./models/flowy-folder";
export * from "./models/flowy-document";
export * from "./models/flowy-notification";

View File

@ -45,7 +45,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cipher",
"cpufeatures",
]
@ -110,14 +110,18 @@ dependencies = [
name = "af-wasm"
version = "0.1.0"
dependencies = [
"af-persistence",
"af-user",
"collab",
"collab-integrate",
"console_error_panic_hook",
"flowy-document",
"flowy-error",
"flowy-folder",
"flowy-notification",
"flowy-server",
"flowy-server-pub",
"flowy-storage",
"flowy-user-pub",
"js-sys",
"lazy_static",
@ -136,6 +140,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-bindgen-test",
"web-sys",
"wee_alloc",
]
[[package]]
@ -155,7 +160,7 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"getrandom 0.2.12",
"once_cell",
"version_check",
@ -284,7 +289,7 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
@ -467,6 +472,12 @@ dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -771,7 +782,7 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"wasm-bindgen",
]
@ -840,7 +851,7 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -917,7 +928,7 @@ version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"hashbrown",
"lock_api",
"once_cell",
@ -1054,7 +1065,7 @@ version = "0.8.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -1295,6 +1306,39 @@ dependencies = [
"validator",
]
[[package]]
name = "flowy-folder"
version = "0.1.0"
dependencies = [
"async-trait",
"bytes",
"chrono",
"collab",
"collab-entity",
"collab-folder",
"collab-integrate",
"collab-plugins",
"flowy-codegen",
"flowy-derive",
"flowy-error",
"flowy-folder-pub",
"flowy-notification",
"lazy_static",
"lib-dispatch",
"lib-infra",
"nanoid",
"parking_lot 0.12.1",
"protobuf",
"serde_json",
"strum_macros 0.21.1",
"tokio",
"tokio-stream",
"tracing",
"unicode-segmentation",
"uuid",
"validator",
]
[[package]]
name = "flowy-folder-pub"
version = "0.1.0"
@ -1577,7 +1621,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
@ -1590,7 +1634,7 @@ version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
@ -1955,7 +1999,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152"
dependencies = [
"accessory",
"cfg-if",
"cfg-if 1.0.0",
"delegate-display",
"fancy_constructor",
"js-sys",
@ -2002,7 +2046,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -2141,7 +2185,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"windows-sys 0.48.0",
]
@ -2231,7 +2275,7 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"proc-macro2",
"quote",
"syn 2.0.48",
@ -2293,6 +2337,12 @@ version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memory_units"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
[[package]]
name = "mime"
version = "0.3.17"
@ -2452,7 +2502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
dependencies = [
"bitflags 2.4.2",
"cfg-if",
"cfg-if 1.0.0",
"foreign-types",
"libc",
"once_cell",
@ -2536,7 +2586,7 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall 0.2.16",
@ -2550,7 +2600,7 @@ version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"redox_syscall 0.4.1",
"smallvec",
@ -2853,7 +2903,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"opaque-debug",
"universal-hash",
@ -3621,7 +3671,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
@ -3638,7 +3688,7 @@ version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest",
]
@ -3875,7 +3925,7 @@ version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"fastrand",
"redox_syscall 0.4.1",
"rustix",
@ -3971,7 +4021,7 @@ version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"once_cell",
]
@ -4465,7 +4515,7 @@ version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
@ -4490,7 +4540,7 @@ version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
@ -4611,6 +4661,18 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wee_alloc"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
dependencies = [
"cfg-if 0.1.10",
"libc",
"memory_units",
"winapi",
]
[[package]]
name = "which"
version = "4.4.2"
@ -4801,7 +4863,7 @@ version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"windows-sys 0.48.0",
]

View File

@ -24,6 +24,8 @@ flowy-error = { path = "../../rust-lib/flowy-error" }
flowy-derive = { path = "../../rust-lib/build-tool/flowy-derive" }
flowy-codegen = { path = "../../rust-lib/build-tool/flowy-codegen" }
flowy-document = { path = "../../rust-lib/flowy-document" }
flowy-folder = { path = "../../rust-lib/flowy-folder" }
flowy-storage = { path = "../../rust-lib/flowy-storage" }
lib-infra = { path = "../../rust-lib/lib-infra" }
collab = { version = "0.1.0" }
collab-entity = { version = "0.1.0" }
@ -40,6 +42,7 @@ wasm-bindgen-futures = "0.4.40"
serde-wasm-bindgen = "0.4"
[profile.dev]
opt-level = 0
lto = false
@ -50,11 +53,6 @@ lto = true
opt-level = 3
codegen-units = 1
[profile.profiling]
inherits = "release"
debug = true
codegen-units = 16
lto = false
[patch.crates-io]
# Please using the following command to update the revision id

View File

@ -4,8 +4,6 @@ version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
indexed_db_futures = { version = "0.4" }

View File

@ -4,8 +4,6 @@ version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
af-persistence.workspace = true

View File

@ -1,16 +1,15 @@
use flowy_codegen::Project;
fn main() {
let crate_name = env!("CARGO_PKG_NAME");
flowy_codegen::protobuf_file::ts_gen(
crate_name,
env!("CARGO_PKG_NAME"),
"user",
Project::Web {
relative_path: "../../../".to_string(),
},
);
flowy_codegen::ts_event::gen(
crate_name,
"user",
Project::Web {
relative_path: "../../../".to_string(),
},

View File

@ -0,0 +1,20 @@
use af_persistence::store::AppFlowyWASMStore;
use flowy_error::FlowyResult;
use flowy_user_pub::session::Session;
use std::rc::Rc;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct AuthenticateUser {
session: Arc<RwLock<Option<Session>>>,
store: Rc<AppFlowyWASMStore>,
}
impl AuthenticateUser {
pub async fn new(store: Rc<AppFlowyWASMStore>) -> FlowyResult<Self> {
Ok(Self {
session: Arc::new(RwLock::new(None)),
store,
})
}
}

View File

@ -1,5 +1,5 @@
use crate::entities::*;
use crate::manager::UserManagerWASM;
use crate::manager::UserManager;
use flowy_error::{FlowyError, FlowyResult};
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
use lib_infra::box_any::BoxAny;
@ -8,7 +8,7 @@ use std::rc::{Rc, Weak};
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn oauth_sign_in_handler(
data: AFPluginData<OauthSignInPB>,
manager: AFPluginState<Weak<UserManagerWASM>>,
manager: AFPluginState<Weak<UserManager>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
@ -19,7 +19,7 @@ pub async fn oauth_sign_in_handler(
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn add_user_handler(
data: AFPluginData<AddUserPB>,
manager: AFPluginState<Weak<UserManagerWASM>>,
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
@ -30,7 +30,7 @@ pub async fn add_user_handler(
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn sign_in_with_password_handler(
data: AFPluginData<UserSignInPB>,
manager: AFPluginState<Weak<UserManagerWASM>>,
manager: AFPluginState<Weak<UserManager>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
@ -40,9 +40,7 @@ pub async fn sign_in_with_password_handler(
data_result_ok(UserProfilePB::from(user_profile))
}
fn upgrade_manager(
manager: AFPluginState<Weak<UserManagerWASM>>,
) -> FlowyResult<Rc<UserManagerWASM>> {
fn upgrade_manager(manager: AFPluginState<Weak<UserManager>>) -> FlowyResult<Rc<UserManager>> {
let manager = manager
.upgrade()
.ok_or(FlowyError::internal().with_context("The user session is already drop"))?;

View File

@ -1,12 +1,12 @@
use crate::event_handler::*;
use crate::manager::UserManagerWASM;
use crate::manager::UserManager;
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
use lib_dispatch::prelude::AFPlugin;
use std::rc::Weak;
use strum_macros::Display;
#[rustfmt::skip]
pub fn init(user_manager: Weak<UserManagerWASM>) -> AFPlugin {
pub fn init(user_manager: Weak<UserManager>) -> AFPlugin {
AFPlugin::new()
.name("Flowy-User")
.state(user_manager)

View File

@ -1,3 +1,4 @@
pub mod authenticate_user;
mod define;
pub mod entities;
mod event_handler;

View File

@ -1,3 +1,4 @@
use crate::authenticate_user::AuthenticateUser;
use crate::define::{user_profile_key, user_workspace_key, AF_USER_SESSION_KEY};
use af_persistence::store::{AppFlowyWASMStore, IndexddbStore};
use anyhow::Context;
@ -37,22 +38,26 @@ pub trait UserCallback {
) -> Fut<FlowyResult<()>>;
}
pub struct UserManagerWASM {
pub struct UserManager {
device_id: String,
pub(crate) store: Rc<AppFlowyWASMStore>,
pub(crate) cloud_services: Rc<dyn UserCloudServiceProvider>,
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
pub(crate) store: Rc<AppFlowyWASMStore>,
user_callbacks: Vec<Rc<dyn UserCallback>>,
pub(crate) authenticate_user: Rc<AuthenticateUser>,
#[allow(dead_code)]
pub(crate) user_awareness: Rc<Mutex<Option<MutexUserAwareness>>>,
pub(crate) collab_db: Arc<CollabKVDB>,
user_callbacks: Vec<Rc<dyn UserCallback>>,
}
impl UserManagerWASM {
impl UserManager {
pub async fn new(
device_id: &str,
store: Rc<AppFlowyWASMStore>,
cloud_services: Rc<dyn UserCloudServiceProvider>,
authenticate_user: Rc<AuthenticateUser>,
collab_builder: Weak<AppFlowyCollabBuilder>,
) -> Result<Self, FlowyError> {
let device_id = device_id.to_string();
@ -63,6 +68,7 @@ impl UserManagerWASM {
cloud_services,
collab_builder,
store,
authenticate_user,
user_callbacks: vec![],
user_awareness: Rc::new(Default::default()),
collab_db,

View File

@ -20,12 +20,15 @@ collab-integrate = { workspace = true }
tokio-stream.workspace = true
af-user.workspace = true
af-persistence.workspace = true
flowy-storage = { workspace = true }
flowy-notification = { workspace = true, features = ["web_ts"] }
flowy-user-pub = { workspace = true }
flowy-server = { workspace = true }
flowy-server-pub = { workspace = true }
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "web_ts"] }
flowy-document = { workspace = true, features = ["web_ts"] }
flowy-folder = { workspace = true, features = ["web_ts"] }
lib-infra = { workspace = true }
collab = { workspace = true, features = ["async-plugin"] }
web-sys = "0.3"
@ -34,9 +37,23 @@ uuid.workspace = true
serde-wasm-bindgen.workspace = true
js-sys = "0.3.67"
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. However, it is slower than the default
# allocator, so it's not enabled by default.
wee_alloc = { version = "0.4.2", optional = true }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so it's only enabled
# in debug mode.
[target."cfg(debug_assertions)".dependencies]
console_error_panic_hook = "0.1.5"
[dev-dependencies]
wasm-bindgen-test = "0.3.40"
tokio = { version = "1.0", features = ["sync"] }
[features]
#default = ["wee_alloc"]
localhost_dev = []

View File

@ -1,8 +1,15 @@
use crate::deps_resolve::document_deps::DocumentDepsResolver;
use crate::deps_resolve::folder_deps::FolderDepsResolver;
use crate::integrate::server::ServerProviderWASM;
use af_user::manager::UserManagerWASM;
use af_persistence::store::AppFlowyWASMStore;
use af_user::authenticate_user::AuthenticateUser;
use af_user::manager::UserManager;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_document::manager::DocumentManager;
use flowy_error::FlowyResult;
use flowy_folder::manager::FolderManager;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_storage::ObjectStorageService;
use lib_dispatch::prelude::AFPluginDispatcher;
use lib_dispatch::runtime::AFPluginRuntime;
use std::rc::Rc;
@ -11,7 +18,9 @@ use std::sync::Arc;
pub struct AppFlowyWASMCore {
pub collab_builder: Arc<AppFlowyCollabBuilder>,
pub event_dispatcher: Rc<AFPluginDispatcher>,
pub user_manager: Rc<UserManagerWASM>,
pub user_manager: Rc<UserManager>,
pub folder_manager: Rc<FolderManager>,
pub document_manager: Rc<DocumentManager>,
}
impl AppFlowyWASMCore {
@ -23,10 +32,31 @@ impl AppFlowyWASMCore {
device_id.to_string(),
));
let store = Rc::new(AppFlowyWASMStore::new().await?);
let auth_user = Rc::new(AuthenticateUser::new(store.clone()).await?);
let document_manager = DocumentDepsResolver::resolve(
Rc::downgrade(&auth_user),
collab_builder.clone(),
server_provider.clone(),
Rc::downgrade(&(server_provider.clone() as Rc<dyn ObjectStorageService>)),
)
.await;
let folder_manager = FolderDepsResolver::resolve(
Rc::downgrade(&auth_user),
document_manager.clone(),
collab_builder.clone(),
server_provider.clone(),
)
.await;
let user_manager = Rc::new(
UserManagerWASM::new(
UserManager::new(
device_id,
store,
server_provider.clone(),
auth_user,
Arc::downgrade(&collab_builder),
)
.await?,
@ -40,6 +70,8 @@ impl AppFlowyWASMCore {
collab_builder,
event_dispatcher,
user_manager,
folder_manager,
document_manager,
})
}
}

View File

@ -0,0 +1,19 @@
use crate::integrate::server::ServerProviderWASM;
use af_user::authenticate_user::AuthenticateUser;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_document::manager::DocumentManager;
use flowy_storage::ObjectStorageService;
use std::rc::{Rc, Weak};
use std::sync::Arc;
pub struct DocumentDepsResolver;
impl DocumentDepsResolver {
pub async fn resolve(
authenticate_user: Weak<AuthenticateUser>,
collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Rc<ServerProviderWASM>,
storage_service: Weak<dyn ObjectStorageService>,
) -> Rc<DocumentManager> {
todo!()
}
}

View File

@ -0,0 +1,20 @@
use crate::integrate::server::ServerProviderWASM;
use af_user::authenticate_user::AuthenticateUser;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_document::manager::DocumentManager;
use flowy_folder::manager::FolderManager;
use std::rc::{Rc, Weak};
use std::sync::Arc;
pub struct FolderDepsResolver;
impl FolderDepsResolver {
pub async fn resolve(
authenticate_user: Weak<AuthenticateUser>,
document_manager: Rc<DocumentManager>,
collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Rc<ServerProviderWASM>,
) -> Rc<FolderManager> {
todo!()
}
}

View File

@ -0,0 +1,2 @@
pub(crate) mod document_deps;
pub(crate) mod folder_deps;

View File

@ -6,11 +6,10 @@ use flowy_error::FlowyError;
use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::AppFlowyServer;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_user_pub::cloud::{
UserCloudService, UserCloudServiceProvider, UserCloudServiceProviderBase,
};
use flowy_storage::{ObjectIdentity, ObjectStorageService, ObjectValue};
use flowy_user_pub::cloud::{UserCloudService, UserCloudServiceProvider};
use flowy_user_pub::entities::{Authenticator, UserTokenState};
use lib_infra::future::{to_fut, Fut};
use lib_infra::future::{to_fut, Fut, FutureResult};
use parking_lot::RwLock;
use std::rc::Rc;
use std::sync::Arc;
@ -64,9 +63,7 @@ impl CollabCloudPluginProvider for ServerProviderWASM {
}
}
impl UserCloudServiceProvider for ServerProviderWASM {}
impl UserCloudServiceProviderBase for ServerProviderWASM {
impl UserCloudServiceProvider for ServerProviderWASM {
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
self.get_server().set_token(token)?;
Ok(())
@ -104,3 +101,21 @@ impl UserCloudServiceProviderBase for ServerProviderWASM {
self.config.base_url.clone()
}
}
impl ObjectStorageService for ServerProviderWASM {
fn get_object_url(&self, object_id: ObjectIdentity) -> FutureResult<String, FlowyError> {
todo!()
}
fn put_object(&self, url: String, object_value: ObjectValue) -> FutureResult<(), FlowyError> {
todo!()
}
fn delete_object(&self, url: String) -> FutureResult<(), FlowyError> {
todo!()
}
fn get_object(&self, url: String) -> FutureResult<ObjectValue, FlowyError> {
todo!()
}
}

View File

@ -4,6 +4,7 @@ use std::cell::RefCell;
use std::rc::Rc;
pub mod core;
mod deps_resolve;
mod integrate;
pub mod notification;
@ -23,6 +24,10 @@ lazy_static! {
static ref APPFLOWY_CORE: RefCellAppFlowyCore = RefCellAppFlowyCore::new();
}
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
@ -37,6 +42,10 @@ pub fn init_tracing_log() {
#[wasm_bindgen]
pub fn init_wasm_core() -> js_sys::Promise {
// It's disabled in release mode so it doesn't bloat up the file size.
#[cfg(debug_assertions)]
console_error_panic_hook::set_once();
#[cfg(feature = "localhost_dev")]
let config = AFCloudConfiguration {
base_url: "http://localhost".to_string(),

View File

@ -7,6 +7,11 @@ use flowy_error::FlowyResult;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use parking_lot::Once;
use flowy_document::deps::DocumentData;
use flowy_document::entities::{CreateDocumentPayloadPB, DocumentDataPB, OpenDocumentPayloadPB};
use flowy_document::event_map::DocumentEvent;
use flowy_folder::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB};
use flowy_folder::event_map::FolderEvent;
use std::sync::Arc;
use uuid::Uuid;
@ -48,6 +53,44 @@ impl WASMEventTester {
.parse::<UserProfilePB>();
Ok(user_profile)
}
pub async fn create_and_open_document(&self, parent_id: &str) -> ViewPB {
let payload = CreateViewPayloadPB {
parent_view_id: parent_id.to_string(),
name,
desc: "".to_string(),
thumbnail: None,
layout: ViewLayoutPB::Document,
initial_data,
meta: Default::default(),
set_as_current: true,
index: None,
};
let view = self
.event_builder()
.event(FolderEvent::CreateView)
.payload(payload)
.async_send()
.await
.parse::<ViewPB>();
let payload = OpenDocumentPayloadPB {
document_id: view.id.clone(),
};
let _ = self
.event_builder()
.event(DocumentEvent::OpenDocument)
.payload(payload)
.async_send()
.await
.parse::<DocumentDataPB>();
view
}
fn event_builder(&self) -> EventBuilder {
EventBuilder::new(self.core.clone())
}
}
pub fn unique_email() -> String {

View File

@ -1920,6 +1920,7 @@ dependencies = [
name = "flowy-folder"
version = "0.1.0"
dependencies = [
"async-trait",
"bytes",
"chrono",
"collab",

View File

@ -76,7 +76,7 @@ pub fn dart_gen(crate_name: &str) {
}
#[allow(unused_variables)]
pub fn ts_gen(crate_name: &str, project: Project) {
pub fn ts_gen(crate_name: &str, dest_folder_name: &str, project: Project) {
// 1. generate the proto files to proto_file_dir
#[cfg(feature = "proto_gen")]
let proto_crates = gen_proto_files(crate_name);
@ -116,7 +116,7 @@ pub fn ts_gen(crate_name: &str, project: Project) {
// 2. generate the protobuf files(Dart)
#[cfg(feature = "ts")]
generate_ts_protobuf_files(
crate_name,
dest_folder_name,
&proto_file_output_path,
&proto_file_paths,
&file_names,

View File

@ -13,7 +13,7 @@ use std::path::PathBuf;
use syn::Item;
use walkdir::WalkDir;
pub fn gen(crate_name: &str, project: Project) {
pub fn gen(dest_folder_name: &str, project: Project) {
let root = project.event_root();
let backend_service_path = project.dst();
@ -40,7 +40,7 @@ pub fn gen(crate_name: &str, project: Project) {
}
render_result.push_str(TS_FOOTER);
let ts_event_folder: PathBuf = [&root, &backend_service_path, "events", crate_name]
let ts_event_folder: PathBuf = [&root, &backend_service_path, "events", dest_folder_name]
.iter()
.collect();
if !ts_event_folder.as_path().exists() {
@ -82,7 +82,10 @@ pub fn gen(crate_name: &str, project: Project) {
Ok(ref mut file) => {
let mut export = String::new();
export.push_str("// Auto-generated, do not edit \n");
export.push_str(&format!("export * from '../../models/{}';\n", crate_name));
export.push_str(&format!(
"export * from '../../models/{}';\n",
dest_folder_name
));
export.push_str(&format!("export * from './{}';\n", event_file));
file.write_all(export.as_bytes()).unwrap();
File::flush(file).unwrap();

View File

@ -3,7 +3,7 @@
#include <stdint.h>
#include <stdlib.h>
int64_t init_sdk(char *data);
int64_t init_sdk(int64_t port, char *data);
void async_event(int64_t port, const uint8_t *input, uintptr_t len);

View File

@ -13,6 +13,7 @@ use flowy_notification::{register_notification_sender, unregister_all_notificati
use flowy_server_pub::AuthenticatorType;
use lib_dispatch::prelude::ToBytes;
use lib_dispatch::prelude::*;
use lib_dispatch::runtime::AFPluginRuntime;
use crate::appflowy_yaml::save_appflowy_cloud_config;
use crate::env_serde::AppFlowyDartConfiguration;
@ -52,7 +53,9 @@ unsafe impl Sync for MutexAppFlowyCore {}
unsafe impl Send for MutexAppFlowyCore {}
#[no_mangle]
pub extern "C" fn init_sdk(data: *mut c_char) -> i64 {
pub extern "C" fn init_sdk(_port: i64, data: *mut c_char) -> i64 {
// and sent it the `Rust's` result
// no need to convert anything :)
let c_str = unsafe { CStr::from_ptr(data) };
let serde_str = c_str.to_str().unwrap();
let configuration = AppFlowyDartConfiguration::from_str(serde_str);
@ -78,7 +81,13 @@ pub extern "C" fn init_sdk(data: *mut c_char) -> i64 {
core.close_db();
}
*APPFLOWY_CORE.0.lock() = Some(AppFlowyCore::new(config));
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone();
// let isolate = allo_isolate::Isolate::new(port);
*APPFLOWY_CORE.0.lock() = runtime.block_on(async move {
Some(AppFlowyCore::new(config, cloned_runtime).await)
// isolate.post("".to_string());
});
0
}

View File

@ -19,6 +19,7 @@ use flowy_notification::register_notification_sender;
use flowy_server::AppFlowyServer;
use flowy_user::entities::AuthenticatorPB;
use flowy_user::errors::FlowyError;
use lib_dispatch::runtime::AFPluginRuntime;
use crate::user_event::TestNotificationSender;
@ -134,19 +135,14 @@ pub fn document_from_document_doc_state(doc_id: &str, doc_state: CollabDocState)
Document::from_doc_state(CollabOrigin::Empty, doc_state, doc_id, vec![]).unwrap()
}
#[cfg(target_arch = "wasm32")]
async fn init_core(config: AppFlowyCoreConfig) -> AppFlowyCore {
// let runtime = tokio::runtime::Runtime::new().unwrap();
// let local_set = tokio::task::LocalSet::new();
// runtime.block_on(AppFlowyCore::new(config))
AppFlowyCore::new(config).await
}
#[cfg(not(target_arch = "wasm32"))]
async fn init_core(config: AppFlowyCoreConfig) -> AppFlowyCore {
std::thread::spawn(|| AppFlowyCore::new(config))
.join()
.unwrap()
std::thread::spawn(|| {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone();
runtime.block_on(async move { AppFlowyCore::new(config, cloned_runtime).await })
})
.join()
.unwrap()
}
impl std::ops::Deref for EventIntegrationTest {

View File

@ -8,6 +8,10 @@ fn main() {
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
}
}

View File

@ -60,9 +60,9 @@ dart = [
]
ts = [
"flowy-user/tauri_ts",
"flowy-folder/ts",
"flowy-folder/tauri_ts",
"flowy-database2/ts",
"flowy-config/tauri_ts",
]
rev-sqlite = ["flowy-user/rev-sqlite"]
openssl_vendored = ["flowy-sqlite/openssl_vendored"]
openssl_vendored = ["flowy-sqlite/openssl_vendored"]

View File

@ -24,9 +24,7 @@ use flowy_folder_pub::cloud::{
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_server_pub::supabase_config::SupabaseConfiguration;
use flowy_storage::ObjectValue;
use flowy_user_pub::cloud::{
UserCloudService, UserCloudServiceProvider, UserCloudServiceProviderBase,
};
use flowy_user_pub::cloud::{UserCloudService, UserCloudServiceProvider};
use flowy_user_pub::entities::{Authenticator, UserTokenState};
use lib_infra::future::{to_fut, Fut, FutureResult};
@ -65,9 +63,8 @@ impl ObjectStorageService for ServerProvider {
})
}
}
impl UserCloudServiceProvider for ServerProvider {}
impl UserCloudServiceProviderBase for ServerProvider {
impl UserCloudServiceProvider for ServerProvider {
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
let server = self.get_server()?;
server.set_token(token)?;

View File

@ -10,7 +10,7 @@ use flowy_document::manager::DocumentManager;
use flowy_error::FlowyResult;
use flowy_folder::manager::{FolderInitDataSource, FolderManager};
use flowy_user::event_map::UserStatusCallback;
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProviderBase};
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProvider};
use flowy_user_pub::entities::{Authenticator, UserProfile, UserWorkspace};
use lib_infra::future::{to_fut, Fut};

View File

@ -2,9 +2,7 @@
use flowy_storage::ObjectStorageService;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
use tracing::{debug, error, event, info, instrument};
@ -53,19 +51,10 @@ pub struct AppFlowyCore {
}
impl AppFlowyCore {
#[cfg(target_arch = "wasm32")]
pub async fn new(config: AppFlowyCoreConfig) -> Self {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
pub async fn new(config: AppFlowyCoreConfig, runtime: Arc<AFPluginRuntime>) -> Self {
Self::init(config, runtime).await
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new(config: AppFlowyCoreConfig) -> Self {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone();
runtime.block_on(Self::init(config, cloned_runtime))
}
pub fn close_db(&self) {
self.user_manager.close_db();
}

View File

@ -9,6 +9,6 @@ fn main() {
#[cfg(feature = "ts")]
{
flowy_codegen::ts_event::gen(crate_name, flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(crate_name, flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(crate_name, crate_name, flowy_codegen::Project::Tauri);
}
}

View File

@ -8,6 +8,10 @@ fn main() {
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
}
}

View File

@ -8,19 +8,24 @@ fn main() {
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
}
#[cfg(feature = "web_ts")]
{
flowy_codegen::ts_event::gen(
env!("CARGO_PKG_NAME"),
"document",
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},
);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
"document",
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},

View File

@ -3,11 +3,16 @@ fn main() {
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
#[cfg(feature = "tauri_ts")]
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
#[cfg(feature = "web_ts")]
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
"error",
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},

View File

@ -22,7 +22,7 @@ flowy-error = { path = "../flowy-error", features = ["impl_from_dispatch_error"]
lib-dispatch = { workspace = true }
bytes.workspace = true
lib-infra = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tokio = { workspace = true, features = ["sync"] }
nanoid = "0.4.0"
lazy_static = "1.4.0"
chrono = { workspace = true, default-features = false, features = ["clock"] }
@ -32,11 +32,13 @@ uuid.workspace = true
tokio-stream = { workspace = true, features = ["sync"] }
serde_json.workspace = true
validator = "0.16.0"
async-trait.workspace = true
[build-dependencies]
flowy-codegen.workspace = true
[features]
dart = ["flowy-codegen/dart", "flowy-notification/dart"]
ts = ["flowy-codegen/ts", "flowy-notification/tauri_ts"]
tauri_ts = ["flowy-codegen/ts", "flowy-notification/tauri_ts"]
web_ts = ["flowy-codegen/ts", "flowy-notification/web_ts"]
test_helper = []

View File

@ -5,9 +5,30 @@ fn main() {
flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME"));
}
#[cfg(feature = "ts")]
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
}
#[cfg(feature = "web_ts")]
{
flowy_codegen::ts_event::gen(
"folder",
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},
);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
"folder",
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},
);
}
}

View File

@ -15,7 +15,7 @@ use collab_integrate::{CollabKVDB, CollabPersistenceConfig};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_pub::cloud::{gen_view_id, FolderCloudService};
use flowy_folder_pub::folder_builder::ParentChildViews;
use lib_infra::async_trait::async_trait;
use lib_infra::conditional_send_sync_trait;
use crate::entities::icon::UpdateViewIconParams;
use crate::entities::{
@ -35,11 +35,12 @@ use crate::util::{
};
use crate::view_operation::{create_view, FolderOperationHandler, FolderOperationHandlers};
/// [FolderUser] represents the user for folder.
#[async_trait]
pub trait FolderUser: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
conditional_send_sync_trait! {
"[crate::manager::FolderUser] represents the user for folder.";
FolderUser {
fn user_id(&self) -> Result<i64, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
}
}
pub struct FolderManager {

View File

@ -12,7 +12,6 @@ use crate::manager_observer::{
subscribe_folder_trash_changed, subscribe_folder_view_changed,
};
use crate::user_default::DefaultFolderBuilder;
use crate::util::is_exist_in_local_disk;
impl FolderManager {
/// Called immediately after the application launched if the user already sign in/sign up.
@ -47,7 +46,7 @@ impl FolderManager {
FolderInitDataSource::LocalDisk {
create_if_not_exist,
} => {
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
let is_exist = self.is_workspace_exist_in_local(uid, &workspace_id).await;
if is_exist {
self
.open_local_folder(uid, &workspace_id, collab_db, folder_notifier)
@ -104,6 +103,15 @@ impl FolderManager {
Ok(())
}
async fn is_workspace_exist_in_local(&self, uid: i64, workspace_id: &str) -> bool {
if let Ok(weak_collab) = self.user.collab_db(uid) {
if let Some(collab_db) = weak_collab.upgrade() {
return collab_db.is_exist(uid, workspace_id).await.unwrap_or(false);
}
}
false
}
async fn create_default_folder(
&self,
uid: i64,

View File

@ -1,31 +1,13 @@
use collab_folder::Folder;
use collab_integrate::CollabKVAction;
use collab_plugins::local_storage::kv::KVTransactionDB;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_pub::folder_builder::ParentChildViews;
use std::sync::Arc;
use tracing::{event, instrument};
use crate::entities::UserFolderPB;
use crate::manager::FolderUser;
use collab_folder::Folder;
use flowy_error::{ErrorCode, FlowyError};
use flowy_folder_pub::folder_builder::ParentChildViews;
use tracing::{event, instrument};
pub(crate) fn folder_not_init_error() -> FlowyError {
FlowyError::internal().with_context("Folder not initialized")
}
pub(crate) fn is_exist_in_local_disk(
user: &Arc<dyn FolderUser>,
doc_id: &str,
) -> FlowyResult<bool> {
let uid = user.user_id()?;
if let Some(collab_db) = user.collab_db(uid)?.upgrade() {
let read_txn = collab_db.read_txn();
Ok(read_txn.is_exist(uid, doc_id))
} else {
Ok(false)
}
}
pub(crate) fn workspace_data_not_sync_error(uid: i64, workspace_id: &str) -> FlowyError {
FlowyError::from(ErrorCode::WorkspaceDataNotSync).with_payload(UserFolderPB {
uid,

View File

@ -3,11 +3,16 @@ fn main() {
flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME"));
#[cfg(feature = "tauri_ts")]
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
#[cfg(feature = "web_ts")]
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
"notification",
flowy_codegen::Project::Web {
relative_path: "../../".to_string(),
},

View File

@ -2,7 +2,6 @@ pub use server::*;
pub mod af_cloud;
pub mod local_server;
// mod request;
mod response;
mod server;

View File

@ -1,198 +0,0 @@
use std::{sync::Arc, time::Duration};
use bytes::Bytes;
use hyper::http;
use reqwest::{header::HeaderMap, Client, Method, Response};
use tokio::sync::oneshot;
use flowy_error::{internal_error, FlowyError};
use crate::af_cloud::configuration::HEADER_TOKEN;
use crate::response::HttpResponse;
pub trait ResponseMiddleware {
fn receive_response(&self, token: &Option<String>, response: &HttpResponse);
}
pub struct HttpRequestBuilder {
url: String,
body: Option<Bytes>,
response: Option<Bytes>,
headers: HeaderMap,
method: Method,
middleware: Vec<Arc<dyn ResponseMiddleware + Send + Sync>>,
}
impl std::default::Default for HttpRequestBuilder {
fn default() -> Self {
Self {
url: "".to_owned(),
body: None,
response: None,
headers: HeaderMap::new(),
method: Method::GET,
middleware: Vec::new(),
}
}
}
impl HttpRequestBuilder {
pub fn new() -> Self {
HttpRequestBuilder::default()
}
#[allow(dead_code)]
pub fn middleware<T>(mut self, middleware: Arc<T>) -> Self
where
T: 'static + ResponseMiddleware + Send + Sync,
{
self.middleware.push(middleware);
self
}
pub fn get(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::GET;
self
}
pub fn post(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::POST;
self
}
pub fn patch(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::PATCH;
self
}
pub fn delete(mut self, url: &str) -> Self {
self.url = url.to_owned();
self.method = Method::DELETE;
self
}
pub fn header(mut self, key: &'static str, value: &str) -> Self {
self.headers.insert(key, value.parse().unwrap());
self
}
pub fn json<T>(self, body: T) -> Result<Self, FlowyError>
where
T: serde::Serialize,
{
let bytes = Bytes::from(serde_json::to_vec(&body)?);
self.bytes(bytes)
}
pub fn bytes(mut self, body: Bytes) -> Result<Self, FlowyError> {
self.body = Some(body);
Ok(self)
}
pub async fn send(self) -> Result<(), FlowyError> {
let _ = self.inner_send().await?;
Ok(())
}
pub async fn response<T>(self) -> Result<T, FlowyError>
where
T: serde::de::DeserializeOwned,
{
let builder = self.inner_send().await?;
match builder.response {
None => Err(unexpected_empty_payload(&builder.url)),
Some(data) => {
let value = serde_json::from_slice(&data)?;
Ok(value)
},
}
}
fn token(&self) -> Option<String> {
match self.headers.get(HEADER_TOKEN) {
None => None,
Some(header) => match header.to_str() {
Ok(val) => Some(val.to_owned()),
Err(_) => None,
},
}
}
async fn inner_send(mut self) -> Result<Self, FlowyError> {
let (tx, rx) = oneshot::channel::<Result<Response, _>>();
let url = self.url.clone();
let body = self.body.take();
let method = self.method.clone();
let headers = self.headers.clone();
// reqwest client is not 'Sync' but channel is.
tokio::spawn(async move {
let client = default_client();
let mut builder = client.request(method.clone(), url).headers(headers);
if let Some(body) = body {
builder = builder.body(body);
}
let response = builder.send().await;
let _ = tx.send(response);
});
let response = rx.await.map_err(internal_error)?;
tracing::trace!("Http Response: {:?}", response);
let flowy_response = flowy_response_from(response?).await?;
let token = self.token();
self.middleware.iter().for_each(|middleware| {
middleware.receive_response(&token, &flowy_response);
});
match flowy_response.error {
None => {
self.response = Some(flowy_response.data);
Ok(self)
},
Some(error) => Err(FlowyError::new(error.code, &error.msg)),
}
}
}
fn unexpected_empty_payload(url: &str) -> FlowyError {
let msg = format!("Request: {} receives unexpected empty payload", url);
FlowyError::payload_none().with_context(msg)
}
async fn flowy_response_from(original: Response) -> Result<HttpResponse, FlowyError> {
let bytes = original.bytes().await?;
let response: HttpResponse = serde_json::from_slice(&bytes)?;
Ok(response)
}
#[allow(dead_code)]
async fn get_response_data(original: Response) -> Result<Bytes, FlowyError> {
if original.status() == http::StatusCode::OK {
let bytes = original.bytes().await?;
let response: HttpResponse = serde_json::from_slice(&bytes)?;
match response.error {
None => Ok(response.data),
Some(error) => Err(FlowyError::new(error.code, &error.msg)),
}
} else {
Err(FlowyError::http().with_context(original))
}
}
fn default_client() -> Client {
let result = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(500))
.timeout(Duration::from_secs(5))
.build();
match result {
Ok(client) => client,
Err(e) => {
tracing::error!("Create reqwest client failed: {}", e);
reqwest::Client::new()
},
}
}

View File

@ -12,7 +12,7 @@ use bytes::Bytes;
use flowy_error::FlowyError;
use lib_infra::future::FutureResult;
use lib_infra::{if_native, if_wasm};
use lib_infra::{conditional_send_sync_trait, if_native, if_wasm};
use mime::Mime;
pub struct ObjectIdentity {
@ -26,50 +26,49 @@ pub struct ObjectValue {
pub raw: Bytes,
pub mime: Mime,
}
conditional_send_sync_trait! {
"Provides a service for object storage. The trait includes methods for CRUD operations on storage objects.";
ObjectStorageService {
/// Creates a new storage object.
///
/// # Parameters
/// - `url`: url of the object to be created.
///
/// # Returns
/// - `Ok()`
/// - `Err(Error)`: An error occurred during the operation.
fn get_object_url(&self, object_id: ObjectIdentity) -> FutureResult<String, FlowyError>;
/// Provides a service for object storage.
///
/// The trait includes methods for CRUD operations on storage objects.
pub trait ObjectStorageService: Send + Sync + 'static {
/// Creates a new storage object.
///
/// # Parameters
/// - `url`: url of the object to be created.
///
/// # Returns
/// - `Ok()`
/// - `Err(Error)`: An error occurred during the operation.
fn get_object_url(&self, object_id: ObjectIdentity) -> FutureResult<String, FlowyError>;
/// Creates a new storage object.
///
/// # Parameters
/// - `url`: url of the object to be created.
///
/// # Returns
/// - `Ok()`
/// - `Err(Error)`: An error occurred during the operation.
fn put_object(&self, url: String, object_value: ObjectValue) -> FutureResult<(), FlowyError>;
/// Creates a new storage object.
///
/// # Parameters
/// - `url`: url of the object to be created.
///
/// # Returns
/// - `Ok()`
/// - `Err(Error)`: An error occurred during the operation.
fn put_object(&self, url: String, object_value: ObjectValue) -> FutureResult<(), FlowyError>;
/// Deletes a storage object by its URL.
///
/// # Parameters
/// - `url`: url of the object to be deleted.
///
/// # Returns
/// - `Ok()`
/// - `Err(Error)`: An error occurred during the operation.
fn delete_object(&self, url: String) -> FutureResult<(), FlowyError>;
/// Deletes a storage object by its URL.
///
/// # Parameters
/// - `url`: url of the object to be deleted.
///
/// # Returns
/// - `Ok()`
/// - `Err(Error)`: An error occurred during the operation.
fn delete_object(&self, url: String) -> FutureResult<(), FlowyError>;
/// Fetches a storage object by its URL.
///
/// # Parameters
/// - `url`: url of the object
///
/// # Returns
/// - `Ok(File)`: The returned file object.
/// - `Err(Error)`: An error occurred during the operation.
fn get_object(&self, url: String) -> FutureResult<ObjectValue, FlowyError>;
/// Fetches a storage object by its URL.
///
/// # Parameters
/// - `url`: url of the object
///
/// # Returns
/// - `Ok(File)`: The returned file object.
/// - `Err(Error)`: An error occurred during the operation.
fn get_object(&self, url: String) -> FutureResult<ObjectValue, FlowyError>;
}
}
pub trait FileStoragePlan: Send + Sync + 'static {

View File

@ -14,8 +14,8 @@ use uuid::Uuid;
use flowy_error::{ErrorCode, FlowyError};
use lib_infra::box_any::BoxAny;
use lib_infra::conditional_send_sync_trait;
use lib_infra::future::FutureResult;
use lib_infra::{if_native, if_wasm};
use crate::entities::{
AuthResponse, Authenticator, Role, UpdateUserProfileParams, UserCredentials, UserProfile,
@ -57,21 +57,12 @@ impl Display for UserCloudConfig {
}
}
if_native! {
pub trait UserCloudServiceProvider: UserCloudServiceProviderBase + Send + Sync + 'static {}
}
conditional_send_sync_trait! {
"This trait is intended for implementation by providers that offer cloud-based services for users.
It includes methods for handling authentication tokens, enabling/disabling synchronization,
setting network reachability, managing encryption secrets, and accessing user-specific cloud services.";
if_wasm! {
pub trait UserCloudServiceProvider: UserCloudServiceProviderBase + 'static {}
}
/// `UserCloudServiceProvider` defines a set of methods for managing user cloud services,
/// including token management, synchronization settings, network reachability, and authentication.
///
/// This trait is intended for implementation by providers that offer cloud-based services for users.
/// It includes methods for handling authentication tokens, enabling/disabling synchronization,
/// setting network reachability, managing encryption secrets, and accessing user-specific cloud services.
pub trait UserCloudServiceProviderBase {
UserCloudServiceProvider {
/// Sets the authentication token for the cloud service.
///
/// # Arguments
@ -126,8 +117,8 @@ pub trait UserCloudServiceProviderBase {
/// # Returns
/// A `String` representing the service URL.
fn service_url(&self) -> String;
}
}
/// Provide the generic interface for the user cloud service
/// The user cloud service is responsible for the user authentication and user profile management
#[allow(unused_variables)]

View File

@ -8,6 +8,10 @@ fn main() {
#[cfg(feature = "tauri_ts")]
{
flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri);
flowy_codegen::protobuf_file::ts_gen(
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
flowy_codegen::Project::Tauri,
);
}
}

View File

@ -44,5 +44,6 @@ futures-util = "0.3.26"
default = ["use_protobuf"]
use_serde = ["bincode", "serde_json", "serde", "serde_repr"]
use_protobuf= ["protobuf"]
local_set = []

View File

@ -16,51 +16,51 @@ use crate::{
service::{AFPluginServiceFactory, Service},
};
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub trait AFConcurrent {}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
impl<T> AFConcurrent for T where T: ?Sized {}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub trait AFConcurrent: Send + Sync {}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
impl<T> AFConcurrent for T where T: Send + Sync {}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub type AFBoxFuture<'a, T> = futures_core::future::LocalBoxFuture<'a, T>;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub type AFBoxFuture<'a, T> = futures_core::future::BoxFuture<'a, T>;
pub type AFStateMap = std::sync::Arc<AFPluginStateMap>;
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub(crate) fn downcast_owned<T: 'static>(boxed: AFBox) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub(crate) fn downcast_owned<T: 'static + Send + Sync>(boxed: AFBox) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed)
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub(crate) type AFBox = Box<dyn Any>;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub(crate) type AFBox = Box<dyn Any + Send + Sync>;
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub type BoxFutureCallback =
Box<dyn FnOnce(AFPluginEventResponse) -> AFBoxFuture<'static, ()> + 'static>;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub type BoxFutureCallback =
Box<dyn FnOnce(AFPluginEventResponse) -> AFBoxFuture<'static, ()> + Send + Sync + 'static>;
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub fn af_spawn<T>(future: T) -> tokio::task::JoinHandle<T::Output>
where
T: Future + 'static,
@ -69,7 +69,7 @@ where
tokio::task::spawn_local(future)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub fn af_spawn<T>(future: T) -> tokio::task::JoinHandle<T::Output>
where
T: Future + Send + 'static,
@ -170,15 +170,6 @@ impl AFPluginDispatcher {
callback: Some(Box::new(callback)),
};
// Spawns a future onto the runtime.
//
// This spawns the given future onto the runtime's executor, usually a
// thread pool. The thread pool is then responsible for polling the future
// until it completes.
//
// The provided future will start running in the background immediately
// when `spawn` is called, even if you don't await the returned
// `JoinHandle`.
let handle = dispatch.runtime.spawn(async move {
service.call(service_ctx).await.unwrap_or_else(|e| {
tracing::error!("Dispatch runtime error: {:?}", e);
@ -186,17 +177,35 @@ impl AFPluginDispatcher {
})
});
let runtime = dispatch.runtime.clone();
DispatchFuture {
fut: Box::pin(async move {
let result = runtime.run_until(handle).await;
result.unwrap_or_else(|e| {
let msg = format!("EVENT_DISPATCH join error: {:?}", e);
tracing::error!("{}", msg);
let error = InternalError::JoinError(msg);
error.as_response()
})
}),
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
{
let result = dispatch.runtime.block_on(handle);
DispatchFuture {
fut: Box::pin(async move {
result.unwrap_or_else(|e| {
let msg = format!("EVENT_DISPATCH join error: {:?}", e);
tracing::error!("{}", msg);
let error = InternalError::JoinError(msg);
error.as_response()
})
}),
}
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
{
let runtime = dispatch.runtime.clone();
DispatchFuture {
fut: Box::pin(async move {
let result = runtime.run_until(handle).await;
result.unwrap_or_else(|e| {
let msg = format!("EVENT_DISPATCH join error: {:?}", e);
tracing::error!("{}", msg);
let error = InternalError::JoinError(msg);
error.as_response()
})
}),
}
}
}
@ -212,7 +221,7 @@ impl AFPluginDispatcher {
))
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> tokio::task::JoinHandle<F::Output>
where
@ -221,7 +230,7 @@ impl AFPluginDispatcher {
self.runtime.spawn(future)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> tokio::task::JoinHandle<F::Output>
where
@ -231,7 +240,7 @@ impl AFPluginDispatcher {
self.runtime.spawn(future)
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub async fn run_until<F>(&self, future: F) -> F::Output
where
F: Future + 'static,
@ -240,7 +249,7 @@ impl AFPluginDispatcher {
self.runtime.run_until(handle).await.unwrap()
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub async fn run_until<'a, F>(&self, future: F) -> F::Output
where
F: Future + Send + 'a,

View File

@ -8,14 +8,14 @@ use tokio::task::JoinHandle;
pub struct AFPluginRuntime {
inner: Runtime,
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
local: tokio::task::LocalSet,
}
impl Display for AFPluginRuntime {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if cfg!(target_arch = "wasm32") {
write!(f, "Runtime(single_thread)")
if cfg!(any(target_arch = "wasm32", feature = "local_set")) {
write!(f, "Runtime(current_thread)")
} else {
write!(f, "Runtime(multi_thread)")
}
@ -27,12 +27,12 @@ impl AFPluginRuntime {
let inner = default_tokio_runtime()?;
Ok(Self {
inner,
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
local: tokio::task::LocalSet::new(),
})
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
@ -41,7 +41,7 @@ impl AFPluginRuntime {
self.local.spawn_local(future)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
#[track_caller]
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
where
@ -51,7 +51,7 @@ impl AFPluginRuntime {
self.inner.spawn(future)
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub async fn run_until<F>(&self, future: F) -> F::Output
where
F: Future,
@ -59,7 +59,7 @@ impl AFPluginRuntime {
self.local.run_until(future).await
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub async fn run_until<F>(&self, future: F) -> F::Output
where
F: Future,
@ -67,7 +67,7 @@ impl AFPluginRuntime {
future.await
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
#[track_caller]
pub fn block_on<F>(&self, f: F) -> F::Output
where
@ -76,7 +76,7 @@ impl AFPluginRuntime {
self.local.block_on(&self.inner, f)
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
#[track_caller]
pub fn block_on<F>(&self, f: F) -> F::Output
where
@ -86,14 +86,14 @@ impl AFPluginRuntime {
}
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub fn default_tokio_runtime() -> io::Result<Runtime> {
runtime::Builder::new_current_thread()
.thread_name("dispatch-rt-st")
.build()
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub fn default_tokio_runtime() -> io::Result<Runtime> {
runtime::Builder::new_multi_thread()
.thread_name("dispatch-rt-mt")

View File

@ -16,7 +16,7 @@ where
BoxServiceFactory(Box::new(FactoryWrapper(factory)))
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
type Inner<Cfg, Req, Res, Err> = Box<
dyn AFPluginServiceFactory<
Req,
@ -27,7 +27,7 @@ type Inner<Cfg, Req, Res, Err> = Box<
Future = AFBoxFuture<'static, Result<BoxService<Req, Res, Err>, Err>>,
>,
>;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
type Inner<Cfg, Req, Res, Err> = Box<
dyn AFPluginServiceFactory<
Req,
@ -58,12 +58,12 @@ where
}
}
#[cfg(target_arch = "wasm32")]
#[cfg(any(target_arch = "wasm32", feature = "local_set"))]
pub type BoxService<Req, Res, Err> = Box<
dyn Service<Req, Response = Res, Error = Err, Future = AFBoxFuture<'static, Result<Res, Err>>>,
>;
#[cfg(not(target_arch = "wasm32"))]
#[cfg(all(not(target_arch = "wasm32"), not(feature = "local_set")))]
pub type BoxService<Req, Res, Err> = Box<
dyn Service<Req, Response = Res, Error = Err, Future = AFBoxFuture<'static, Result<Res, Err>>>
+ Sync

View File

@ -13,6 +13,21 @@ macro_rules! if_wasm {
$item
)*}
}
// Define a generic macro to conditionally apply Send and Sync traits with documentation
#[macro_export]
macro_rules! conditional_send_sync_trait {
($doc:expr; $trait_name:ident { $( $item:tt )* }) => {
// For wasm32 targets, define the trait without Send + Sync
#[doc = $doc]
#[cfg(target_arch = "wasm32")]
pub trait $trait_name { $( $item )* }
// For non-wasm32 targets, define the trait with Send + Sync
#[doc = $doc]
#[cfg(not(target_arch = "wasm32"))]
pub trait $trait_name: Send + Sync { $( $item )* }
};
}
pub fn move_vec_element<T, F>(
vec: &mut Vec<T>,

View File

@ -26,9 +26,18 @@ run_task = { name = [
"rm_rust_generated_files",
"rm_web_generated_protobuf_files",
"rm_web_generated_event_files",
"rm_pkg",
] }
[tasks.rm_pkg]
private = true
script = ["""
cd ${WEB_LIB_PATH}
rimraf dist pkg
"""]
script_runner = "@duckscript"
[tasks.rm_web_generated_protobuf_files]
private = true
script = ["""