chore: only sync when doucment was changed (#7081)

* chore: only sync when doucment was changed

* chore: fmt
This commit is contained in:
Nathan.fooo
2024-12-30 10:26:06 +08:00
committed by GitHub
parent 7d61252e6a
commit a521541cb7
30 changed files with 854 additions and 768 deletions

View File

@ -1031,7 +1031,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1056,7 +1056,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-trait",
@ -1096,7 +1096,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1117,7 +1117,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"bytes",
@ -1137,7 +1137,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1159,7 +1159,7 @@ dependencies = [
[[package]]
name = "collab-importer"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-recursion",
@ -1209,6 +1209,9 @@ dependencies = [
"collab-folder",
"collab-plugins",
"collab-user",
"diesel",
"flowy-error",
"flowy-sqlite",
"futures",
"lib-infra",
"serde",
@ -1220,7 +1223,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-stream",
@ -1300,7 +1303,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"collab",
@ -2144,6 +2147,7 @@ dependencies = [
"arc-swap",
"base64 0.21.5",
"bytes",
"collab-integrate",
"dashmap 6.0.1",
"flowy-ai-pub",
"flowy-codegen",

View File

@ -120,14 +120,14 @@ custom-protocol = ["tauri/custom-protocol"]
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
# Working directory: frontend
# To update the commit ID, run:

View File

@ -1029,7 +1029,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1054,7 +1054,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-trait",
@ -1094,7 +1094,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1115,7 +1115,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"bytes",
@ -1135,7 +1135,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1157,7 +1157,7 @@ dependencies = [
[[package]]
name = "collab-importer"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-recursion",
@ -1207,6 +1207,9 @@ dependencies = [
"collab-folder",
"collab-plugins",
"collab-user",
"diesel",
"flowy-error",
"flowy-sqlite",
"futures",
"lib-infra",
"serde",
@ -1218,7 +1221,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-stream",
@ -1298,7 +1301,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"collab",
@ -2189,6 +2192,7 @@ dependencies = [
"arc-swap",
"base64 0.21.7",
"bytes",
"collab-integrate",
"dashmap 6.0.1",
"flowy-ai-pub",
"flowy-codegen",

View File

@ -118,14 +118,14 @@ custom-protocol = ["tauri/custom-protocol"]
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
# Working directory: frontend

View File

@ -892,7 +892,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -917,7 +917,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-trait",
@ -957,7 +957,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -978,7 +978,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"bytes",
@ -998,7 +998,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"arc-swap",
@ -1020,7 +1020,7 @@ dependencies = [
[[package]]
name = "collab-importer"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-recursion",
@ -1070,6 +1070,9 @@ dependencies = [
"collab-folder",
"collab-plugins",
"collab-user",
"diesel",
"flowy-error",
"flowy-sqlite",
"futures",
"lib-infra",
"serde",
@ -1081,7 +1084,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"async-stream",
@ -1161,7 +1164,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.2.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9"
dependencies = [
"anyhow",
"collab",
@ -2001,6 +2004,7 @@ dependencies = [
"arc-swap",
"base64 0.21.5",
"bytes",
"collab-integrate",
"dashmap 6.0.1",
"dotenv",
"flowy-ai-pub",

View File

@ -143,14 +143,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" }
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" }
# Working directory: frontend
# To update the commit ID, run:

View File

@ -24,6 +24,9 @@ tokio = { workspace = true, features = ["sync"] }
lib-infra = { workspace = true }
futures = "0.3"
arc-swap = "1.7"
flowy-sqlite = { workspace = true }
diesel.workspace = true
flowy-error.workspace = true
[features]
default = []

View File

@ -1,24 +1,11 @@
pub use collab::preclude::Snapshot;
pub use collab_plugins::local_storage::CollabPersistenceConfig;
pub use collab_plugins::CollabKVDB;
use collab_plugins::{if_native, if_wasm};
pub mod collab_builder;
pub mod config;
if_native! {
mod native;
mod plugin_provider {
pub use crate::native::plugin_provider::*;
}
}
if_wasm! {
mod wasm;
mod plugin_provider {
pub use crate::wasm::plugin_provider::*;
}
}
pub mod persistence;
mod plugin_provider;
pub use collab_plugins::local_storage::kv::doc::CollabKVAction;
pub use collab_plugins::local_storage::kv::error::PersistenceError;

View File

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

View File

@ -1,57 +0,0 @@
use collab::preclude::CollabPlugin;
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
#[cfg(target_arch = "wasm32")]
pub trait CollabCloudPluginProvider: 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>;
fn is_sync_enabled(&self) -> bool;
}
#[cfg(target_arch = "wasm32")]
impl<U> CollabCloudPluginProvider for std::rc::Rc<U>
where
U: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()
}
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>> {
(**self).get_plugins(context)
}
fn is_sync_enabled(&self) -> bool {
(**self).is_sync_enabled()
}
}
#[cfg(not(target_arch = "wasm32"))]
pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>;
fn is_sync_enabled(&self) -> bool;
}
#[cfg(not(target_arch = "wasm32"))]
impl<U> CollabCloudPluginProvider for std::sync::Arc<U>
where
U: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()
}
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>> {
(**self).get_plugins(context)
}
fn is_sync_enabled(&self) -> bool {
(**self).is_sync_enabled()
}
}

View File

@ -0,0 +1,55 @@
use diesel::upsert::excluded;
use flowy_error::{FlowyError, FlowyResult};
use flowy_sqlite::{
diesel, insert_into,
query_dsl::*,
schema::{af_collab_metadata, af_collab_metadata::dsl},
DBConnection, ExpressionMethods, Identifiable, Insertable, Queryable,
};
use std::collections::HashMap;
#[derive(Queryable, Insertable, Identifiable)]
#[diesel(table_name = af_collab_metadata)]
#[diesel(primary_key(object_id))]
pub struct AFCollabMetadata {
pub object_id: String,
pub updated_at: i64,
pub prev_sync_state_vector: Vec<u8>,
pub collab_type: i32,
}
pub fn batch_insert_collab_metadata(
mut conn: DBConnection,
new_metadata: &[AFCollabMetadata],
) -> FlowyResult<()> {
conn.immediate_transaction(|conn| {
for metadata in new_metadata {
let _ = insert_into(af_collab_metadata::table)
.values(metadata)
.on_conflict(af_collab_metadata::object_id)
.do_update()
.set((
af_collab_metadata::updated_at.eq(excluded(af_collab_metadata::updated_at)),
af_collab_metadata::prev_sync_state_vector
.eq(excluded(af_collab_metadata::prev_sync_state_vector)),
))
.execute(conn)?;
}
Ok::<(), FlowyError>(())
})?;
Ok(())
}
pub fn batch_select_collab_metadata(
mut conn: DBConnection,
object_ids: &[String],
) -> FlowyResult<HashMap<String, AFCollabMetadata>> {
let metadata = dsl::af_collab_metadata
.filter(af_collab_metadata::object_id.eq_any(object_ids))
.load::<AFCollabMetadata>(&mut conn)?
.into_iter()
.map(|m| (m.object_id.clone(), m))
.collect();
Ok(metadata)
}

View File

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

View File

@ -1,9 +1,8 @@
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
use collab::preclude::CollabPlugin;
use lib_infra::future::Fut;
use std::rc::Rc;
pub trait CollabCloudPluginProvider: 'static {
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>;
@ -11,9 +10,9 @@ pub trait CollabCloudPluginProvider: 'static {
fn is_sync_enabled(&self) -> bool;
}
impl<T> CollabCloudPluginProvider for Rc<T>
impl<U> CollabCloudPluginProvider for std::sync::Arc<U>
where
T: CollabCloudPluginProvider,
U: CollabCloudPluginProvider,
{
fn provider_type(&self) -> CollabPluginProviderType {
(**self).provider_type()

View File

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

View File

@ -45,6 +45,7 @@ zip = { workspace = true, features = ["deflate"] }
zip-extensions = "0.8.0"
pin-project = "1.1.5"
flowy-storage-pub = { workspace = true }
collab-integrate.workspace = true
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
notify = "6.1.1"

View File

@ -5,6 +5,7 @@ use crate::entities::{
use crate::local_ai::local_llm_chat::LocalAIController;
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
use crate::persistence::{insert_chat, read_chat_metadata, ChatTable};
use std::collections::HashMap;
use appflowy_plugin::manager::PluginManager;
use dashmap::DashMap;
@ -16,6 +17,9 @@ use flowy_sqlite::kv::KVStorePreferences;
use flowy_sqlite::DBConnection;
use crate::notification::{chat_notification_builder, ChatNotification};
use collab_integrate::persistence::collab_metadata_sql::{
batch_insert_collab_metadata, batch_select_collab_metadata, AFCollabMetadata,
};
use flowy_storage_pub::storage::StorageService;
use lib_infra::async_trait::async_trait;
use lib_infra::util::timestamp;
@ -31,7 +35,6 @@ pub trait AIUserService: Send + Sync + 'static {
fn application_root_dir(&self) -> Result<PathBuf, FlowyError>;
}
/// AIExternalService is an interface for external services that AI plugin can interact with.
#[async_trait]
pub trait AIExternalService: Send + Sync + 'static {
@ -45,7 +48,8 @@ pub trait AIExternalService: Send + Sync + 'static {
&self,
workspace_id: &str,
rag_ids: Vec<String>,
) -> Result<(), FlowyError>;
rag_metadata_map: HashMap<String, AFCollabMetadata>,
) -> Result<Vec<AFCollabMetadata>, FlowyError>;
async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError>;
}
@ -130,14 +134,7 @@ impl AIManager {
.await
{
Ok(settings) => {
if settings.rag_ids.is_empty() {
return;
}
if let Ok(workspace_id) = user_service.workspace_id() {
let _ = external_service
.sync_rag_documents(&workspace_id, settings.rag_ids)
.await;
}
let _ = sync_chat_documents(user_service, external_service, settings.rag_ids).await;
},
Err(err) => {
error!("failed to refresh chat settings: {}", err);
@ -167,7 +164,8 @@ impl AIManager {
}
pub async fn get_chat_info(&self, chat_id: &str) -> FlowyResult<ChatInfoPB> {
let mut conn = self.user_service.sqlite_connection(0)?;
let uid = self.user_service.user_id()?;
let mut conn = self.user_service.sqlite_connection(uid)?;
let metadata = read_chat_metadata(&mut conn, chat_id)?;
let files = metadata
.files
@ -397,17 +395,44 @@ impl AIManager {
let user_service = self.user_service.clone();
let external_service = self.external_service.clone();
tokio::spawn(async move {
if let Ok(workspace_id) = user_service.workspace_id() {
let _ = external_service
.sync_rag_documents(&workspace_id, rag_ids)
.await;
}
});
sync_chat_documents(user_service, external_service, rag_ids).await?;
Ok(())
}
}
async fn sync_chat_documents(
user_service: Arc<dyn AIUserService>,
external_service: Arc<dyn AIExternalService>,
rag_ids: Vec<String>,
) -> FlowyResult<()> {
if rag_ids.is_empty() {
return Ok(());
}
let uid = user_service.user_id()?;
let conn = user_service.sqlite_connection(uid)?;
let metadata_map = batch_select_collab_metadata(conn, &rag_ids)?;
let user_service = user_service.clone();
tokio::spawn(async move {
if let Ok(workspace_id) = user_service.workspace_id() {
if let Ok(metadatas) = external_service
.sync_rag_documents(&workspace_id, rag_ids, metadata_map)
.await
{
if let Ok(uid) = user_service.user_id() {
if let Ok(conn) = user_service.sqlite_connection(uid) {
info!("sync rag documents success: {}", metadatas.len());
batch_insert_collab_metadata(conn, &metadatas).unwrap();
}
}
}
}
});
Ok(())
}
fn save_chat(conn: DBConnection, chat_id: &str) -> FlowyResult<()> {
let row = ChatTable {
chat_id: chat_id.to_string(),

View File

@ -5,11 +5,11 @@ use base64::Engine;
use semver::Version;
use tracing::{error, info};
use crate::log_filter::create_log_filter;
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
use flowy_user::services::entities::URL_SAFE_ENGINE;
use lib_infra::file_util::copy_dir_recursive;
use lib_infra::util::OperatingSystem;
use crate::log_filter::create_log_filter;
#[derive(Clone)]
pub struct AppFlowyCoreConfig {

View File

@ -1,4 +1,10 @@
use collab::core::collab::DataSource;
use collab::core::origin::CollabOrigin;
use collab::preclude::updates::decoder::Decode;
use collab::preclude::{Collab, StateVector};
use collab::util::is_change_since_sv;
use collab_entity::CollabType;
use collab_integrate::persistence::collab_metadata_sql::AFCollabMetadata;
use flowy_ai::ai_manager::{AIExternalService, AIManager, AIUserService};
use flowy_ai_pub::cloud::ChatCloudService;
use flowy_error::FlowyError;
@ -10,6 +16,8 @@ use flowy_sqlite::DBConnection;
use flowy_storage_pub::storage::StorageService;
use flowy_user::services::authenticate_user::AuthenticateUser;
use lib_infra::async_trait::async_trait;
use lib_infra::util::timestamp;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Weak};
use tracing::{error, info};
@ -62,36 +70,70 @@ impl AIExternalService for ChatQueryServiceImpl {
Ok(ids)
}
async fn sync_rag_documents(
&self,
workspace_id: &str,
rag_ids: Vec<String>,
) -> Result<(), FlowyError> {
info!("sync_rag_documents: {:?}", rag_ids);
mut rag_metadata_map: HashMap<String, AFCollabMetadata>,
) -> Result<Vec<AFCollabMetadata>, FlowyError> {
let mut result = Vec::new();
for rag_id in rag_ids.iter() {
if let Some(query_collab) = self
for rag_id in rag_ids {
// Retrieve the collab object for the current rag_id
let query_collab = match self
.folder_service
.get_collab(rag_id, CollabType::Document)
.get_collab(&rag_id, CollabType::Document)
.await
{
let params = FullSyncCollabParams {
object_id: rag_id.clone(),
collab_type: CollabType::Document,
encoded_collab: query_collab.encoded_collab,
};
match self
.folder_cloud_service
.full_sync_collab_object(workspace_id, params)
.await
{
Ok(_) => info!("[Chat] full sync rag document: {}", rag_id),
Err(err) => error!("failed to sync rag document:{} error:{}", rag_id, err),
Some(collab) => collab,
None => {
continue;
},
};
// Check if the state vector exists and detect changes
if let Some(metadata) = rag_metadata_map.remove(&rag_id) {
if let Ok(prev_sv) = StateVector::decode_v1(&metadata.prev_sync_state_vector) {
let collab = Collab::new_with_source(
CollabOrigin::Empty,
&rag_id,
DataSource::DocStateV1(query_collab.encoded_collab.doc_state.to_vec()),
vec![],
false,
)?;
if !is_change_since_sv(&collab, &prev_sv) {
info!("[Chat] no change since sv: {}", rag_id);
continue;
}
}
}
// Perform full sync if changes are detected or no state vector is found
let params = FullSyncCollabParams {
object_id: rag_id.clone(),
collab_type: CollabType::Document,
encoded_collab: query_collab.encoded_collab.clone(),
};
if let Err(err) = self
.folder_cloud_service
.full_sync_collab_object(workspace_id, params)
.await
{
error!("Failed to sync rag document: {} error: {}", rag_id, err);
} else {
info!("[Chat] full sync rag document: {}", rag_id);
result.push(AFCollabMetadata {
object_id: rag_id,
updated_at: timestamp(),
prev_sync_state_vector: query_collab.encoded_collab.state_vector.to_vec(),
collab_type: CollabType::Document as i32,
});
}
}
Ok(())
Ok(result)
}
async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError> {

View File

@ -1,4 +1,3 @@
use std::sync::Arc;
use bytes::Bytes;
use collab::entity::EncodedCollab;
use collab_folder::ViewLayout;
@ -8,71 +7,72 @@ use flowy_folder::entities::CreateViewParams;
use flowy_folder::share::ImportType;
use flowy_folder::view_operation::{FolderOperationHandler, ImportedData};
use lib_infra::async_trait::async_trait;
use std::sync::Arc;
pub struct ChatFolderOperation(pub Arc<AIManager>);
#[async_trait]
impl FolderOperationHandler for ChatFolderOperation {
fn name(&self) -> &str {
"ChatFolderOperationHandler"
}
fn name(&self) -> &str {
"ChatFolderOperationHandler"
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.open_chat(view_id).await
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.open_chat(view_id).await
}
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_chat(view_id).await
}
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_chat(view_id).await
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.delete_chat(view_id).await
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.delete_chat(view_id).await
}
async fn duplicate_view(&self, _view_id: &str) -> Result<Bytes, FlowyError> {
Err(FlowyError::not_support())
}
async fn duplicate_view(&self, _view_id: &str) -> Result<Bytes, FlowyError> {
Err(FlowyError::not_support())
}
async fn create_view_with_view_data(
&self,
_user_id: i64,
_params: CreateViewParams,
) -> Result<Option<EncodedCollab>, FlowyError> {
Err(FlowyError::not_support())
}
async fn create_view_with_view_data(
&self,
_user_id: i64,
_params: CreateViewParams,
) -> Result<Option<EncodedCollab>, FlowyError> {
Err(FlowyError::not_support())
}
async fn create_default_view(
&self,
user_id: i64,
parent_view_id: &str,
view_id: &str,
_name: &str,
_layout: ViewLayout,
) -> Result<(), FlowyError> {
self
.0
.create_chat(&user_id, parent_view_id, view_id)
.await?;
Ok(())
}
async fn create_default_view(
&self,
user_id: i64,
parent_view_id: &str,
view_id: &str,
_name: &str,
_layout: ViewLayout,
) -> Result<(), FlowyError> {
self
.0
.create_chat(&user_id, parent_view_id, view_id)
.await?;
Ok(())
}
async fn import_from_bytes(
&self,
_uid: i64,
_view_id: &str,
_name: &str,
_import_type: ImportType,
_bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
Err(FlowyError::not_support())
}
async fn import_from_bytes(
&self,
_uid: i64,
_view_id: &str,
_name: &str,
_import_type: ImportType,
_bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
Err(FlowyError::not_support())
}
async fn import_from_file_path(
&self,
_view_id: &str,
_name: &str,
_path: String,
) -> Result<(), FlowyError> {
Err(FlowyError::not_support())
}
async fn import_from_file_path(
&self,
_view_id: &str,
_name: &str,
_path: String,
) -> Result<(), FlowyError> {
Err(FlowyError::not_support())
}
}

View File

@ -1,86 +1,88 @@
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use bytes::Bytes;
use collab::entity::EncodedCollab;
use collab_entity::CollabType;
use collab_folder::{View, ViewLayout};
use collab_plugins::local_storage::kv::KVTransactionDB;
use flowy_database2::DatabaseManager;
use flowy_database2::entities::DatabaseLayoutPB;
use flowy_database2::services::share::csv::CSVFormat;
use flowy_database2::template::{make_default_board, make_default_calendar, make_default_grid};
use flowy_database2::DatabaseManager;
use flowy_error::FlowyError;
use flowy_folder::entities::{CreateViewParams, ViewLayoutPB};
use flowy_folder::manager::FolderUser;
use flowy_folder::share::ImportType;
use flowy_folder::view_operation::{DatabaseEncodedCollab, FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData};
use flowy_folder::view_operation::{
DatabaseEncodedCollab, FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData,
};
use flowy_user::services::data_import::{load_collab_by_object_id, load_collab_by_object_ids};
use lib_infra::async_trait::async_trait;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
pub struct DatabaseFolderOperation(pub Arc<DatabaseManager>);
#[async_trait]
impl FolderOperationHandler for DatabaseFolderOperation {
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.open_database_view(view_id).await?;
Ok(())
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.open_database_view(view_id).await?;
Ok(())
}
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_database_view(view_id).await?;
Ok(())
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
match self.0.delete_database_view(view_id).await {
Ok(_) => tracing::trace!("Delete database view: {}", view_id),
Err(e) => tracing::error!("🔴delete database failed: {}", e),
}
Ok(())
}
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_database_view(view_id).await?;
Ok(())
}
async fn gather_publish_encode_collab(
&self,
user: &Arc<dyn FolderUser>,
view_id: &str,
) -> Result<GatherEncodedCollab, FlowyError> {
let workspace_id = user.workspace_id()?;
// get the collab_object_id for the database.
//
// the collab object_id for the database is not the view_id,
// we should use the view_id to get the database_id
let oid = self.0.get_database_id_with_view_id(view_id).await?;
let row_oids = self.0.get_database_row_ids_with_view_id(view_id).await?;
let row_metas = self
.0
.get_database_row_metas_with_view_id(view_id, row_oids.clone())
.await?;
let row_document_ids = row_metas
.iter()
.filter_map(|meta| meta.document_id.clone())
.collect::<Vec<_>>();
let row_oids = row_oids
.into_iter()
.map(|oid| oid.into_inner())
.collect::<Vec<_>>();
let database_metas = self.0.get_all_databases_meta().await;
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
match self.0.delete_database_view(view_id).await {
Ok(_) => tracing::trace!("Delete database view: {}", view_id),
Err(e) => tracing::error!("🔴delete database failed: {}", e),
}
Ok(())
}
let uid = user
.user_id()
.map_err(|e| e.with_context("unable to get the uid: {}"))?;
async fn gather_publish_encode_collab(
&self,
user: &Arc<dyn FolderUser>,
view_id: &str,
) -> Result<GatherEncodedCollab, FlowyError> {
let workspace_id = user.workspace_id()?;
// get the collab_object_id for the database.
//
// the collab object_id for the database is not the view_id,
// we should use the view_id to get the database_id
let oid = self.0.get_database_id_with_view_id(view_id).await?;
let row_oids = self.0.get_database_row_ids_with_view_id(view_id).await?;
let row_metas = self
.0
.get_database_row_metas_with_view_id(view_id, row_oids.clone())
.await?;
let row_document_ids = row_metas
.iter()
.filter_map(|meta| meta.document_id.clone())
.collect::<Vec<_>>();
let row_oids = row_oids
.into_iter()
.map(|oid| oid.into_inner())
.collect::<Vec<_>>();
let database_metas = self.0.get_all_databases_meta().await;
// get the collab db
let collab_db = user
.collab_db(uid)
.map_err(|e| e.with_context("unable to get the collab"))?;
let collab_db = collab_db.upgrade().ok_or_else(|| {
FlowyError::internal().with_context(
"The collab db has been dropped, indicating that the user has switched to a new account",
)
})?;
let uid = user
.user_id()
.map_err(|e| e.with_context("unable to get the uid: {}"))?;
// get the collab db
let collab_db = user
.collab_db(uid)
.map_err(|e| e.with_context("unable to get the collab"))?;
let collab_db = collab_db.upgrade().ok_or_else(|| {
FlowyError::internal().with_context(
"The collab db has been dropped, indicating that the user has switched to a new account",
)
})?;
tokio::task::spawn_blocking(move || {
tokio::task::spawn_blocking(move || {
let collab_read_txn = collab_db.read_txn();
let database_collab = load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, &oid)
.map_err(|e| {
@ -143,196 +145,194 @@ impl FolderOperationHandler for DatabaseFolderOperation {
}))
})
.await?
}
}
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
Ok(Bytes::from(view_id.to_string()))
}
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
Ok(Bytes::from(view_id.to_string()))
}
/// Create a database view with duplicated data.
/// If the ext contains the {"database_id": "xx"}, then it will link
/// to the existing database.
async fn create_view_with_view_data(
&self,
_user_id: i64,
params: CreateViewParams,
) -> Result<Option<EncodedCollab>, FlowyError> {
match CreateDatabaseExtParams::from_map(params.meta.clone()) {
None => match params.initial_data {
ViewData::DuplicateData(data) => {
let duplicated_view_id =
String::from_utf8(data.to_vec()).map_err(|_| FlowyError::invalid_data())?;
let encoded_collab = self
.0
.duplicate_database(&duplicated_view_id, &params.view_id)
.await?;
Ok(Some(encoded_collab))
},
ViewData::Data(data) => {
let encoded_collab = self
.0
.create_database_with_data(&params.view_id, data.to_vec())
.await?;
Ok(Some(encoded_collab))
},
ViewData::Empty => Ok(None),
},
Some(database_params) => {
let layout = match params.layout {
ViewLayoutPB::Board => DatabaseLayoutPB::Board,
ViewLayoutPB::Calendar => DatabaseLayoutPB::Calendar,
ViewLayoutPB::Grid => DatabaseLayoutPB::Grid,
ViewLayoutPB::Document | ViewLayoutPB::Chat => {
return Err(FlowyError::not_support());
},
};
let name = params.name.to_string();
let database_view_id = params.view_id.to_string();
let database_parent_view_id = params.parent_view_id.to_string();
self
.0
.create_linked_view(
name,
layout.into(),
database_params.database_id,
database_view_id,
database_parent_view_id,
)
.await?;
Ok(None)
},
}
}
/// Create a database view with build-in data.
/// If the ext contains the {"database_id": "xx"}, then it will link to
/// the existing database. The data of the database will be shared within
/// these references views.
async fn create_default_view(
&self,
_user_id: i64,
_parent_view_id: &str,
view_id: &str,
name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
let name = name.to_string();
let data = match layout {
ViewLayout::Grid => make_default_grid(view_id, &name),
ViewLayout::Board => make_default_board(view_id, &name),
ViewLayout::Calendar => make_default_calendar(view_id, &name),
ViewLayout::Document | ViewLayout::Chat => {
return Err(
FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout)),
);
},
};
let result = self.0.import_database(data).await;
match result {
Ok(_) => Ok(()),
Err(err) => {
if err.is_already_exists() {
Ok(())
} else {
Err(err)
}
},
}
}
async fn import_from_bytes(
&self,
_uid: i64,
view_id: &str,
_name: &str,
import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
let format = match import_type {
ImportType::CSV => CSVFormat::Original,
ImportType::AFDatabase => CSVFormat::META,
_ => CSVFormat::Original,
};
let content = tokio::task::spawn_blocking(move || {
String::from_utf8(bytes).map_err(|err| FlowyError::internal().with_context(err))
})
.await??;
let result = self
/// Create a database view with duplicated data.
/// If the ext contains the {"database_id": "xx"}, then it will link
/// to the existing database.
async fn create_view_with_view_data(
&self,
_user_id: i64,
params: CreateViewParams,
) -> Result<Option<EncodedCollab>, FlowyError> {
match CreateDatabaseExtParams::from_map(params.meta.clone()) {
None => match params.initial_data {
ViewData::DuplicateData(data) => {
let duplicated_view_id =
String::from_utf8(data.to_vec()).map_err(|_| FlowyError::invalid_data())?;
let encoded_collab = self
.0
.import_csv(view_id.to_string(), content, format)
.duplicate_database(&duplicated_view_id, &params.view_id)
.await?;
Ok(
result
.encoded_collabs
.into_iter()
.map(|encoded| {
(
encoded.object_id,
encoded.collab_type,
encoded.encoded_collab,
)
})
.collect(),
)
}
async fn import_from_file_path(
&self,
view_id: &str,
_name: &str,
path: String,
) -> Result<(), FlowyError> {
let file_path = Path::new(&path);
if !file_path.exists() {
return Err(FlowyError::record_not_found().with_context("File not found"));
}
let data = tokio::fs::read(file_path).await?;
let content =
String::from_utf8(data).map_err(|e| FlowyError::invalid_data().with_context(e))?;
let _ = self
Ok(Some(encoded_collab))
},
ViewData::Data(data) => {
let encoded_collab = self
.0
.import_csv(view_id.to_string(), content, CSVFormat::Original)
.create_database_with_data(&params.view_id, data.to_vec())
.await?;
Ok(())
}
async fn did_update_view(&self, old: &View, new: &View) -> Result<(), FlowyError> {
let database_layout = match new.layout {
ViewLayout::Document | ViewLayout::Chat => {
return Err(FlowyError::internal().with_context("Can't handle document layout type"));
},
ViewLayout::Grid => DatabaseLayoutPB::Grid,
ViewLayout::Board => DatabaseLayoutPB::Board,
ViewLayout::Calendar => DatabaseLayoutPB::Calendar,
Ok(Some(encoded_collab))
},
ViewData::Empty => Ok(None),
},
Some(database_params) => {
let layout = match params.layout {
ViewLayoutPB::Board => DatabaseLayoutPB::Board,
ViewLayoutPB::Calendar => DatabaseLayoutPB::Calendar,
ViewLayoutPB::Grid => DatabaseLayoutPB::Grid,
ViewLayoutPB::Document | ViewLayoutPB::Chat => {
return Err(FlowyError::not_support());
},
};
let name = params.name.to_string();
let database_view_id = params.view_id.to_string();
let database_parent_view_id = params.parent_view_id.to_string();
self
.0
.create_linked_view(
name,
layout.into(),
database_params.database_id,
database_view_id,
database_parent_view_id,
)
.await?;
Ok(None)
},
}
}
if old.layout != new.layout {
self
.0
.update_database_layout(&new.id, database_layout)
.await?;
Ok(())
/// Create a database view with build-in data.
/// If the ext contains the {"database_id": "xx"}, then it will link to
/// the existing database. The data of the database will be shared within
/// these references views.
async fn create_default_view(
&self,
_user_id: i64,
_parent_view_id: &str,
view_id: &str,
name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
let name = name.to_string();
let data = match layout {
ViewLayout::Grid => make_default_grid(view_id, &name),
ViewLayout::Board => make_default_board(view_id, &name),
ViewLayout::Calendar => make_default_calendar(view_id, &name),
ViewLayout::Document | ViewLayout::Chat => {
return Err(
FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout)),
);
},
};
let result = self.0.import_database(data).await;
match result {
Ok(_) => Ok(()),
Err(err) => {
if err.is_already_exists() {
Ok(())
} else {
Ok(())
Err(err)
}
},
}
}
async fn import_from_bytes(
&self,
_uid: i64,
view_id: &str,
_name: &str,
import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
let format = match import_type {
ImportType::CSV => CSVFormat::Original,
ImportType::AFDatabase => CSVFormat::META,
_ => CSVFormat::Original,
};
let content = tokio::task::spawn_blocking(move || {
String::from_utf8(bytes).map_err(|err| FlowyError::internal().with_context(err))
})
.await??;
let result = self
.0
.import_csv(view_id.to_string(), content, format)
.await?;
Ok(
result
.encoded_collabs
.into_iter()
.map(|encoded| {
(
encoded.object_id,
encoded.collab_type,
encoded.encoded_collab,
)
})
.collect(),
)
}
async fn import_from_file_path(
&self,
view_id: &str,
_name: &str,
path: String,
) -> Result<(), FlowyError> {
let file_path = Path::new(&path);
if !file_path.exists() {
return Err(FlowyError::record_not_found().with_context("File not found"));
}
fn name(&self) -> &str {
"DatabaseFolderOperationHandler"
let data = tokio::fs::read(file_path).await?;
let content =
String::from_utf8(data).map_err(|e| FlowyError::invalid_data().with_context(e))?;
let _ = self
.0
.import_csv(view_id.to_string(), content, CSVFormat::Original)
.await?;
Ok(())
}
async fn did_update_view(&self, old: &View, new: &View) -> Result<(), FlowyError> {
let database_layout = match new.layout {
ViewLayout::Document | ViewLayout::Chat => {
return Err(FlowyError::internal().with_context("Can't handle document layout type"));
},
ViewLayout::Grid => DatabaseLayoutPB::Grid,
ViewLayout::Board => DatabaseLayoutPB::Board,
ViewLayout::Calendar => DatabaseLayoutPB::Calendar,
};
if old.layout != new.layout {
self
.0
.update_database_layout(&new.id, database_layout)
.await?;
Ok(())
} else {
Ok(())
}
}
fn name(&self) -> &str {
"DatabaseFolderOperationHandler"
}
}
#[derive(Debug, serde::Deserialize)]
struct CreateDatabaseExtParams {
database_id: String,
database_id: String,
}
impl CreateDatabaseExtParams {
pub fn from_map(map: HashMap<String, String>) -> Option<Self> {
let value = serde_json::to_value(map).ok()?;
serde_json::from_value::<Self>(value).ok()
}
pub fn from_map(map: HashMap<String, String>) -> Option<Self> {
let value = serde_json::to_value(map).ok()?;
serde_json::from_value::<Self>(value).ok()
}
}

View File

@ -1,11 +1,9 @@
use std::convert::TryFrom;
use std::sync::Arc;
use crate::deps_resolve::folder_deps::get_encoded_collab_v1_from_disk;
use bytes::Bytes;
use collab::entity::EncodedCollab;
use collab_entity::CollabType;
use collab_folder::hierarchy_builder::NestedViewBuilder;
use collab_folder::ViewLayout;
use tokio::sync::RwLock;
use flowy_document::entities::DocumentDataPB;
use flowy_document::manager::DocumentManager;
use flowy_document::parser::json::parser::JsonToDocumentParser;
@ -13,148 +11,152 @@ use flowy_error::FlowyError;
use flowy_folder::entities::{CreateViewParams, ViewLayoutPB};
use flowy_folder::manager::FolderUser;
use flowy_folder::share::ImportType;
use flowy_folder::view_operation::{FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData};
use flowy_folder::view_operation::{
FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData,
};
use lib_dispatch::prelude::ToBytes;
use lib_infra::async_trait::async_trait;
use crate::deps_resolve::folder_deps::get_encoded_collab_v1_from_disk;
use std::convert::TryFrom;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct DocumentFolderOperation(pub Arc<DocumentManager>);
#[async_trait]
impl FolderOperationHandler for DocumentFolderOperation {
fn name(&self) -> &str {
"DocumentFolderOperationHandler"
}
fn name(&self) -> &str {
"DocumentFolderOperationHandler"
}
async fn create_workspace_view(
&self,
uid: i64,
workspace_view_builder: Arc<RwLock<NestedViewBuilder>>,
) -> Result<(), FlowyError> {
let manager = self.0.clone();
let mut write_guard = workspace_view_builder.write().await;
// Create a view named "Getting started" with an icon ⭐️ and the built-in README data.
// Don't modify this code unless you know what you are doing.
write_guard
.with_view_builder(|view_builder| async {
let view = view_builder
.with_name("Getting started")
.with_icon("⭐️")
.build();
// create a empty document
let json_str = include_str!("../../../assets/read_me.json");
let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap();
manager
.create_document(uid, &view.view.id, Some(document_pb.into()))
.await
.unwrap();
view
})
.await;
Ok(())
}
async fn create_workspace_view(
&self,
uid: i64,
workspace_view_builder: Arc<RwLock<NestedViewBuilder>>,
) -> Result<(), FlowyError> {
let manager = self.0.clone();
let mut write_guard = workspace_view_builder.write().await;
// Create a view named "Getting started" with an icon ⭐️ and the built-in README data.
// Don't modify this code unless you know what you are doing.
write_guard
.with_view_builder(|view_builder| async {
let view = view_builder
.with_name("Getting started")
.with_icon("⭐️")
.build();
// create a empty document
let json_str = include_str!("../../../assets/read_me.json");
let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap();
manager
.create_document(uid, &view.view.id, Some(document_pb.into()))
.await
.unwrap();
view
})
.await;
Ok(())
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.open_document(view_id).await?;
Ok(())
}
async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.open_document(view_id).await?;
Ok(())
}
/// Close the document view.
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_document(view_id).await?;
Ok(())
}
/// Close the document view.
async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> {
self.0.close_document(view_id).await?;
Ok(())
}
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
match self.0.delete_document(view_id).await {
Ok(_) => tracing::trace!("Delete document: {}", view_id),
Err(e) => tracing::error!("🔴delete document failed: {}", e),
async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> {
match self.0.delete_document(view_id).await {
Ok(_) => tracing::trace!("Delete document: {}", view_id),
Err(e) => tracing::error!("🔴delete document failed: {}", e),
}
Ok(())
}
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
let data: DocumentDataPB = self.0.get_document_data(view_id).await?.into();
let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?;
Ok(data_bytes)
}
async fn gather_publish_encode_collab(
&self,
user: &Arc<dyn FolderUser>,
view_id: &str,
) -> Result<GatherEncodedCollab, FlowyError> {
let encoded_collab =
get_encoded_collab_v1_from_disk(user, view_id, CollabType::Document).await?;
Ok(GatherEncodedCollab::Document(encoded_collab))
}
async fn create_view_with_view_data(
&self,
user_id: i64,
params: CreateViewParams,
) -> Result<Option<EncodedCollab>, FlowyError> {
debug_assert_eq!(params.layout, ViewLayoutPB::Document);
let data = match params.initial_data {
ViewData::DuplicateData(data) => Some(DocumentDataPB::try_from(data)?),
ViewData::Data(data) => Some(DocumentDataPB::try_from(data)?),
ViewData::Empty => None,
};
let encoded_collab = self
.0
.create_document(user_id, &params.view_id, data.map(|d| d.into()))
.await?;
Ok(Some(encoded_collab))
}
/// Create a view with built-in data.
async fn create_default_view(
&self,
user_id: i64,
_parent_view_id: &str,
view_id: &str,
_name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
debug_assert_eq!(layout, ViewLayout::Document);
match self.0.create_document(user_id, view_id, None).await {
Ok(_) => Ok(()),
Err(err) => {
if err.is_already_exists() {
Ok(())
} else {
Err(err)
}
Ok(())
},
}
}
async fn duplicate_view(&self, view_id: &str) -> Result<Bytes, FlowyError> {
let data: DocumentDataPB = self.0.get_document_data(view_id).await?.into();
let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?;
Ok(data_bytes)
}
async fn import_from_bytes(
&self,
uid: i64,
view_id: &str,
_name: &str,
_import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
let data = DocumentDataPB::try_from(Bytes::from(bytes))?;
let encoded_collab = self
.0
.create_document(uid, view_id, Some(data.into()))
.await?;
Ok(vec![(
view_id.to_string(),
CollabType::Document,
encoded_collab,
)])
}
async fn gather_publish_encode_collab(
&self,
user: &Arc<dyn FolderUser>,
view_id: &str,
) -> Result<GatherEncodedCollab, FlowyError> {
let encoded_collab =
get_encoded_collab_v1_from_disk(user, view_id, CollabType::Document).await?;
Ok(GatherEncodedCollab::Document(encoded_collab))
}
async fn create_view_with_view_data(
&self,
user_id: i64,
params: CreateViewParams,
) -> Result<Option<EncodedCollab>, FlowyError> {
debug_assert_eq!(params.layout, ViewLayoutPB::Document);
let data = match params.initial_data {
ViewData::DuplicateData(data) => Some(DocumentDataPB::try_from(data)?),
ViewData::Data(data) => Some(DocumentDataPB::try_from(data)?),
ViewData::Empty => None,
};
let encoded_collab = self
.0
.create_document(user_id, &params.view_id, data.map(|d| d.into()))
.await?;
Ok(Some(encoded_collab))
}
/// Create a view with built-in data.
async fn create_default_view(
&self,
user_id: i64,
_parent_view_id: &str,
view_id: &str,
_name: &str,
layout: ViewLayout,
) -> Result<(), FlowyError> {
debug_assert_eq!(layout, ViewLayout::Document);
match self.0.create_document(user_id, view_id, None).await {
Ok(_) => Ok(()),
Err(err) => {
if err.is_already_exists() {
Ok(())
} else {
Err(err)
}
},
}
}
async fn import_from_bytes(
&self,
uid: i64,
view_id: &str,
_name: &str,
_import_type: ImportType,
bytes: Vec<u8>,
) -> Result<Vec<ImportedData>, FlowyError> {
let data = DocumentDataPB::try_from(Bytes::from(bytes))?;
let encoded_collab = self
.0
.create_document(uid, view_id, Some(data.into()))
.await?;
Ok(vec![(
view_id.to_string(),
CollabType::Document,
encoded_collab,
)])
}
async fn import_from_file_path(
&self,
_view_id: &str,
_name: &str,
_path: String,
) -> Result<(), FlowyError> {
// TODO(lucas): import file from local markdown file
Ok(())
}
async fn import_from_file_path(
&self,
_view_id: &str,
_name: &str,
_path: String,
) -> Result<(), FlowyError> {
// TODO(lucas): import file from local markdown file
Ok(())
}
}

View File

@ -2,6 +2,7 @@ mod folder_deps_chat_impl;
mod folder_deps_database_impl;
mod folder_deps_doc_impl;
use crate::server_layer::ServerProvider;
use collab_entity::{CollabType, EncodedCollab};
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use collab_integrate::CollabKVDB;
@ -9,239 +10,238 @@ use flowy_ai::ai_manager::AIManager;
use flowy_database2::DatabaseManager;
use flowy_document::manager::DocumentManager;
use flowy_error::{internal_error, FlowyError, FlowyResult};
use flowy_folder::entities::{ UpdateViewParams};
use flowy_folder::entities::UpdateViewParams;
use flowy_folder::manager::{FolderManager, FolderUser};
use flowy_folder::ViewLayout;
use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_sqlite::kv::KVStorePreferences;
use flowy_user::services::authenticate_user::AuthenticateUser;
use flowy_user::services::data_import::{load_collab_by_object_id};
use flowy_user::services::data_import::load_collab_by_object_id;
use std::sync::{Arc, Weak};
use crate::server_layer::ServerProvider;
use collab_plugins::local_storage::kv::KVTransactionDB;
use flowy_folder_pub::query::{FolderQueryService, FolderService, FolderViewEdit, QueryCollab};
use lib_infra::async_trait::async_trait;
use crate::deps_resolve::folder_deps::folder_deps_chat_impl::ChatFolderOperation;
use crate::deps_resolve::folder_deps::folder_deps_database_impl::DatabaseFolderOperation;
use crate::deps_resolve::folder_deps::folder_deps_doc_impl::DocumentFolderOperation;
use collab_plugins::local_storage::kv::KVTransactionDB;
use flowy_folder_pub::query::{FolderQueryService, FolderService, FolderViewEdit, QueryCollab};
use lib_infra::async_trait::async_trait;
pub struct FolderDepsResolver();
#[allow(clippy::too_many_arguments)]
impl FolderDepsResolver {
pub async fn resolve(
authenticate_user: Weak<AuthenticateUser>,
collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Arc<ServerProvider>,
folder_indexer: Arc<FolderIndexManagerImpl>,
store_preferences: Arc<KVStorePreferences>,
) -> Arc<FolderManager> {
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl {
authenticate_user: authenticate_user.clone(),
});
pub async fn resolve(
authenticate_user: Weak<AuthenticateUser>,
collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Arc<ServerProvider>,
folder_indexer: Arc<FolderIndexManagerImpl>,
store_preferences: Arc<KVStorePreferences>,
) -> Arc<FolderManager> {
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl {
authenticate_user: authenticate_user.clone(),
});
Arc::new(
FolderManager::new(
user.clone(),
collab_builder,
server_provider.clone(),
folder_indexer,
store_preferences,
)
.unwrap(),
)
}
Arc::new(
FolderManager::new(
user.clone(),
collab_builder,
server_provider.clone(),
folder_indexer,
store_preferences,
)
.unwrap(),
)
}
}
pub fn register_handlers(
folder_manager: &Arc<FolderManager>,
document_manager: Arc<DocumentManager>,
database_manager: Arc<DatabaseManager>,
chat_manager: Arc<AIManager>,
folder_manager: &Arc<FolderManager>,
document_manager: Arc<DocumentManager>,
database_manager: Arc<DatabaseManager>,
chat_manager: Arc<AIManager>,
) {
let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager));
folder_manager.register_operation_handler(ViewLayout::Document, document_folder_operation);
let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager));
folder_manager.register_operation_handler(ViewLayout::Document, document_folder_operation);
let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager));
let chat_folder_operation = Arc::new(ChatFolderOperation(chat_manager));
folder_manager.register_operation_handler(ViewLayout::Board, database_folder_operation.clone());
folder_manager.register_operation_handler(ViewLayout::Grid, database_folder_operation.clone());
folder_manager.register_operation_handler(ViewLayout::Calendar, database_folder_operation);
folder_manager.register_operation_handler(ViewLayout::Chat, chat_folder_operation);
let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager));
let chat_folder_operation = Arc::new(ChatFolderOperation(chat_manager));
folder_manager.register_operation_handler(ViewLayout::Board, database_folder_operation.clone());
folder_manager.register_operation_handler(ViewLayout::Grid, database_folder_operation.clone());
folder_manager.register_operation_handler(ViewLayout::Calendar, database_folder_operation);
folder_manager.register_operation_handler(ViewLayout::Chat, chat_folder_operation);
}
struct FolderUserImpl {
authenticate_user: Weak<AuthenticateUser>,
authenticate_user: Weak<AuthenticateUser>,
}
impl FolderUserImpl {
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.authenticate_user
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.authenticate_user
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
}
impl FolderUser for FolderUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self.upgrade_user()?.user_id()
}
fn user_id(&self) -> Result<i64, FlowyError> {
self.upgrade_user()?.user_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_id()
}
fn workspace_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_id()
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self.upgrade_user()?.get_collab_db(uid)
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self.upgrade_user()?.get_collab_db(uid)
}
fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &str) -> FlowyResult<bool> {
self.upgrade_user()?.is_collab_on_disk(uid, workspace_id)
}
fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &str) -> FlowyResult<bool> {
self.upgrade_user()?.is_collab_on_disk(uid, workspace_id)
}
}
#[derive(Clone)]
pub struct FolderServiceImpl {
folder_manager: Weak<FolderManager>,
user: Arc<dyn FolderUser>,
folder_manager: Weak<FolderManager>,
user: Arc<dyn FolderUser>,
}
impl FolderService for FolderServiceImpl {}
impl FolderServiceImpl {
pub fn new(
folder_manager: Weak<FolderManager>,
authenticate_user: Weak<AuthenticateUser>,
) -> Self {
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl { authenticate_user });
Self {
folder_manager,
user,
}
pub fn new(
folder_manager: Weak<FolderManager>,
authenticate_user: Weak<AuthenticateUser>,
) -> Self {
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl { authenticate_user });
Self {
folder_manager,
user,
}
}
}
#[async_trait]
impl FolderViewEdit for FolderServiceImpl {
async fn set_view_title_if_empty(&self, view_id: &str, title: &str) -> FlowyResult<()> {
if title.is_empty() {
return Ok(());
}
if let Some(folder_manager) = self.folder_manager.upgrade() {
if let Ok(view) = folder_manager.get_view(view_id).await {
if view.name.is_empty() {
let title = if title.len() > 50 {
title.chars().take(50).collect()
} else {
title.to_string()
};
folder_manager
.update_view_with_params(UpdateViewParams {
view_id: view_id.to_string(),
name: Some(title),
desc: None,
thumbnail: None,
layout: None,
is_favorite: None,
extra: None,
})
.await?;
}
}
}
Ok(())
async fn set_view_title_if_empty(&self, view_id: &str, title: &str) -> FlowyResult<()> {
if title.is_empty() {
return Ok(());
}
if let Some(folder_manager) = self.folder_manager.upgrade() {
if let Ok(view) = folder_manager.get_view(view_id).await {
if view.name.is_empty() {
let title = if title.len() > 50 {
title.chars().take(50).collect()
} else {
title.to_string()
};
folder_manager
.update_view_with_params(UpdateViewParams {
view_id: view_id.to_string(),
name: Some(title),
desc: None,
thumbnail: None,
layout: None,
is_favorite: None,
extra: None,
})
.await?;
}
}
}
Ok(())
}
}
#[async_trait]
impl FolderQueryService for FolderServiceImpl {
async fn get_surrounding_view_ids_with_view_layout(
&self,
parent_view_id: &str,
view_layout: ViewLayout,
) -> Vec<String> {
let folder_manager = match self.folder_manager.upgrade() {
Some(folder_manager) => folder_manager,
None => return vec![],
};
async fn get_surrounding_view_ids_with_view_layout(
&self,
parent_view_id: &str,
view_layout: ViewLayout,
) -> Vec<String> {
let folder_manager = match self.folder_manager.upgrade() {
Some(folder_manager) => folder_manager,
None => return vec![],
};
if let Ok(view) = folder_manager.get_view(parent_view_id).await {
if view.space_info().is_some() {
return vec![];
if let Ok(view) = folder_manager.get_view(parent_view_id).await {
if view.space_info().is_some() {
return vec![];
}
}
match folder_manager
.get_untrashed_views_belong_to(parent_view_id)
.await
{
Ok(views) => {
let mut children = views
.into_iter()
.filter_map(|child| {
if child.layout == view_layout {
Some(child.id.clone())
} else {
None
}
}
match folder_manager
.get_untrashed_views_belong_to(parent_view_id)
.await
{
Ok(views) => {
let mut children = views
.into_iter()
.filter_map(|child| {
if child.layout == view_layout {
Some(child.id.clone())
} else {
None
}
})
.collect::<Vec<_>>();
children.push(parent_view_id.to_string());
children
},
_ => vec![],
}
})
.collect::<Vec<_>>();
children.push(parent_view_id.to_string());
children
},
_ => vec![],
}
}
async fn get_collab(&self, object_id: &str, collab_type: CollabType) -> Option<QueryCollab> {
let encode_collab = get_encoded_collab_v1_from_disk(&self.user, object_id, collab_type.clone())
.await
.ok();
async fn get_collab(&self, object_id: &str, collab_type: CollabType) -> Option<QueryCollab> {
let encode_collab = get_encoded_collab_v1_from_disk(&self.user, object_id, collab_type.clone())
.await
.ok();
encode_collab.map(|encoded_collab| QueryCollab {
collab_type,
encoded_collab,
})
}
encode_collab.map(|encoded_collab| QueryCollab {
collab_type,
encoded_collab,
})
}
}
#[inline]
async fn get_encoded_collab_v1_from_disk(
user: &Arc<dyn FolderUser>,
view_id: &str,
collab_type: CollabType,
user: &Arc<dyn FolderUser>,
view_id: &str,
collab_type: CollabType,
) -> Result<EncodedCollab, FlowyError> {
let workspace_id = user.workspace_id()?;
let uid = user
.user_id()
.map_err(|e| e.with_context("unable to get the uid: {}"))?;
let workspace_id = user.workspace_id()?;
let uid = user
.user_id()
.map_err(|e| e.with_context("unable to get the uid: {}"))?;
// get the collab db
let collab_db = user
.collab_db(uid)
.map_err(|e| e.with_context("unable to get the collab"))?;
let collab_db = collab_db.upgrade().ok_or_else(|| {
FlowyError::internal().with_context(
"The collab db has been dropped, indicating that the user has switched to a new account",
)
// get the collab db
let collab_db = user
.collab_db(uid)
.map_err(|e| e.with_context("unable to get the collab"))?;
let collab_db = collab_db.upgrade().ok_or_else(|| {
FlowyError::internal().with_context(
"The collab db has been dropped, indicating that the user has switched to a new account",
)
})?;
let collab_read_txn = collab_db.read_txn();
let collab =
load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, view_id).map_err(|e| {
FlowyError::internal().with_context(format!("load document collab failed: {}", e))
})?;
let collab_read_txn = collab_db.read_txn();
let collab =
load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, view_id).map_err(|e| {
FlowyError::internal().with_context(format!("load document collab failed: {}", e))
})?;
tokio::task::spawn_blocking(move || {
let data = collab
.encode_collab_v1(|collab| collab_type.validate_require_data(collab))
.map_err(|e| {
FlowyError::internal().with_context(format!("encode document collab failed: {}", e))
})?;
Ok::<_, FlowyError>(data)
})
.await
.map_err(internal_error)?
tokio::task::spawn_blocking(move || {
let data = collab
.encode_collab_v1(|collab| collab_type.validate_require_data(collab))
.map_err(|e| {
FlowyError::internal().with_context(format!("encode document collab failed: {}", e))
})?;
Ok::<_, FlowyError>(data)
})
.await
.map_err(internal_error)?
}

View File

@ -10,10 +10,10 @@ mod collab_deps;
mod document_deps;
mod chat_deps;
mod cloud_service_impl;
mod database_deps;
pub mod file_storage_deps;
mod folder_deps;
pub(crate) mod reminder_deps;
mod search_deps;
mod user_deps;
mod folder_deps;
mod cloud_service_impl;
pub(crate) mod reminder_deps;

View File

@ -32,17 +32,17 @@ use module::make_plugins;
use crate::config::AppFlowyCoreConfig;
use crate::deps_resolve::file_storage_deps::FileStorageResolver;
use crate::deps_resolve::*;
use deps_resolve::reminder_deps::CollabInteractImpl;
use user_state_callback::UserStatusCallbackImpl;
use crate::log_filter::init_log;
use crate::server_layer::{current_server_type, Server, ServerProvider};
use deps_resolve::reminder_deps::CollabInteractImpl;
use user_state_callback::UserStatusCallbackImpl;
pub mod config;
mod deps_resolve;
pub mod module;
pub(crate) mod user_state_callback;
pub(crate) mod server_layer;
mod log_filter;
pub mod module;
pub(crate) mod server_layer;
pub(crate) mod user_state_callback;
/// This name will be used as to identify the current [AppFlowyCore] instance.
/// Don't change this.

View File

@ -43,7 +43,6 @@ getrandom = { version = "0.2", features = ["js"] }
[dev-dependencies]
tempfile = "3.4.0"
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
collab-integrate = { workspace = true }
tokio = { workspace = true, features = ["rt", "rt-multi-thread"] }
[build-dependencies]

View File

@ -36,7 +36,7 @@ client-api = { workspace = true, optional = true }
tantivy = { version = "0.22.0", optional = true }
[features]
default = ["impl_from_dispatch_error", "impl_from_serde", "impl_from_reqwest"]
default = ["impl_from_dispatch_error", "impl_from_serde", "impl_from_reqwest", "impl_from_sqlite"]
impl_from_dispatch_error = ["lib-dispatch"]
impl_from_serde = []
impl_from_reqwest = ["reqwest"]

View File

@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP TABLE af_collab_metadata;

View File

@ -0,0 +1,7 @@
-- Your SQL goes here
CREATE TABLE af_collab_metadata (
object_id TEXT PRIMARY KEY NOT NULL,
updated_at BIGINT NOT NULL,
prev_sync_state_vector BLOB NOT NULL,
collab_type INTEGER NOT NULL
);

View File

@ -1,5 +1,14 @@
// @generated automatically by Diesel CLI.
diesel::table! {
af_collab_metadata (object_id) {
object_id -> Text,
updated_at -> BigInt,
prev_sync_state_vector -> Binary,
collab_type -> Integer,
}
}
diesel::table! {
chat_local_setting_table (chat_id) {
chat_id -> Text,
@ -119,6 +128,7 @@ diesel::table! {
}
diesel::allow_tables_to_appear_in_same_query!(
af_collab_metadata,
chat_local_setting_table,
chat_message_table,
chat_table,

View File

@ -36,7 +36,7 @@ use crate::migrations::workspace_trash_v1::WorkspaceTrashMapToSectionMigration;
use crate::migrations::AnonUser;
use crate::services::authenticate_user::AuthenticateUser;
use crate::services::cloud_config::get_cloud_config;
use crate::services::collab_interact::{UserReminder, DefaultCollabInteract};
use crate::services::collab_interact::{DefaultCollabInteract, UserReminder};
use super::manager_user_workspace::save_user_workspace;
use crate::migrations::doc_key_with_workspace::CollabDocKeyWithWorkspaceIdMigration;