chore: remove appflowy_web project (#5671)

* chore: remove appflowy_web project

* chore: remove ci
This commit is contained in:
Nathan.fooo 2024-07-01 22:49:24 +08:00 committed by GitHub
parent 2b8dca209e
commit 46896b5a2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 9 additions and 11174 deletions

View File

@ -1,83 +0,0 @@
name: WEB-CI
on:
workflow_dispatch:
inputs:
build:
description: 'Build the web app'
required: true
default: 'true'
env:
CARGO_TERM_COLOR: always
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.77.2"
CARGO_MAKE_VERSION: "0.36.6"
jobs:
web-build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache Rust Dependencies
uses: Swatinem/rust-cache@v2
with:
key: rust-dependencies-${{ runner.os }}
workspaces: |
frontend/rust-lib
frontend/appflowy_web/appflowy_wasm
# TODO: Can combine caching deps and node_modules in one
# See Glob patterns: https://github.com/actions/toolkit/tree/main/packages/glob
- name: Cache Node.js dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}
- name: Cache node_modules
uses: actions/cache@v4
with:
path: frontend/appflowy_web/node_modules
key: node-modules-${{ runner.os }}
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
profile: minimal
- name: Install wasm-pack
run: cargo install wasm-pack
- uses: taiki-e/install-action@v2
with:
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
- name: install dependencies
if: matrix.platform == 'ubuntu-latest'
working-directory: frontend
run: |
sudo apt-get update
npm install -g pnpm@${{ env.PNPM_VERSION }}
cargo make install_web_protobuf
- name: Build
working-directory: frontend/appflowy_web
run: |
pnpm install
pnpm run build_release_wasm

View File

@ -50,7 +50,6 @@ 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"
TAURI_APP_BACKEND_SERVICE_PATH = "appflowy_web_app/src/application/services/tauri-services/backend"
# Test default config
TEST_CRATE_TYPE = "cdylib"

View File

@ -1846,6 +1846,7 @@ name = "flowy-chat"
version = "0.1.0"
dependencies = [
"allo-isolate",
"anyhow",
"bytes",
"dashmap",
"flowy-chat-pub",
@ -1859,9 +1860,13 @@ dependencies = [
"lib-dispatch",
"lib-infra",
"log",
"parking_lot 0.12.1",
"protobuf",
"serde",
"serde_json",
"strum_macros 0.21.1",
"tokio",
"tokio-stream",
"tracing",
"uuid",
"validator",
@ -2131,6 +2136,7 @@ dependencies = [
"fancy-regex 0.11.0",
"flowy-codegen",
"flowy-derive",
"flowy-sidecar",
"flowy-sqlite",
"lib-dispatch",
"protobuf",
@ -2446,6 +2452,7 @@ dependencies = [
"anyhow",
"base64 0.21.5",
"chrono",
"client-api",
"collab",
"collab-entity",
"flowy-error",

View File

@ -1,7 +0,0 @@
src/services
src/styles
node_modules/
dist/
src-tauri/
.eslintrc.cjs
tsconfig.json

View File

@ -1,73 +0,0 @@
module.exports = {
// 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

@ -1,29 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
wasm-libs/**/target
**/src/services/backend/models/
**/src/services/backend/events/
**/src/appflowy_app/i18n/translations/

View File

@ -1,19 +0,0 @@
.DS_Store
node_modules
/build
/public
/.svelte-kit
/package
/.vscode
.env
.env.*
!.env.example
# rust and generated ts code
/src-tauri
/src/services
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -1,20 +0,0 @@
module.exports = {
arrowParens: 'always',
bracketSpacing: true,
endOfLine: 'lf',
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
jsxBracketSameLine: false,
jsxSingleQuote: true,
printWidth: 121,
plugins: [require('prettier-plugin-tailwindcss')],
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,
vueIndentScriptAndStyle: false,
};

View File

@ -1,45 +0,0 @@
# AppFlowy Web Project
## Installation
```bash
cd appflowy-web
# Install dependencies
rm -rf node_modules && pnpm install
```
## Running the app
```bash
# development
pnpm run dev
# production mode
pnpm run build
# generate wasm
pnpm run wasm
```
## Run tests in Chrome
> Before executing the test, you need to install the [Chrome Driver](https://chromedriver.chromium.org/downloads). If
> you are using a Mac, you can easily install it using Homebrew.
>
> ```shell
> brew install chromedriver
> ```
Go to `frontend/appflowy_web/wasm-libs` and run:
```shell
wasm-pack test --chrome
```
Run tests in headless Chrome
```shell
wasm-pack test --headless --chrome
```

View File

@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@ -1,41 +0,0 @@
{
"name": "appflowy_web",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"build_release_wasm": "cd wasm-libs/af-wasm && wasm-pack build",
"build_dev_wasm": "cd wasm-libs/af-wasm && wasm-pack build --features=\"localhost_dev\"",
"dev": "pnpm run build_dev_wasm && vite",
"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": {
"events": "^3.3.0",
"google-protobuf": "^3.21.2",
"protoc-gen-ts": "^0.8.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ts-results": "^3.3.0",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/events": "^3.0.3",
"@types/node": "^20.10.6",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.0.8",
"vite-plugin-wasm": "^3.3.0",
"rimraf": "^5.0.5"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,15 +0,0 @@
export interface NotifyArgs {
source: string;
ty: number;
id: string;
payload?: Unit8Array;
error?: Unit8Array;
}
declare global {
interface Window {
onFlowyNotify: (eventName: string, args: NotifyArgs) => void;
}
}
export {};

View File

@ -1,42 +0,0 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}

View File

@ -1,58 +0,0 @@
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
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 { v4 as uuidv4 } from 'uuid';
import {AddUserPB, UserWasmEventAddUser} from "@/services/backend/events/user";
init_tracing_log();
// FIXME: handle the promise that init_wasm_core returns
init_wasm_core();
function App() {
useEffect(() => {
initApp();
return subscribeNotification((event: NotifyArgs) => {
console.log(event);
});
}, []);
const handleClick = async () => {
let email = `${uuidv4()}@example.com`;
let password = "AppFlowy!2024";
const payload = AddUserPB.fromObject({email: email, password: password })
let result = await UserWasmEventAddUser(payload);
if (!result.ok) {
}
};
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={handleClick}>Click me!</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}
export default App;

View File

@ -1,35 +0,0 @@
import { initEventBus } from "./event_bus.ts";
import {async_event, register_listener} from "../../wasm-libs/af-wasm/pkg";
export function initApp() {
initEventBus();
register_listener();
}
type InvokeArgs = Record<string, unknown>;
export async function invoke<T>(cmd: string, args?: InvokeArgs): Promise<T> {
switch (cmd) {
case "invoke_request":
const request = args?.request as { ty?: unknown, payload?: unknown } | undefined;
if (!request || typeof request !== 'object') {
throw new Error("Invalid or missing 'request' argument in 'invoke_request'");
}
const { ty, payload } = request;
if (typeof ty !== 'string') {
throw new Error("Invalid 'ty' in request for 'invoke_request'");
}
if (!(payload instanceof Array)) {
throw new Error("Invalid 'payload' in request for 'invoke_request'");
}
return async_event(ty, new Uint8Array(payload));
default:
throw new Error(`Unknown command: ${cmd}`);
}
}

View File

@ -1,34 +0,0 @@
import { EventEmitter } from "events";
import { NotifyArgs } from "../@types/global";
const AF_NOTIFICATION = "af-notification";
let eventEmitter: EventEmitter;
export function getEventEmitterInstance() {
if (!eventEmitter) {
eventEmitter = new EventEmitter();
}
return eventEmitter;
}
export function initEventBus() {
window.onFlowyNotify = (eventName: string, args: NotifyArgs) => {
notify(eventName, args);
};
}
export function notify(_eventName: string, args: NotifyArgs) {
const eventEmitter = getEventEmitterInstance();
eventEmitter.emit(AF_NOTIFICATION, args);
}
export function onNotify(callback: (args: NotifyArgs) => void) {
const eventEmitter = getEventEmitterInstance();
eventEmitter.on(AF_NOTIFICATION, callback);
return offNotify;
}
export function offNotify() {
const eventEmitter = getEventEmitterInstance();
eventEmitter.removeAllListeners(AF_NOTIFICATION);
}

View File

@ -1,17 +0,0 @@
import { NotifyArgs } from "../@types/global";
import { onNotify } from "./event_bus.ts";
export function subscribeNotification(
callback: (args: NotifyArgs) => void,
options?: { id?: string }
) {
return onNotify((payload) => {
const { id } = payload;
if (options?.id !== undefined && id !== options.id) {
return;
}
callback(payload);
});
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,68 +0,0 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@ -1,10 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

View File

@ -1,5 +0,0 @@
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

@ -1 +0,0 @@
/// <reference types="vite/client" />

View File

@ -1,31 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"typeRoots": ["./node_modules/@types", "./src/@types"],
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
}
},
"include": ["src", "vite.config.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
}

View File

@ -1,10 +0,0 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

View File

@ -1,15 +0,0 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import wasm from "vite-plugin-wasm";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), wasm()],
resolve: {
alias: [
{ find: 'src/', replacement: `${__dirname}/src/` },
{ find: '@/', replacement: `${__dirname}/src/` },
{ find: '$app/', replacement: `${__dirname}/src/application/` },
],
},
})

File diff suppressed because it is too large Load Diff

View File

@ -1,76 +0,0 @@
[workspace]
members = ["af-wasm", "af-user", "af-persistence"]
resolver = "2"
[workspace.dependencies]
af-user = { path = "af-user" }
af-persistence = { path = "af-persistence" }
lib-dispatch = { path = "../../rust-lib/lib-dispatch" }
parking_lot = { version = "0.12.1" }
tracing = { version = "0.1.22" }
serde = { version = "1.0.194", features = ["derive"] }
serde_json = "1.0"
collab-integrate = { path = "../../rust-lib/collab-integrate" }
flowy-notification = { path = "../../rust-lib/flowy-notification" }
flowy-user-pub = { path = "../../rust-lib/flowy-user-pub" }
flowy-server = { path = "../../rust-lib/flowy-server" }
flowy-server-pub = { path = "../../rust-lib/flowy-server-pub" }
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" }
lib-infra = { path = "../../rust-lib/lib-infra" }
bytes = { version = "1.5" }
protobuf = { version = "2.28.0" }
thiserror = "1.0"
anyhow = "1.0"
futures-util = "0.3"
uuid = { version = "1.5", features = ["serde", "v4", "v5"] }
tokio-stream = "0.1"
tokio = { version = "1.35", features = ["sync"] }
wasm-bindgen-futures = "0.4.40"
serde-wasm-bindgen = "0.4"
# Please use the following script to update collab.
# Working directory: frontend
#
# To update the commit ID, run:
# scripts/tool/update_collab_rev.sh new_rev_id
#
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { version = "0.2" }
collab-entity = { version = "0.2" }
collab-folder = { version = "0.2" }
collab-document = { version = "0.2" }
collab-database = { version = "0.2" }
collab-plugins = { version = "0.2" }
collab-user = { version = "0.2" }
yrs = "0.18.8"
# Please using the following command to update the revision id
# Current directory: frontend
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9884d93aa2805013f36a79c1757174a0b5063065" }
[profile.dev]
opt-level = 0
lto = false
codegen-units = 16
[profile.release]
lto = true
opt-level = 3
codegen-units = 1
[patch.crates-io]
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }

View File

@ -1,20 +0,0 @@
[package]
name = "af-persistence"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indexed_db_futures = { version = "0.4" }
js-sys = "0.3"
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["console", "Window"] }
tokio = { version = "1.26.0", features = ["sync", "rt"] }
flowy-error.workspace = true
thiserror.workspace = true
anyhow.workspace = true
serde.workspace = true
serde_json.workspace = true
futures-util.workspace = true
wasm-bindgen-futures.workspace = true

View File

@ -1,30 +0,0 @@
use flowy_error::FlowyError;
use web_sys::DomException;
#[derive(Debug, thiserror::Error)]
pub enum PersistenceError {
#[error(transparent)]
Internal(#[from] anyhow::Error),
#[error(transparent)]
SerdeError(#[from] serde_json::Error),
#[error("{0}")]
RecordNotFound(String),
}
impl From<DomException> for PersistenceError {
fn from(value: DomException) -> Self {
PersistenceError::Internal(anyhow::anyhow!("DOMException: {:?}", value))
}
}
impl From<PersistenceError> for FlowyError {
fn from(value: PersistenceError) -> Self {
match value {
PersistenceError::Internal(value) => FlowyError::internal().with_context(value),
PersistenceError::SerdeError(value) => FlowyError::serde().with_context(value),
PersistenceError::RecordNotFound(value) => FlowyError::record_not_found().with_context(value),
}
}
}

View File

@ -1,2 +0,0 @@
pub mod error;
pub mod store;

View File

@ -1,192 +0,0 @@
use crate::error::PersistenceError;
use anyhow::anyhow;
use futures_util::future::LocalBoxFuture;
use indexed_db_futures::idb_object_store::IdbObjectStore;
use indexed_db_futures::idb_transaction::{IdbTransaction, IdbTransactionResult};
use indexed_db_futures::prelude::IdbOpenDbRequestLike;
use indexed_db_futures::{IdbDatabase, IdbQuerySource, IdbVersionChangeEvent};
use js_sys::Uint8Array;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::future::Future;
use std::sync::Arc;
use tokio::sync::RwLock;
use wasm_bindgen::JsValue;
use web_sys::IdbTransactionMode;
pub trait IndexddbStore {
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
where
T: serde::de::DeserializeOwned + Sync;
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
where
T: serde::Serialize + Sync + 'a;
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>>;
}
const APPFLOWY_STORE: &str = "appflowy_store";
pub struct AppFlowyWASMStore {
db: Arc<RwLock<IdbDatabase>>,
}
unsafe impl Send for AppFlowyWASMStore {}
unsafe impl Sync for AppFlowyWASMStore {}
impl AppFlowyWASMStore {
pub async fn new() -> Result<Self, PersistenceError> {
let mut db_req = IdbDatabase::open_u32("appflowy", 1)?;
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
if !evt.db().object_store_names().any(|n| &n == APPFLOWY_STORE) {
evt.db().create_object_store(APPFLOWY_STORE)?;
}
Ok(())
}));
let db = Arc::new(RwLock::new(db_req.await?));
Ok(Self { db })
}
pub async fn begin_read_transaction<F, Fut>(&self, f: F) -> Result<(), PersistenceError>
where
F: FnOnce(&IndexddbStoreImpl<'_>) -> Fut,
Fut: Future<Output = Result<(), PersistenceError>>,
{
let db = self.db.read().await;
let txn = db.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readonly)?;
let store = store_from_transaction(&txn)?;
let operation = IndexddbStoreImpl(store);
f(&operation).await?;
transaction_result_to_result(txn.await)?;
Ok(())
}
pub async fn begin_write_transaction<F>(&self, f: F) -> Result<(), PersistenceError>
where
F: FnOnce(IndexddbStoreImpl<'_>) -> LocalBoxFuture<'_, Result<(), PersistenceError>>,
{
let db = self.db.write().await;
let txn = db.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
let store = store_from_transaction(&txn)?;
let operation = IndexddbStoreImpl(store);
f(operation).await?;
transaction_result_to_result(txn.await)?;
Ok(())
}
}
pub struct IndexddbStoreImpl<'i>(IdbObjectStore<'i>);
impl<'i> IndexddbStore for IndexddbStoreImpl<'i> {
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
where
T: DeserializeOwned + Sync,
{
let js_key = to_js_value(key);
Box::pin(async move {
match self.0.get(&js_key)?.await? {
None => Err(PersistenceError::RecordNotFound(format!(
"Can't find the value for given key: {} ",
key
))),
Some(value) => {
let bytes = Uint8Array::new(&value).to_vec();
let object = serde_json::from_slice::<T>(&bytes)?;
Ok(Some(object))
},
}
})
}
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
where
T: Serialize + Sync + 'a,
{
let js_key = to_js_value(key);
Box::pin(async move {
let js_value = to_js_value(serde_json::to_vec(&value)?);
self.0.put_key_val(&js_key, &js_value)?.await?;
Ok(())
})
}
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>> {
let js_key = to_js_value(key);
Box::pin(async move {
self.0.delete(&js_key)?.await?;
Ok(())
})
}
}
impl IndexddbStore for AppFlowyWASMStore {
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
where
T: serde::de::DeserializeOwned + Sync,
{
let db = Arc::downgrade(&self.db);
Box::pin(async move {
let db = db.upgrade().ok_or_else(|| {
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
})?;
let read_guard = db.read().await;
let txn =
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readonly)?;
let store = store_from_transaction(&txn)?;
IndexddbStoreImpl(store).get(key).await
})
}
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
where
T: serde::Serialize + Sync + 'a,
{
let db = Arc::downgrade(&self.db);
Box::pin(async move {
let db = db.upgrade().ok_or_else(|| {
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
})?;
let read_guard = db.read().await;
let txn =
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
let store = store_from_transaction(&txn)?;
IndexddbStoreImpl(store).set(key, value).await?;
transaction_result_to_result(txn.await)?;
Ok(())
})
}
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>> {
let db = Arc::downgrade(&self.db);
Box::pin(async move {
let db = db.upgrade().ok_or_else(|| {
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
})?;
let read_guard = db.read().await;
let txn =
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
let store = store_from_transaction(&txn)?;
IndexddbStoreImpl(store).remove(key).await?;
transaction_result_to_result(txn.await)?;
Ok(())
})
}
}
fn to_js_value<K: AsRef<[u8]>>(key: K) -> JsValue {
JsValue::from(Uint8Array::from(key.as_ref()))
}
fn store_from_transaction<'a>(
txn: &'a IdbTransaction<'a>,
) -> Result<IdbObjectStore<'a>, PersistenceError> {
txn
.object_store(APPFLOWY_STORE)
.map_err(PersistenceError::from)
}
fn transaction_result_to_result(result: IdbTransactionResult) -> Result<(), PersistenceError> {
match result {
IdbTransactionResult::Success => Ok(()),
IdbTransactionResult::Error(err) => Err(PersistenceError::from(err)),
IdbTransactionResult::Abort => Err(PersistenceError::Internal(anyhow!("Transaction aborted"))),
}
}

View File

@ -1,28 +0,0 @@
[package]
name = "af-user"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
af-persistence.workspace = true
lib-dispatch = { workspace = true }
flowy-derive = { workspace = true }
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "impl_from_collab_persistence"] }
flowy-user-pub = { workspace = true }
strum_macros = "0.25.2"
tracing.workspace = true
lib-infra = { workspace = true }
collab = { workspace = true }
collab-entity.workspace = true
collab-user.workspace = true
collab-integrate = { workspace = true }
protobuf.workspace = true
bytes.workspace = true
anyhow.workspace = true
wasm-bindgen-futures.workspace = true
tokio = { workspace = true, features = ["sync"] }
[build-dependencies]
flowy-codegen = { workspace = true, features = ["ts"] }

View File

@ -1,3 +0,0 @@
# Check out the FlowyConfig (located in flowy_toml.rs) for more details.
proto_input = ["src/entities", "src/event_map.rs"]
event_files = ["src/event_map.rs"]

View File

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

View File

@ -1,20 +0,0 @@
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,9 +0,0 @@
pub(crate) const AF_USER_SESSION_KEY: &str = "af-user-session";
pub(crate) fn user_workspace_key(uid: i64) -> String {
format!("af-user-workspaces-{}", uid)
}
pub(crate) fn user_profile_key(uid: i64) -> String {
format!("af-user-profile-{}", uid)
}

View File

@ -1,68 +0,0 @@
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_user_pub::entities::Authenticator;
use std::collections::HashMap;
#[derive(ProtoBuf, Default)]
pub struct OauthSignInPB {
/// Use this field to store the third party auth information.
/// Different auth type has different fields.
/// Supabase:
/// - map: { "uuid": "xxx" }
///
#[pb(index = 1)]
pub map: HashMap<String, String>,
#[pb(index = 2)]
pub authenticator: AuthenticatorPB,
}
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
pub enum AuthenticatorPB {
Local = 0,
Supabase = 1,
AppFlowyCloud = 2,
}
impl From<Authenticator> for AuthenticatorPB {
fn from(auth_type: Authenticator) -> Self {
match auth_type {
Authenticator::Supabase => AuthenticatorPB::Supabase,
Authenticator::Local => AuthenticatorPB::Local,
Authenticator::AppFlowyCloud => AuthenticatorPB::AppFlowyCloud,
}
}
}
impl From<AuthenticatorPB> for Authenticator {
fn from(pb: AuthenticatorPB) -> Self {
match pb {
AuthenticatorPB::Supabase => Authenticator::Supabase,
AuthenticatorPB::Local => Authenticator::Local,
AuthenticatorPB::AppFlowyCloud => Authenticator::AppFlowyCloud,
}
}
}
impl Default for AuthenticatorPB {
fn default() -> Self {
Self::AppFlowyCloud
}
}
#[derive(ProtoBuf, Default)]
pub struct AddUserPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
}
#[derive(ProtoBuf, Default)]
pub struct UserSignInPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub password: String,
}

View File

@ -1,5 +0,0 @@
mod auth;
mod user;
pub use auth::*;
pub use user::*;

View File

@ -1,73 +0,0 @@
use crate::entities::AuthenticatorPB;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_user_pub::entities::{EncryptionType, UserProfile};
#[derive(ProtoBuf, Default, Eq, PartialEq, Debug, Clone)]
pub struct UserProfilePB {
#[pb(index = 1)]
pub id: i64,
#[pb(index = 2)]
pub email: String,
#[pb(index = 3)]
pub name: String,
#[pb(index = 4)]
pub token: String,
#[pb(index = 5)]
pub icon_url: String,
#[pb(index = 6)]
pub openai_key: String,
#[pb(index = 7)]
pub authenticator: AuthenticatorPB,
#[pb(index = 8)]
pub encryption_sign: String,
#[pb(index = 9)]
pub workspace_id: String,
#[pb(index = 10)]
pub stability_ai_key: String,
#[pb(index = 11)]
pub ai_model: String,
}
impl From<UserProfile> for UserProfilePB {
fn from(user_profile: UserProfile) -> Self {
let (encryption_sign, _encryption_ty) = match user_profile.encryption_type {
EncryptionType::NoEncryption => ("".to_string(), EncryptionTypePB::NoEncryption),
EncryptionType::SelfEncryption(sign) => (sign, EncryptionTypePB::Symmetric),
};
Self {
id: user_profile.uid,
email: user_profile.email,
name: user_profile.name,
token: user_profile.token,
icon_url: user_profile.icon_url,
openai_key: user_profile.openai_key,
encryption_sign,
authenticator: user_profile.authenticator.into(),
workspace_id: user_profile.workspace_id,
stability_ai_key: user_profile.stability_ai_key,
ai_model: user_profile.ai_model,
}
}
}
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
pub enum EncryptionTypePB {
NoEncryption = 0,
Symmetric = 1,
}
impl Default for EncryptionTypePB {
fn default() -> Self {
Self::NoEncryption
}
}

View File

@ -1,48 +0,0 @@
use crate::entities::*;
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;
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<UserManager>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
let user_profile = manager.sign_up(BoxAny::new(params.map)).await?;
data_result_ok(user_profile.into())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn add_user_handler(
data: AFPluginData<AddUserPB>,
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
manager.add_user(&params.email, &params.password).await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn sign_in_with_password_handler(
data: AFPluginData<UserSignInPB>,
manager: AFPluginState<Weak<UserManager>>,
) -> DataResult<UserProfilePB, FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
let user_profile = manager
.sign_in_with_password(&params.email, &params.password)
.await?;
data_result_ok(UserProfilePB::from(user_profile))
}
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"))?;
Ok(manager)
}

View File

@ -1,29 +0,0 @@
use crate::event_handler::*;
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<UserManager>) -> AFPlugin {
AFPlugin::new()
.name("Flowy-User")
.state(user_manager)
.event(UserWasmEvent::OauthSignIn, oauth_sign_in_handler)
.event(UserWasmEvent::AddUser, add_user_handler)
.event(UserWasmEvent::SignInPassword, sign_in_with_password_handler)
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
#[event_err = "FlowyError"]
pub enum UserWasmEvent {
#[event(input = "OauthSignInPB", output = "UserProfilePB")]
OauthSignIn = 0,
#[event(input = "AddUserPB")]
AddUser = 1,
#[event(input = "UserSignInPB", output = "UserProfilePB")]
SignInPassword = 2,
}

View File

@ -1,7 +0,0 @@
pub mod authenticate_user;
mod define;
pub mod entities;
mod event_handler;
pub mod event_map;
pub mod manager;
mod protobuf;

View File

@ -1,205 +0,0 @@
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;
use collab::core::collab::DataSource;
use collab_entity::CollabType;
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::{CollabKVDB, MutexCollab};
use collab_user::core::{MutexUserAwareness, UserAwareness};
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProvider};
use flowy_user_pub::entities::{
user_awareness_object_id, AuthResponse, Authenticator, UserAuthResponse, UserProfile,
UserWorkspace,
};
use flowy_user_pub::session::Session;
use lib_infra::box_any::BoxAny;
use lib_infra::future::Fut;
use std::rc::Rc;
use std::sync::{Arc, Mutex, Weak};
use tracing::{error, instrument, trace};
pub trait UserCallback {
fn did_init(
&self,
user_id: i64,
cloud_config: &Option<UserCloudConfig>,
user_workspace: &UserWorkspace,
device_id: &str,
) -> Fut<FlowyResult<()>>;
fn did_sign_in(&self, uid: i64, workspace: &UserWorkspace, device_id: &str) -> FlowyResult<()>;
fn did_sign_up(
&self,
is_new_user: bool,
user_profile: &UserProfile,
user_workspace: &UserWorkspace,
device_id: &str,
) -> Fut<FlowyResult<()>>;
}
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) 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 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();
let store = Rc::new(AppFlowyWASMStore::new().await?);
let collab_db = Arc::new(CollabKVDB::new().await?);
Ok(Self {
device_id,
cloud_services,
collab_builder,
store,
authenticate_user,
user_callbacks: vec![],
user_awareness: Rc::new(Default::default()),
collab_db,
})
}
pub async fn sign_up(&self, params: BoxAny) -> FlowyResult<UserProfile> {
let auth_service = self.cloud_services.get_user_service()?;
let response: AuthResponse = auth_service.sign_up(params).await?;
let new_user_profile = UserProfile::from((&response, &Authenticator::AppFlowyCloud));
let new_session = Session::from(&response);
self.prepare_collab(&new_session);
self
.save_auth_data(&response, &new_user_profile, &new_session)
.await?;
for callback in self.user_callbacks.iter() {
if let Err(e) = callback
.did_sign_up(
response.is_new_user,
&new_user_profile,
&new_session.user_workspace,
&self.device_id,
)
.await
{
error!("Failed to call did_sign_up callback: {:?}", e);
}
}
// TODO(nathan): send notification
// send_auth_state_notification(AuthStateChangedPB {
// state: AuthStatePB::AuthStateSignIn,
// message: "Sign in success".to_string(),
// });
Ok(new_user_profile)
}
pub(crate) async fn add_user(&self, email: &str, password: &str) -> Result<(), FlowyError> {
let auth_service = self.cloud_services.get_user_service()?;
auth_service.create_user(email, password).await?;
Ok(())
}
pub(crate) async fn sign_in_with_password(
&self,
email: &str,
password: &str,
) -> Result<UserProfile, FlowyError> {
let auth_service = self.cloud_services.get_user_service()?;
let user_profile = auth_service.sign_in_with_password(email, password).await?;
Ok(user_profile)
}
fn prepare_collab(&self, session: &Session) {
let collab_builder = self.collab_builder.upgrade().unwrap();
collab_builder.initialize(session.user_workspace.id.clone());
}
#[instrument(level = "info", skip_all, err)]
async fn save_auth_data(
&self,
response: &impl UserAuthResponse,
user_profile: &UserProfile,
session: &Session,
) -> Result<(), FlowyError> {
let uid = user_profile.uid;
let user_profile = user_profile.clone();
let session = session.clone();
let user_workspace = response.user_workspaces().to_vec();
self
.store
.begin_write_transaction(|store| {
Box::pin(async move {
store.set(&user_workspace_key(uid), &user_workspace).await?;
store.set(AF_USER_SESSION_KEY, session).await?;
store.set(&user_profile_key(uid), user_profile).await?;
Ok(())
})
})
.await?;
Ok(())
}
pub async fn save_user_session(&self, session: &Session) -> FlowyResult<()> {
self.store.set(AF_USER_SESSION_KEY, session).await?;
Ok(())
}
pub async fn save_user_workspaces(
&self,
uid: i64,
user_workspaces: &[UserWorkspace],
) -> FlowyResult<()> {
self
.store
.set(&user_workspace_key(uid), &user_workspaces.to_vec())
.await?;
Ok(())
}
pub async fn save_user_profile(&self, user_profile: &UserProfile) -> FlowyResult<()> {
let uid = user_profile.uid;
self.store.set(&user_profile_key(uid), user_profile).await?;
Ok(())
}
async fn collab_for_user_awareness(
&self,
uid: i64,
object_id: &str,
collab_db: Weak<CollabKVDB>,
raw_data: Vec<u8>,
) -> Result<Arc<MutexCollab>, FlowyError> {
let collab_builder = self.collab_builder.upgrade().ok_or(FlowyError::new(
ErrorCode::Internal,
"Unexpected error: collab builder is not available",
))?;
let collab = collab_builder
.build(
uid,
object_id,
CollabType::UserAwareness,
DataSource::DocStateV1(raw_data),
collab_db,
CollabBuilderConfig::default().sync_enable(true),
)
.await
.context("Build collab for user awareness failed")?;
Ok(collab)
}
}

View File

@ -1,689 +0,0 @@
// This file is generated by rust-protobuf 2.28.0. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `auth.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
#[derive(PartialEq,Clone,Default)]
pub struct OauthSignInPB {
// message fields
pub map: ::std::collections::HashMap<::std::string::String, ::std::string::String>,
pub authenticator: AuthenticatorPB,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a OauthSignInPB {
fn default() -> &'a OauthSignInPB {
<OauthSignInPB as ::protobuf::Message>::default_instance()
}
}
impl OauthSignInPB {
pub fn new() -> OauthSignInPB {
::std::default::Default::default()
}
// repeated .OauthSignInPB.MapEntry map = 1;
pub fn get_map(&self) -> &::std::collections::HashMap<::std::string::String, ::std::string::String> {
&self.map
}
pub fn clear_map(&mut self) {
self.map.clear();
}
// Param is passed by value, moved
pub fn set_map(&mut self, v: ::std::collections::HashMap<::std::string::String, ::std::string::String>) {
self.map = v;
}
// Mutable pointer to the field.
pub fn mut_map(&mut self) -> &mut ::std::collections::HashMap<::std::string::String, ::std::string::String> {
&mut self.map
}
// Take field
pub fn take_map(&mut self) -> ::std::collections::HashMap<::std::string::String, ::std::string::String> {
::std::mem::replace(&mut self.map, ::std::collections::HashMap::new())
}
// .AuthenticatorPB authenticator = 2;
pub fn get_authenticator(&self) -> AuthenticatorPB {
self.authenticator
}
pub fn clear_authenticator(&mut self) {
self.authenticator = AuthenticatorPB::Local;
}
// Param is passed by value, moved
pub fn set_authenticator(&mut self, v: AuthenticatorPB) {
self.authenticator = v;
}
}
impl ::protobuf::Message for OauthSignInPB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_map_into::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(wire_type, is, &mut self.map)?;
},
2 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.authenticator, 2, &mut self.unknown_fields)?
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
my_size += ::protobuf::rt::compute_map_size::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(1, &self.map);
if self.authenticator != AuthenticatorPB::Local {
my_size += ::protobuf::rt::enum_size(2, self.authenticator);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
::protobuf::rt::write_map_with_cached_sizes::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(1, &self.map, os)?;
if self.authenticator != AuthenticatorPB::Local {
os.write_enum(2, ::protobuf::ProtobufEnum::value(&self.authenticator))?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> OauthSignInPB {
OauthSignInPB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_map_accessor::<_, ::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(
"map",
|m: &OauthSignInPB| { &m.map },
|m: &mut OauthSignInPB| { &mut m.map },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<AuthenticatorPB>>(
"authenticator",
|m: &OauthSignInPB| { &m.authenticator },
|m: &mut OauthSignInPB| { &mut m.authenticator },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<OauthSignInPB>(
"OauthSignInPB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static OauthSignInPB {
static instance: ::protobuf::rt::LazyV2<OauthSignInPB> = ::protobuf::rt::LazyV2::INIT;
instance.get(OauthSignInPB::new)
}
}
impl ::protobuf::Clear for OauthSignInPB {
fn clear(&mut self) {
self.map.clear();
self.authenticator = AuthenticatorPB::Local;
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for OauthSignInPB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for OauthSignInPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct AddUserPB {
// message fields
pub email: ::std::string::String,
pub password: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a AddUserPB {
fn default() -> &'a AddUserPB {
<AddUserPB as ::protobuf::Message>::default_instance()
}
}
impl AddUserPB {
pub fn new() -> AddUserPB {
::std::default::Default::default()
}
// string email = 1;
pub fn get_email(&self) -> &str {
&self.email
}
pub fn clear_email(&mut self) {
self.email.clear();
}
// Param is passed by value, moved
pub fn set_email(&mut self, v: ::std::string::String) {
self.email = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_email(&mut self) -> &mut ::std::string::String {
&mut self.email
}
// Take field
pub fn take_email(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string password = 2;
pub fn get_password(&self) -> &str {
&self.password
}
pub fn clear_password(&mut self) {
self.password.clear();
}
// Param is passed by value, moved
pub fn set_password(&mut self, v: ::std::string::String) {
self.password = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_password(&mut self) -> &mut ::std::string::String {
&mut self.password
}
// Take field
pub fn take_password(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.password, ::std::string::String::new())
}
}
impl ::protobuf::Message for AddUserPB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.email);
}
if !self.password.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.password);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.email.is_empty() {
os.write_string(1, &self.email)?;
}
if !self.password.is_empty() {
os.write_string(2, &self.password)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> AddUserPB {
AddUserPB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"email",
|m: &AddUserPB| { &m.email },
|m: &mut AddUserPB| { &mut m.email },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"password",
|m: &AddUserPB| { &m.password },
|m: &mut AddUserPB| { &mut m.password },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<AddUserPB>(
"AddUserPB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static AddUserPB {
static instance: ::protobuf::rt::LazyV2<AddUserPB> = ::protobuf::rt::LazyV2::INIT;
instance.get(AddUserPB::new)
}
}
impl ::protobuf::Clear for AddUserPB {
fn clear(&mut self) {
self.email.clear();
self.password.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for AddUserPB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for AddUserPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(PartialEq,Clone,Default)]
pub struct UserSignInPB {
// message fields
pub email: ::std::string::String,
pub password: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a UserSignInPB {
fn default() -> &'a UserSignInPB {
<UserSignInPB as ::protobuf::Message>::default_instance()
}
}
impl UserSignInPB {
pub fn new() -> UserSignInPB {
::std::default::Default::default()
}
// string email = 1;
pub fn get_email(&self) -> &str {
&self.email
}
pub fn clear_email(&mut self) {
self.email.clear();
}
// Param is passed by value, moved
pub fn set_email(&mut self, v: ::std::string::String) {
self.email = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_email(&mut self) -> &mut ::std::string::String {
&mut self.email
}
// Take field
pub fn take_email(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string password = 2;
pub fn get_password(&self) -> &str {
&self.password
}
pub fn clear_password(&mut self) {
self.password.clear();
}
// Param is passed by value, moved
pub fn set_password(&mut self, v: ::std::string::String) {
self.password = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_password(&mut self) -> &mut ::std::string::String {
&mut self.password
}
// Take field
pub fn take_password(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.password, ::std::string::String::new())
}
}
impl ::protobuf::Message for UserSignInPB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.email);
}
if !self.password.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.password);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.email.is_empty() {
os.write_string(1, &self.email)?;
}
if !self.password.is_empty() {
os.write_string(2, &self.password)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> UserSignInPB {
UserSignInPB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"email",
|m: &UserSignInPB| { &m.email },
|m: &mut UserSignInPB| { &mut m.email },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"password",
|m: &UserSignInPB| { &m.password },
|m: &mut UserSignInPB| { &mut m.password },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserSignInPB>(
"UserSignInPB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static UserSignInPB {
static instance: ::protobuf::rt::LazyV2<UserSignInPB> = ::protobuf::rt::LazyV2::INIT;
instance.get(UserSignInPB::new)
}
}
impl ::protobuf::Clear for UserSignInPB {
fn clear(&mut self) {
self.email.clear();
self.password.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for UserSignInPB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for UserSignInPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum AuthenticatorPB {
Local = 0,
Supabase = 1,
AppFlowyCloud = 2,
}
impl ::protobuf::ProtobufEnum for AuthenticatorPB {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<AuthenticatorPB> {
match value {
0 => ::std::option::Option::Some(AuthenticatorPB::Local),
1 => ::std::option::Option::Some(AuthenticatorPB::Supabase),
2 => ::std::option::Option::Some(AuthenticatorPB::AppFlowyCloud),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [AuthenticatorPB] = &[
AuthenticatorPB::Local,
AuthenticatorPB::Supabase,
AuthenticatorPB::AppFlowyCloud,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<AuthenticatorPB>("AuthenticatorPB", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for AuthenticatorPB {
}
impl ::std::default::Default for AuthenticatorPB {
fn default() -> Self {
AuthenticatorPB::Local
}
}
impl ::protobuf::reflect::ProtobufValue for AuthenticatorPB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\nauth.proto\"\xaa\x01\n\rOauthSignInPB\x12)\n\x03map\x18\x01\x20\x03(\
\x0b2\x17.OauthSignInPB.MapEntryR\x03map\x126\n\rauthenticator\x18\x02\
\x20\x01(\x0e2\x10.AuthenticatorPBR\rauthenticator\x1a6\n\x08MapEntry\
\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x14\n\x05value\x18\x02\
\x20\x01(\tR\x05value:\x028\x01\"=\n\tAddUserPB\x12\x14\n\x05email\x18\
\x01\x20\x01(\tR\x05email\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08p\
assword\"@\n\x0cUserSignInPB\x12\x14\n\x05email\x18\x01\x20\x01(\tR\x05e\
mail\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08password*=\n\x0fAuthen\
ticatorPB\x12\t\n\x05Local\x10\0\x12\x0c\n\x08Supabase\x10\x01\x12\x11\n\
\rAppFlowyCloud\x10\x02b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,95 +0,0 @@
// This file is generated by rust-protobuf 2.28.0. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `event_map.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum UserWasmEvent {
OauthSignIn = 0,
AddUser = 1,
SignInPassword = 2,
}
impl ::protobuf::ProtobufEnum for UserWasmEvent {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<UserWasmEvent> {
match value {
0 => ::std::option::Option::Some(UserWasmEvent::OauthSignIn),
1 => ::std::option::Option::Some(UserWasmEvent::AddUser),
2 => ::std::option::Option::Some(UserWasmEvent::SignInPassword),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [UserWasmEvent] = &[
UserWasmEvent::OauthSignIn,
UserWasmEvent::AddUser,
UserWasmEvent::SignInPassword,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<UserWasmEvent>("UserWasmEvent", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for UserWasmEvent {
}
impl ::std::default::Default for UserWasmEvent {
fn default() -> Self {
UserWasmEvent::OauthSignIn
}
}
impl ::protobuf::reflect::ProtobufValue for UserWasmEvent {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0fevent_map.proto*A\n\rUserWasmEvent\x12\x0f\n\x0bOauthSignIn\x10\0\
\x12\x0b\n\x07AddUser\x10\x01\x12\x12\n\x0eSignInPassword\x10\x02b\x06pr\
oto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,12 +0,0 @@
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(ambiguous_glob_reexports)]
// Auto-generated, do not edit
mod event_map;
pub use event_map::*;
mod auth;
pub use auth::*;
mod user;
pub use user::*;

View File

@ -1,661 +0,0 @@
// This file is generated by rust-protobuf 2.28.0. Do not edit
// @generated
// https://github.com/rust-lang/rust-clippy/issues/702
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![allow(unused_attributes)]
#![cfg_attr(rustfmt, rustfmt::skip)]
#![allow(box_pointers)]
#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(trivial_casts)]
#![allow(unused_imports)]
#![allow(unused_results)]
//! Generated file from `user.proto`
/// Generated files are compatible only with the same version
/// of protobuf runtime.
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
#[derive(PartialEq,Clone,Default)]
pub struct UserProfilePB {
// message fields
pub id: i64,
pub email: ::std::string::String,
pub name: ::std::string::String,
pub token: ::std::string::String,
pub icon_url: ::std::string::String,
pub openai_key: ::std::string::String,
pub authenticator: super::auth::AuthenticatorPB,
pub encryption_sign: ::std::string::String,
pub workspace_id: ::std::string::String,
pub stability_ai_key: ::std::string::String,
pub ai_model: ::std::string::String,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
}
impl<'a> ::std::default::Default for &'a UserProfilePB {
fn default() -> &'a UserProfilePB {
<UserProfilePB as ::protobuf::Message>::default_instance()
}
}
impl UserProfilePB {
pub fn new() -> UserProfilePB {
::std::default::Default::default()
}
// int64 id = 1;
pub fn get_id(&self) -> i64 {
self.id
}
pub fn clear_id(&mut self) {
self.id = 0;
}
// Param is passed by value, moved
pub fn set_id(&mut self, v: i64) {
self.id = v;
}
// string email = 2;
pub fn get_email(&self) -> &str {
&self.email
}
pub fn clear_email(&mut self) {
self.email.clear();
}
// Param is passed by value, moved
pub fn set_email(&mut self, v: ::std::string::String) {
self.email = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_email(&mut self) -> &mut ::std::string::String {
&mut self.email
}
// Take field
pub fn take_email(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.email, ::std::string::String::new())
}
// string name = 3;
pub fn get_name(&self) -> &str {
&self.name
}
pub fn clear_name(&mut self) {
self.name.clear();
}
// Param is passed by value, moved
pub fn set_name(&mut self, v: ::std::string::String) {
self.name = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_name(&mut self) -> &mut ::std::string::String {
&mut self.name
}
// Take field
pub fn take_name(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.name, ::std::string::String::new())
}
// string token = 4;
pub fn get_token(&self) -> &str {
&self.token
}
pub fn clear_token(&mut self) {
self.token.clear();
}
// Param is passed by value, moved
pub fn set_token(&mut self, v: ::std::string::String) {
self.token = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_token(&mut self) -> &mut ::std::string::String {
&mut self.token
}
// Take field
pub fn take_token(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.token, ::std::string::String::new())
}
// string icon_url = 5;
pub fn get_icon_url(&self) -> &str {
&self.icon_url
}
pub fn clear_icon_url(&mut self) {
self.icon_url.clear();
}
// Param is passed by value, moved
pub fn set_icon_url(&mut self, v: ::std::string::String) {
self.icon_url = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_icon_url(&mut self) -> &mut ::std::string::String {
&mut self.icon_url
}
// Take field
pub fn take_icon_url(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.icon_url, ::std::string::String::new())
}
// string openai_key = 6;
pub fn get_openai_key(&self) -> &str {
&self.openai_key
}
pub fn clear_openai_key(&mut self) {
self.openai_key.clear();
}
// Param is passed by value, moved
pub fn set_openai_key(&mut self, v: ::std::string::String) {
self.openai_key = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_openai_key(&mut self) -> &mut ::std::string::String {
&mut self.openai_key
}
// Take field
pub fn take_openai_key(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.openai_key, ::std::string::String::new())
}
// .AuthenticatorPB authenticator = 7;
pub fn get_authenticator(&self) -> super::auth::AuthenticatorPB {
self.authenticator
}
pub fn clear_authenticator(&mut self) {
self.authenticator = super::auth::AuthenticatorPB::Local;
}
// Param is passed by value, moved
pub fn set_authenticator(&mut self, v: super::auth::AuthenticatorPB) {
self.authenticator = v;
}
// string encryption_sign = 8;
pub fn get_encryption_sign(&self) -> &str {
&self.encryption_sign
}
pub fn clear_encryption_sign(&mut self) {
self.encryption_sign.clear();
}
// Param is passed by value, moved
pub fn set_encryption_sign(&mut self, v: ::std::string::String) {
self.encryption_sign = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_encryption_sign(&mut self) -> &mut ::std::string::String {
&mut self.encryption_sign
}
// Take field
pub fn take_encryption_sign(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.encryption_sign, ::std::string::String::new())
}
// string workspace_id = 9;
pub fn get_workspace_id(&self) -> &str {
&self.workspace_id
}
pub fn clear_workspace_id(&mut self) {
self.workspace_id.clear();
}
// Param is passed by value, moved
pub fn set_workspace_id(&mut self, v: ::std::string::String) {
self.workspace_id = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_workspace_id(&mut self) -> &mut ::std::string::String {
&mut self.workspace_id
}
// Take field
pub fn take_workspace_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.workspace_id, ::std::string::String::new())
}
// string stability_ai_key = 10;
pub fn get_stability_ai_key(&self) -> &str {
&self.stability_ai_key
}
pub fn clear_stability_ai_key(&mut self) {
self.stability_ai_key.clear();
}
// Param is passed by value, moved
pub fn set_stability_ai_key(&mut self, v: ::std::string::String) {
self.stability_ai_key = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_stability_ai_key(&mut self) -> &mut ::std::string::String {
&mut self.stability_ai_key
}
// Take field
pub fn take_stability_ai_key(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.stability_ai_key, ::std::string::String::new())
}
// string ai_model = 11;
pub fn get_ai_model(&self) -> &str {
&self.ai_model
}
pub fn clear_ai_model(&mut self) {
self.ai_model.clear();
}
// Param is passed by value, moved
pub fn set_ai_model(&mut self, v: ::std::string::String) {
self.ai_model = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_ai_model(&mut self) -> &mut ::std::string::String {
&mut self.ai_model
}
// Take field
pub fn take_ai_model(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.ai_model, ::std::string::String::new())
}
}
impl ::protobuf::Message for UserProfilePB {
fn is_initialized(&self) -> bool {
true
}
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
while !is.eof()? {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_int64()?;
self.id = tmp;
},
2 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
},
3 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
},
4 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.token)?;
},
5 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.icon_url)?;
},
6 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.openai_key)?;
},
7 => {
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.authenticator, 7, &mut self.unknown_fields)?
},
8 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.encryption_sign)?;
},
9 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.workspace_id)?;
},
10 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.stability_ai_key)?;
},
11 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ai_model)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
},
};
}
::std::result::Result::Ok(())
}
// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if self.id != 0 {
my_size += ::protobuf::rt::value_size(1, self.id, ::protobuf::wire_format::WireTypeVarint);
}
if !self.email.is_empty() {
my_size += ::protobuf::rt::string_size(2, &self.email);
}
if !self.name.is_empty() {
my_size += ::protobuf::rt::string_size(3, &self.name);
}
if !self.token.is_empty() {
my_size += ::protobuf::rt::string_size(4, &self.token);
}
if !self.icon_url.is_empty() {
my_size += ::protobuf::rt::string_size(5, &self.icon_url);
}
if !self.openai_key.is_empty() {
my_size += ::protobuf::rt::string_size(6, &self.openai_key);
}
if self.authenticator != super::auth::AuthenticatorPB::Local {
my_size += ::protobuf::rt::enum_size(7, self.authenticator);
}
if !self.encryption_sign.is_empty() {
my_size += ::protobuf::rt::string_size(8, &self.encryption_sign);
}
if !self.workspace_id.is_empty() {
my_size += ::protobuf::rt::string_size(9, &self.workspace_id);
}
if !self.stability_ai_key.is_empty() {
my_size += ::protobuf::rt::string_size(10, &self.stability_ai_key);
}
if !self.ai_model.is_empty() {
my_size += ::protobuf::rt::string_size(11, &self.ai_model);
}
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if self.id != 0 {
os.write_int64(1, self.id)?;
}
if !self.email.is_empty() {
os.write_string(2, &self.email)?;
}
if !self.name.is_empty() {
os.write_string(3, &self.name)?;
}
if !self.token.is_empty() {
os.write_string(4, &self.token)?;
}
if !self.icon_url.is_empty() {
os.write_string(5, &self.icon_url)?;
}
if !self.openai_key.is_empty() {
os.write_string(6, &self.openai_key)?;
}
if self.authenticator != super::auth::AuthenticatorPB::Local {
os.write_enum(7, ::protobuf::ProtobufEnum::value(&self.authenticator))?;
}
if !self.encryption_sign.is_empty() {
os.write_string(8, &self.encryption_sign)?;
}
if !self.workspace_id.is_empty() {
os.write_string(9, &self.workspace_id)?;
}
if !self.stability_ai_key.is_empty() {
os.write_string(10, &self.stability_ai_key)?;
}
if !self.ai_model.is_empty() {
os.write_string(11, &self.ai_model)?;
}
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
fn get_cached_size(&self) -> u32 {
self.cached_size.get()
}
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
&self.unknown_fields
}
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
&mut self.unknown_fields
}
fn as_any(&self) -> &dyn (::std::any::Any) {
self as &dyn (::std::any::Any)
}
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
self as &mut dyn (::std::any::Any)
}
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
self
}
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
Self::descriptor_static()
}
fn new() -> UserProfilePB {
UserProfilePB::new()
}
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
"id",
|m: &UserProfilePB| { &m.id },
|m: &mut UserProfilePB| { &mut m.id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"email",
|m: &UserProfilePB| { &m.email },
|m: &mut UserProfilePB| { &mut m.email },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"name",
|m: &UserProfilePB| { &m.name },
|m: &mut UserProfilePB| { &mut m.name },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"token",
|m: &UserProfilePB| { &m.token },
|m: &mut UserProfilePB| { &mut m.token },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"icon_url",
|m: &UserProfilePB| { &m.icon_url },
|m: &mut UserProfilePB| { &mut m.icon_url },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"openai_key",
|m: &UserProfilePB| { &m.openai_key },
|m: &mut UserProfilePB| { &mut m.openai_key },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<super::auth::AuthenticatorPB>>(
"authenticator",
|m: &UserProfilePB| { &m.authenticator },
|m: &mut UserProfilePB| { &mut m.authenticator },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"encryption_sign",
|m: &UserProfilePB| { &m.encryption_sign },
|m: &mut UserProfilePB| { &mut m.encryption_sign },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"workspace_id",
|m: &UserProfilePB| { &m.workspace_id },
|m: &mut UserProfilePB| { &mut m.workspace_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"stability_ai_key",
|m: &UserProfilePB| { &m.stability_ai_key },
|m: &mut UserProfilePB| { &mut m.stability_ai_key },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"ai_model",
|m: &UserProfilePB| { &m.ai_model },
|m: &mut UserProfilePB| { &mut m.ai_model },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserProfilePB>(
"UserProfilePB",
fields,
file_descriptor_proto()
)
})
}
fn default_instance() -> &'static UserProfilePB {
static instance: ::protobuf::rt::LazyV2<UserProfilePB> = ::protobuf::rt::LazyV2::INIT;
instance.get(UserProfilePB::new)
}
}
impl ::protobuf::Clear for UserProfilePB {
fn clear(&mut self) {
self.id = 0;
self.email.clear();
self.name.clear();
self.token.clear();
self.icon_url.clear();
self.openai_key.clear();
self.authenticator = super::auth::AuthenticatorPB::Local;
self.encryption_sign.clear();
self.workspace_id.clear();
self.stability_ai_key.clear();
self.ai_model.clear();
self.unknown_fields.clear();
}
}
impl ::std::fmt::Debug for UserProfilePB {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
::protobuf::text_format::fmt(self, f)
}
}
impl ::protobuf::reflect::ProtobufValue for UserProfilePB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Message(self)
}
}
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
pub enum EncryptionTypePB {
NoEncryption = 0,
Symmetric = 1,
}
impl ::protobuf::ProtobufEnum for EncryptionTypePB {
fn value(&self) -> i32 {
*self as i32
}
fn from_i32(value: i32) -> ::std::option::Option<EncryptionTypePB> {
match value {
0 => ::std::option::Option::Some(EncryptionTypePB::NoEncryption),
1 => ::std::option::Option::Some(EncryptionTypePB::Symmetric),
_ => ::std::option::Option::None
}
}
fn values() -> &'static [Self] {
static values: &'static [EncryptionTypePB] = &[
EncryptionTypePB::NoEncryption,
EncryptionTypePB::Symmetric,
];
values
}
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
::protobuf::reflect::EnumDescriptor::new_pb_name::<EncryptionTypePB>("EncryptionTypePB", file_descriptor_proto())
})
}
}
impl ::std::marker::Copy for EncryptionTypePB {
}
impl ::std::default::Default for EncryptionTypePB {
fn default() -> Self {
EncryptionTypePB::NoEncryption
}
}
impl ::protobuf::reflect::ProtobufValue for EncryptionTypePB {
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
}
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\nuser.proto\x1a\nauth.proto\"\xe2\x02\n\rUserProfilePB\x12\x0e\n\x02i\
d\x18\x01\x20\x01(\x03R\x02id\x12\x14\n\x05email\x18\x02\x20\x01(\tR\x05\
email\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x14\n\x05token\
\x18\x04\x20\x01(\tR\x05token\x12\x19\n\x08icon_url\x18\x05\x20\x01(\tR\
\x07iconUrl\x12\x1d\n\nopenai_key\x18\x06\x20\x01(\tR\topenaiKey\x126\n\
\rauthenticator\x18\x07\x20\x01(\x0e2\x10.AuthenticatorPBR\rauthenticato\
r\x12'\n\x0fencryption_sign\x18\x08\x20\x01(\tR\x0eencryptionSign\x12!\n\
\x0cworkspace_id\x18\t\x20\x01(\tR\x0bworkspaceId\x12(\n\x10stability_ai\
_key\x18\n\x20\x01(\tR\x0estabilityAiKey\x12\x19\n\x08ai_model\x18\x0b\
\x20\x01(\tR\x07aiModel*3\n\x10EncryptionTypePB\x12\x10\n\x0cNoEncryptio\
n\x10\0\x12\r\n\tSymmetric\x10\x01b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
}
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
file_descriptor_proto_lazy.get(|| {
parse_descriptor_proto()
})
}

View File

@ -1,58 +0,0 @@
[package]
name = "af-wasm"
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]
wasm-bindgen = { version = "0.2.89" }
lazy_static = "1.4.0"
lib-dispatch = { workspace = true, features = ["use_serde"] }
parking_lot.workspace = true
tracing.workspace = true
tracing-core = { version = "0.1.32" }
tracing-wasm = "0.2.1"
serde.workspace = true
collab-integrate = { workspace = true }
tokio-stream.workspace = true
af-user.workspace = true
af-persistence.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 }
web-sys = "0.3"
wasm-bindgen-futures.workspace = true
uuid.workspace = true
serde-wasm-bindgen.workspace = true
js-sys = "0.3.67"
anyhow = "1.0"
# `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,89 +0,0 @@
use crate::deps_resolve::document_deps::DocumentDepsResolver;
use crate::deps_resolve::folder_deps::FolderDepsResolver;
use crate::integrate::server::ServerProviderWASM;
use af_persistence::store::AppFlowyWASMStore;
use af_user::authenticate_user::AuthenticateUser;
use af_user::manager::UserManager;
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, WorkspaceCollabIntegrate};
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;
use std::sync::Arc;
pub struct AppFlowyWASMCore {
pub collab_builder: Arc<AppFlowyCollabBuilder>,
pub event_dispatcher: Rc<AFPluginDispatcher>,
pub user_manager: Rc<UserManager>,
pub folder_manager: Rc<FolderManager>,
pub document_manager: Rc<DocumentManager>,
}
impl AppFlowyWASMCore {
pub async fn new(device_id: &str, cloud_config: AFCloudConfiguration) -> FlowyResult<Self> {
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let server_provider = Rc::new(ServerProviderWASM::new(device_id, cloud_config));
let store = Rc::new(AppFlowyWASMStore::new().await?);
let auth_user = Rc::new(AuthenticateUser::new(store.clone()).await?);
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
device_id.to_string(),
server_provider.clone(),
WorkspaceCollabIntegrateImpl(auth_user.clone()),
));
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(
UserManager::new(
device_id,
store,
server_provider.clone(),
auth_user,
Arc::downgrade(&collab_builder),
)
.await?,
);
let event_dispatcher = Rc::new(AFPluginDispatcher::new(
runtime,
vec![af_user::event_map::init(Rc::downgrade(&user_manager))],
));
Ok(Self {
collab_builder,
event_dispatcher,
user_manager,
folder_manager,
document_manager,
})
}
}
struct WorkspaceCollabIntegrateImpl(Rc<AuthenticateUser>);
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, anyhow::Error> {
let workspace_id = self.0.workspace_id()?;
Ok(workspace_id)
}
fn device_id(&self) -> Result<String, anyhow::Error> {
Ok("fake device id".to_string())
}
}

View File

@ -1,19 +0,0 @@
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

@ -1,20 +0,0 @@
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

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

View File

@ -1 +0,0 @@
pub mod server;

View File

@ -1,126 +0,0 @@
use collab::preclude::CollabPlugin;
use collab_integrate::collab_builder::{
CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType,
};
use flowy_error::FlowyError;
use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::AppFlowyServer;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
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, FutureResult};
use parking_lot::RwLock;
use std::rc::Rc;
use std::sync::Arc;
use tokio_stream::wrappers::WatchStream;
use tracing::{info, warn};
pub struct ServerProviderWASM {
device_id: String,
config: AFCloudConfiguration,
server: RwLock<Option<Rc<dyn AppFlowyServer>>>,
}
impl ServerProviderWASM {
pub fn new(device_id: &str, config: AFCloudConfiguration) -> Self {
info!("Server config: {}", config);
Self {
device_id: device_id.to_string(),
server: RwLock::new(Default::default()),
config,
}
}
pub fn get_server(&self) -> Rc<dyn AppFlowyServer> {
let server = self.server.read().as_ref().cloned();
match server {
Some(server) => server,
None => {
let server = Rc::new(AppFlowyCloudServer::new(
self.config.clone(),
true,
self.device_id.clone(),
"0.0.1",
));
*self.server.write() = Some(server.clone());
server
},
}
}
}
impl CollabCloudPluginProvider for ServerProviderWASM {
fn provider_type(&self) -> CollabPluginProviderType {
CollabPluginProviderType::AppFlowyCloud
}
fn get_plugins(&self, _context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>> {
vec![]
}
fn is_sync_enabled(&self) -> bool {
true
}
}
impl UserCloudServiceProvider for ServerProviderWASM {
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
self.get_server().set_token(token)?;
Ok(())
}
fn set_ai_model(&self, ai_model: &str) -> Result<(), FlowyError> {
Ok(())
}
fn subscribe_token_state(&self) -> Option<WatchStream<UserTokenState>> {
self.get_server().subscribe_token_state()
}
fn set_enable_sync(&self, _uid: i64, _enable_sync: bool) {
warn!("enable sync is not supported in wasm")
}
fn set_user_authenticator(&self, _authenticator: &Authenticator) {
warn!("set user authenticator is not supported in wasm")
}
fn get_user_authenticator(&self) -> Authenticator {
Authenticator::AppFlowyCloud
}
fn set_network_reachable(&self, _reachable: bool) {
warn!("set network reachable is not supported in wasm")
}
fn set_encrypt_secret(&self, _secret: String) {
warn!("set encrypt secret is not supported in wasm")
}
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
Ok(self.get_server().user_service())
}
fn service_url(&self) -> String {
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

@ -1,170 +0,0 @@
use crate::notification::TSNotificationSender;
use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
use std::cell::RefCell;
use std::rc::Rc;
pub mod core;
mod deps_resolve;
mod integrate;
pub mod notification;
use crate::core::AppFlowyWASMCore;
use lazy_static::lazy_static;
use lib_dispatch::prelude::{
AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode,
};
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use tracing::{error, info};
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
use wasm_bindgen_futures::{future_to_promise, js_sys};
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)]
pub fn log(s: &str);
#[wasm_bindgen(js_namespace = window)]
fn onFlowyNotify(event_name: &str, payload: JsValue);
}
#[wasm_bindgen]
pub fn init_tracing_log() {
tracing_wasm::set_as_global_default();
}
#[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(),
ws_base_url: "ws://localhost/ws/v1".to_string(),
gotrue_url: "http://localhost/gotrue".to_string(),
};
#[cfg(not(feature = "localhost_dev"))]
let config = AFCloudConfiguration {
base_url: "https://beta.appflowy.cloud".to_string(),
ws_base_url: "wss://beta.appflowy.cloud/ws/v1".to_string(),
gotrue_url: "https://beta.appflowy.cloud/gotrue".to_string(),
};
let future = async move {
if let Ok(core) = AppFlowyWASMCore::new("device_id", config).await {
*APPFLOWY_CORE.0.borrow_mut() = Some(core);
info!("🔥🔥🔥Initialized AppFlowyWASMCore");
} else {
error!("Failed to initialize AppFlowyWASMCore")
}
Ok(JsValue::from_str(""))
};
future_to_promise(future)
}
#[wasm_bindgen]
pub fn async_event(name: String, payload: Vec<u8>) -> js_sys::Promise {
if let Some(dispatcher) = APPFLOWY_CORE.dispatcher() {
let future = async move {
let request = WasmRequest::new(name, payload);
let event_resp =
AFPluginDispatcher::boxed_async_send_with_callback(dispatcher.as_ref(), request, |_| {
Box::pin(async {})
})
.await;
let response = WasmResponse::from(event_resp);
serde_wasm_bindgen::to_value(&response).map_err(error_response)
};
future_to_promise(future)
} else {
future_to_promise(async { Err(JsValue::from_str("Dispatcher is not initialized")) })
}
}
#[wasm_bindgen]
pub fn register_listener() {
unregister_all_notification_sender();
register_notification_sender(TSNotificationSender::new());
}
pub fn on_event(event_name: &str, args: JsValue) {
onFlowyNotify(event_name, args);
}
struct RefCellAppFlowyCore(RefCell<Option<AppFlowyWASMCore>>);
/// safety:
/// In a WebAssembly, implement the Sync for RefCellAppFlowyCore is safety
/// since WASM currently operates in a single-threaded environment.
unsafe impl Sync for RefCellAppFlowyCore {}
impl RefCellAppFlowyCore {
fn new() -> Self {
Self(RefCell::new(None))
}
fn dispatcher(&self) -> Option<Rc<AFPluginDispatcher>> {
self
.0
.borrow()
.as_ref()
.map(|core| core.event_dispatcher.clone())
}
}
fn error_response(error: serde_wasm_bindgen::Error) -> JsValue {
error!("Error: {}", error);
serde_wasm_bindgen::to_value(&WasmResponse::error(error.to_string())).unwrap()
}
pub struct WasmRequest {
name: String,
payload: Vec<u8>,
}
impl WasmRequest {
pub fn new(name: String, payload: Vec<u8>) -> Self {
Self { name, payload }
}
}
impl From<WasmRequest> for AFPluginRequest {
fn from(request: WasmRequest) -> Self {
AFPluginRequest::new(request.name).payload(request.payload)
}
}
#[derive(serde::Serialize)]
pub struct WasmResponse {
pub code: i8,
pub payload: Vec<u8>,
}
impl WasmResponse {
pub fn error(msg: String) -> Self {
Self {
code: StatusCode::Err as i8,
payload: msg.into_bytes(),
}
}
}
impl From<AFPluginEventResponse> for WasmResponse {
fn from(response: AFPluginEventResponse) -> Self {
Self {
code: response.status_code as i8,
payload: response.payload.to_vec(),
}
}
}

View File

@ -1,19 +0,0 @@
use flowy_notification::entities::SubscribeObject;
use flowy_notification::NotificationSender;
pub const AF_NOTIFICATION: &str = "af-notification";
pub struct TSNotificationSender {}
impl TSNotificationSender {
pub(crate) fn new() -> Self {
TSNotificationSender {}
}
}
impl NotificationSender for TSNotificationSender {
fn send_subject(&self, _subject: SubscribeObject) -> Result<(), String> {
// on_event(AF_NOTIFICATION, serde_wasm_bindgen::to_value(&subject).unwrap_or(JsValue::UNDEFINED));
Ok(())
}
}

View File

@ -1,4 +0,0 @@
use wasm_bindgen_test::wasm_bindgen_test_configure;
wasm_bindgen_test_configure!(run_in_browser);
mod user;
pub(crate) mod util;

View File

@ -1,10 +0,0 @@
use crate::util::tester::{unique_email, WASMEventTester};
use wasm_bindgen_test::wasm_bindgen_test;
#[wasm_bindgen_test]
async fn sign_up_event_test() {
let tester = WASMEventTester::new().await;
let email = unique_email();
let user_profile = tester.sign_in_with_email(&email).await.unwrap();
assert_eq!(user_profile.email, email);
}

View File

@ -1 +0,0 @@
mod event_test;

View File

@ -1,132 +0,0 @@
use af_wasm::core::AppFlowyWASMCore;
use flowy_error::{internal_error, FlowyError};
use std::rc::Rc;
use std::{
convert::TryFrom,
fmt::{Debug, Display},
hash::Hash,
sync::Arc,
};
use lib_dispatch::prelude::{
AFPluginDispatcher, AFPluginEventResponse, AFPluginFromBytes, AFPluginRequest, ToBytes, *,
};
#[derive(Clone)]
pub struct EventBuilder {
context: TestContext,
}
impl EventBuilder {
pub fn new(core: Arc<AppFlowyWASMCore>) -> Self {
Self {
context: TestContext::new(core),
}
}
pub fn payload<P>(mut self, payload: P) -> Self
where
P: ToBytes,
{
match payload.into_bytes() {
Ok(bytes) => {
let module_request = self.take_request();
self.context.request = Some(module_request.payload(bytes))
},
Err(e) => {
tracing::error!("Set payload failed: {:?}", e);
},
}
self
}
pub fn event<Event>(mut self, event: Event) -> Self
where
Event: Eq + Hash + Debug + Clone + Display,
{
self.context.request = Some(AFPluginRequest::new(event));
self
}
pub async fn async_send(mut self) -> Self {
let request = self.take_request();
let resp = AFPluginDispatcher::async_send(self.dispatch().as_ref(), request).await;
self.context.response = Some(resp);
self
}
pub fn parse<R>(self) -> R
where
R: AFPluginFromBytes,
{
let response = self.get_response();
match response.clone().parse::<R, FlowyError>() {
Ok(Ok(data)) => data,
Ok(Err(e)) => {
panic!(
"Parser {:?} failed: {:?}, response {:?}",
std::any::type_name::<R>(),
e,
response
)
},
Err(e) => panic!(
"Dispatch {:?} failed: {:?}, response {:?}",
std::any::type_name::<R>(),
e,
response
),
}
}
#[allow(dead_code)]
pub fn try_parse<R>(self) -> Result<R, FlowyError>
where
R: AFPluginFromBytes,
{
let response = self.get_response();
response.parse::<R, FlowyError>().map_err(internal_error)?
}
#[allow(dead_code)]
pub fn error(self) -> Option<FlowyError> {
let response = self.get_response();
<AFPluginData<FlowyError>>::try_from(response.payload)
.ok()
.map(|data| data.into_inner())
}
fn dispatch(&self) -> &Rc<AFPluginDispatcher> {
&self.context.sdk.event_dispatcher
}
fn get_response(&self) -> AFPluginEventResponse {
self
.context
.response
.as_ref()
.expect("must call sync_send/async_send first")
.clone()
}
fn take_request(&mut self) -> AFPluginRequest {
self.context.request.take().expect("must call event first")
}
}
#[derive(Clone)]
pub struct TestContext {
pub sdk: Arc<AppFlowyWASMCore>,
request: Option<AFPluginRequest>,
response: Option<AFPluginEventResponse>,
}
impl TestContext {
pub fn new(sdk: Arc<AppFlowyWASMCore>) -> Self {
Self {
sdk,
request: None,
response: None,
}
}
}

View File

@ -1,2 +0,0 @@
mod event_builder;
pub mod tester;

View File

@ -1,105 +0,0 @@
use crate::util::event_builder::EventBuilder;
use af_user::entities::*;
use af_user::event_map::UserWasmEvent::*;
use af_wasm::core::AppFlowyWASMCore;
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;
pub struct WASMEventTester {
core: Arc<AppFlowyWASMCore>,
}
impl WASMEventTester {
pub async fn new() -> Self {
setup_log();
let config = AFCloudConfiguration {
base_url: "http://localhost".to_string(),
ws_base_url: "ws://localhost/ws/v1".to_string(),
gotrue_url: "http://localhost/gotrue".to_string(),
};
let core = Arc::new(AppFlowyWASMCore::new("device_id", config).await.unwrap());
Self { core }
}
pub async fn sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
let email = email.to_string();
let password = "AppFlowy!2024".to_string();
let payload = AddUserPB {
email: email.clone(),
password: password.clone(),
};
EventBuilder::new(self.core.clone())
.event(AddUser)
.payload(payload)
.async_send()
.await;
let payload = UserSignInPB { email, password };
let user_profile = EventBuilder::new(self.core.clone())
.event(SignInPassword)
.payload(payload)
.async_send()
.await
.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 {
format!("{}@appflowy.io", Uuid::new_v4())
}
pub fn setup_log() {
static START: Once = Once::new();
START.call_once(|| {
tracing_wasm::set_as_global_default();
});
}

View File

@ -1,2 +0,0 @@
[toolchain]
channel = "1.77.2"

View File

@ -1,12 +0,0 @@
# https://rust-lang.github.io/rustfmt/?version=master&search=
max_width = 100
tab_spaces = 2
newline_style = "Auto"
match_block_trailing_comma = true
use_field_init_shorthand = true
use_try_shorthand = true
reorder_imports = true
reorder_modules = true
remove_nested_parens = true
merge_derives = true
edition = "2021"

View File

@ -23,7 +23,6 @@ pub struct ProtoCache {
pub enum Project {
Tauri,
TauriApp,
Web { relative_path: String },
Native,
}
@ -34,7 +33,6 @@ impl Project {
Project::TauriApp => {
"appflowy_web_app/src/application/services/tauri-services/backend".to_string()
},
Project::Web { .. } => "appflowy_web/src/services/backend".to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
@ -42,7 +40,6 @@ impl Project {
pub fn event_root(&self) -> String {
match self {
Project::Tauri | Project::TauriApp => "../../".to_string(),
Project::Web { relative_path } => relative_path.to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
@ -50,7 +47,6 @@ impl Project {
pub fn model_root(&self) -> String {
match self {
Project::Tauri | Project::TauriApp => "../../".to_string(),
Project::Web { relative_path } => relative_path.to_string(),
Project::Native => panic!("Native project is not supported yet."),
}
}
@ -62,13 +58,6 @@ impl Project {
import { Ok, Err, Result } from "ts-results";
import { invoke } from "@tauri-apps/api/tauri";
import * as pb from "../..";
"#
.to_string(),
Project::Web { .. } => r#"
/// Auto generate. Do not edit
import { Ok, Err, Result } from "ts-results";
import { invoke } from "@/application/app.ts";
import * as pb from "../..";
"#
.to_string(),
Project::Native => panic!("Native project is not supported yet."),

View File

@ -1,4 +1,3 @@
[tasks.wasm_build]
script_runner = "bash"
script = [
@ -29,15 +28,6 @@ run_task = { name = [
"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 = ["""

View File

@ -8,7 +8,7 @@ fi
NEW_REV="$1"
echo "New revision: $NEW_REV"
directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web/wasm-libs" "appflowy_web_app/src-tauri")
directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web_app/src-tauri")
for dir in "${directories[@]}"; do
echo "Updating $dir"

View File

@ -8,7 +8,7 @@ fi
NEW_REV="$1"
echo "New revision: $NEW_REV"
directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web/wasm-libs" "appflowy_web_app/src-tauri")
directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web_app/src-tauri")
for dir in "${directories[@]}"; do
echo "Updating $dir"