chore: checking workspace state consistent after switching workspace (#5201)

* refactor: getting workspace id

* refactor: check workspace id is match for http response

* refactor: check http repsonse in valid by checing the workspace id

* chore: update log

* chore: fix test

* chore: fix test

* chore: add test

* chore: update test
This commit is contained in:
Nathan.fooo 2024-04-26 09:44:07 +08:00 committed by GitHub
parent 65a289648e
commit cc66147bc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 980 additions and 575 deletions

View File

@ -184,12 +184,6 @@ void _resolveHomeDeps(GetIt getIt) {
(user, _) => UserListener(userProfile: user),
);
getIt.registerFactoryParam<WorkspaceBloc, UserProfilePB, void>(
(user, _) => WorkspaceBloc(
userService: UserBackendService(userId: user.id),
),
);
// share
getIt.registerFactoryParam<DocumentShareBloc, ViewPB, void>(
(view, _) => DocumentShareBloc(view: view),

View File

@ -162,7 +162,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -740,7 +740,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"again",
"anyhow",
@ -786,7 +786,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"futures-channel",
"futures-util",
@ -860,7 +860,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -884,7 +884,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -914,7 +914,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -933,7 +933,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"bytes",
@ -948,7 +948,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"chrono",
@ -986,7 +986,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-stream",
@ -1025,7 +1025,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -1050,7 +1050,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -1064,7 +1064,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -1404,7 +1404,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -2770,7 +2770,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"futures-util",
@ -2787,7 +2787,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -3219,7 +3219,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"reqwest",
@ -5707,7 +5707,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",

View File

@ -87,7 +87,7 @@ yrs = { git = "https://github.com/appflowy/y-crdt", rev = "3f25bb510ca5274e7657d
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab.
# Working directory: frontend
#
@ -97,10 +97,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fda
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -112,6 +112,7 @@ version = "0.1.0"
dependencies = [
"af-persistence",
"af-user",
"anyhow",
"collab",
"collab-integrate",
"console_error_panic_hook",
@ -215,7 +216,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -541,7 +542,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"again",
"anyhow",
@ -587,7 +588,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"futures-channel",
"futures-util",
@ -631,7 +632,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -655,7 +656,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -674,7 +675,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"bytes",
@ -689,7 +690,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"chrono",
@ -727,7 +728,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-stream",
@ -765,7 +766,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -790,7 +791,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -804,7 +805,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -1001,7 +1002,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -1774,7 +1775,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"futures-util",
@ -1791,7 +1792,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -2092,7 +2093,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"reqwest",
@ -3718,7 +3719,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -4980,4 +4981,4 @@ dependencies = [
[[patch.unused]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"

View File

@ -55,7 +55,7 @@ codegen-units = 1
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab.
# Working directory: frontend
#
@ -65,10 +65,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fda
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -36,7 +36,7 @@ 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

View File

@ -4,7 +4,7 @@ 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;
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, WorkspaceCollabIntegrate};
use flowy_document::manager::DocumentManager;
use flowy_error::FlowyResult;
use flowy_folder::manager::FolderManager;
@ -27,13 +27,13 @@ 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 collab_builder = Arc::new(AppFlowyCollabBuilder::new(
server_provider.clone(),
device_id.to_string(),
));
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),
@ -75,3 +75,15 @@ impl AppFlowyWASMCore {
})
}
}
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

@ -153,7 +153,7 @@ checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -714,7 +714,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"again",
"anyhow",
@ -760,7 +760,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"futures-channel",
"futures-util",
@ -843,7 +843,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -867,7 +867,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -897,7 +897,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -916,7 +916,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"bytes",
@ -931,7 +931,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"chrono",
@ -969,7 +969,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-stream",
@ -1008,7 +1008,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -1033,7 +1033,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -1047,7 +1047,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -1391,7 +1391,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -2844,7 +2844,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"futures-util",
@ -2861,7 +2861,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -3298,7 +3298,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"reqwest",
@ -5802,7 +5802,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",

View File

@ -86,7 +86,7 @@ yrs = { git = "https://github.com/appflowy/y-crdt", rev = "3f25bb510ca5274e7657d
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab.
# Working directory: frontend
#
@ -96,10 +96,10 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fda
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -163,7 +163,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -696,7 +696,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"again",
"anyhow",
@ -742,7 +742,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"futures-channel",
"futures-util",
@ -785,7 +785,7 @@ dependencies = [
[[package]]
name = "collab"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -809,7 +809,7 @@ dependencies = [
[[package]]
name = "collab-database"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-trait",
@ -839,7 +839,7 @@ dependencies = [
[[package]]
name = "collab-document"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -858,7 +858,7 @@ dependencies = [
[[package]]
name = "collab-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"bytes",
@ -873,7 +873,7 @@ dependencies = [
[[package]]
name = "collab-folder"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"chrono",
@ -911,7 +911,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"async-stream",
@ -950,7 +950,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -975,7 +975,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"bincode",
@ -989,7 +989,7 @@ dependencies = [
[[package]]
name = "collab-user"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=f8930ed1b19b65dd7b890df2b0db54048141e8c4#f8930ed1b19b65dd7b890df2b0db54048141e8c4"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86#9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86"
dependencies = [
"anyhow",
"collab",
@ -1326,7 +1326,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -1610,6 +1610,7 @@ dependencies = [
"flowy-storage",
"flowy-user",
"flowy-user-pub",
"futures",
"futures-util",
"lib-dispatch",
"lib-infra",
@ -2595,7 +2596,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"futures-util",
@ -2612,7 +2613,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",
@ -2983,7 +2984,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"reqwest",
@ -5109,7 +5110,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fdaac9d4aa0db222d69d346b65bfd85d50bf3c00#fdaac9d4aa0db222d69d346b65bfd85d50bf3c00"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=54dfeb552752eb0e4d887af3a0affaa80863943d#54dfeb552752eb0e4d887af3a0affaa80863943d"
dependencies = [
"anyhow",
"app-error",

View File

@ -115,7 +115,7 @@ rocksdb = { git = "https://github.com/LucasXu0/rust-rocksdb", rev = "21cf4a23ec1
# Run the script.add_workspace_members:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fdaac9d4aa0db222d69d346b65bfd85d50bf3c00" }
client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "54dfeb552752eb0e4d887af3a0affaa80863943d" }
# Please use the following script to update collab.
# Working directory: frontend
#
@ -125,10 +125,10 @@ client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fd
# To switch to the local path, run:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "f8930ed1b19b65dd7b890df2b0db54048141e8c4" }
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "9efc5d6e13aca7d8ed3763bf57646bf2ddf1ff86" }

View File

@ -65,56 +65,32 @@ impl Display for CollabPluginProviderContext {
}
}
pub trait WorkspaceCollabIntegrate: Send + Sync {
fn workspace_id(&self) -> Result<String, Error>;
fn device_id(&self) -> Result<String, Error>;
}
pub struct AppFlowyCollabBuilder {
network_reachability: CollabConnectReachability,
workspace_id: RwLock<Option<String>>,
plugin_provider: RwLock<Arc<dyn CollabCloudPluginProvider>>,
snapshot_persistence: Mutex<Option<Arc<dyn SnapshotPersistence>>>,
#[cfg(not(target_arch = "wasm32"))]
rocksdb_backup: Mutex<Option<Arc<dyn RocksdbBackup>>>,
device_id: String,
}
pub struct CollabBuilderConfig {
pub sync_enable: bool,
/// If auto_initialize is false, the collab object will not be initialized automatically.
/// You need to call collab.initialize() manually.
///
/// Default is true.
pub auto_initialize: bool,
}
impl Default for CollabBuilderConfig {
fn default() -> Self {
Self {
sync_enable: true,
auto_initialize: true,
}
}
}
impl CollabBuilderConfig {
pub fn sync_enable(mut self, sync_enable: bool) -> Self {
self.sync_enable = sync_enable;
self
}
pub fn auto_initialize(mut self, auto_initialize: bool) -> Self {
self.auto_initialize = auto_initialize;
self
}
workspace_integrate: Arc<dyn WorkspaceCollabIntegrate>,
}
impl AppFlowyCollabBuilder {
pub fn new<T: CollabCloudPluginProvider>(storage_provider: T, device_id: String) -> Self {
pub fn new(
storage_provider: impl CollabCloudPluginProvider + 'static,
workspace_integrate: impl WorkspaceCollabIntegrate + 'static,
) -> Self {
Self {
network_reachability: CollabConnectReachability::new(),
workspace_id: Default::default(),
plugin_provider: RwLock::new(Arc::new(storage_provider)),
snapshot_persistence: Default::default(),
#[cfg(not(target_arch = "wasm32"))]
rocksdb_backup: Default::default(),
device_id,
workspace_integrate: Arc::new(workspace_integrate),
}
}
@ -127,10 +103,6 @@ impl AppFlowyCollabBuilder {
*self.rocksdb_backup.lock() = Some(rocksdb_backup);
}
pub fn initialize(&self, workspace_id: String) {
*self.workspace_id.write() = Some(workspace_id);
}
pub fn update_network(&self, reachable: bool) {
if reachable {
self
@ -149,15 +121,14 @@ impl AppFlowyCollabBuilder {
object_id: &str,
collab_type: CollabType,
) -> Result<CollabObject, Error> {
let workspace_id = self.workspace_id.read().clone().ok_or_else(|| {
anyhow::anyhow!("When using supabase plugin, the workspace_id should not be empty")
})?;
let device_id = self.workspace_integrate.device_id()?;
let workspace_id = self.workspace_integrate.workspace_id()?;
Ok(CollabObject::new(
uid,
object_id.to_string(),
collab_type,
workspace_id,
self.device_id.clone(),
device_id,
))
}
@ -175,8 +146,10 @@ impl AppFlowyCollabBuilder {
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabDocState] type.
/// - `collab_db`: A weak reference to the [CollabKVDB].
///
#[allow(clippy::too_many_arguments)]
pub async fn build(
&self,
workspace_id: &str,
uid: i64,
object_id: &str,
object_type: CollabType,
@ -184,14 +157,13 @@ impl AppFlowyCollabBuilder {
collab_db: Weak<CollabKVDB>,
build_config: CollabBuilderConfig,
) -> Result<Arc<MutexCollab>, Error> {
let persistence_config = CollabPersistenceConfig::default();
self.build_with_config(
workspace_id,
uid,
object_id,
object_type,
collab_db,
collab_doc_state,
persistence_config,
build_config,
)
}
@ -211,25 +183,34 @@ impl AppFlowyCollabBuilder {
/// - `collab_db`: A weak reference to the [CollabKVDB].
///
#[allow(clippy::too_many_arguments)]
#[instrument(
level = "trace",
skip(self, collab_db, collab_doc_state, persistence_config, build_config)
)]
#[instrument(level = "trace", skip(self, collab_db, collab_doc_state, build_config))]
pub fn build_with_config(
&self,
workspace_id: &str,
uid: i64,
object_id: &str,
object_type: CollabType,
collab_db: Weak<CollabKVDB>,
collab_doc_state: DataSource,
#[allow(unused_variables)] persistence_config: CollabPersistenceConfig,
build_config: CollabBuilderConfig,
) -> Result<Arc<MutexCollab>, Error> {
let collab = CollabBuilder::new(uid, object_id)
.with_doc_state(collab_doc_state)
.with_device_id(self.device_id.clone())
.with_device_id(self.workspace_integrate.device_id()?)
.build()?;
// Compare the workspace_id with the currently opened workspace_id. Return an error if they do not match.
// This check is crucial in asynchronous code contexts where the workspace_id might change during operation.
let actual_workspace_id = self.workspace_integrate.workspace_id()?;
if workspace_id != actual_workspace_id {
return Err(anyhow::anyhow!(
"workspace_id not match when build collab. expect workspace_id: {}, actual workspace_id: {}",
workspace_id,
actual_workspace_id
));
}
let persistence_config = CollabPersistenceConfig::default();
#[cfg(target_arch = "wasm32")]
{
collab.lock().add_plugin(Box::new(IndexeddbDiskPlugin::new(
@ -317,3 +298,33 @@ impl AppFlowyCollabBuilder {
Ok(arc_collab)
}
}
pub struct CollabBuilderConfig {
pub sync_enable: bool,
/// If auto_initialize is false, the collab object will not be initialized automatically.
/// You need to call collab.initialize() manually.
///
/// Default is true.
pub auto_initialize: bool,
}
impl Default for CollabBuilderConfig {
fn default() -> Self {
Self {
sync_enable: true,
auto_initialize: true,
}
}
}
impl CollabBuilderConfig {
pub fn sync_enable(mut self, sync_enable: bool) -> Self {
self.sync_enable = sync_enable;
self
}
pub fn auto_initialize(mut self, auto_initialize: bool) -> Self {
self.auto_initialize = auto_initialize;
self
}
}

View File

@ -1,8 +1,8 @@
use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType};
use collab::preclude::CollabPlugin;
use std::sync::Arc;
pub trait CollabCloudPluginProvider: Send + Sync + 'static {
#[cfg(target_arch = "wasm32")]
pub trait CollabCloudPluginProvider: 'static {
fn provider_type(&self) -> CollabPluginProviderType;
fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>>;
@ -10,7 +10,35 @@ pub trait CollabCloudPluginProvider: Send + Sync + 'static {
fn is_sync_enabled(&self) -> bool;
}
impl<T> CollabCloudPluginProvider for Arc<T>
#[cfg(target_arch = "wasm32")]
impl<T> CollabCloudPluginProvider for std::rc::Rc<T>
where
T: 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<T> CollabCloudPluginProvider for std::sync::Arc<T>
where
T: CollabCloudPluginProvider,
{

View File

@ -20,7 +20,7 @@ lib-dispatch = { workspace = true }
lib-infra = { workspace = true }
flowy-server = { path = "../flowy-server" }
flowy-server-pub = { workspace = true }
flowy-notification = { workspace = true }
flowy-notification = { workspace = true }
anyhow.workspace = true
flowy-storage = { workspace = true }
flowy-search = { workspace = true }
@ -28,7 +28,7 @@ flowy-search = { workspace = true }
serde.workspace = true
serde_json.workspace = true
protobuf.workspace = true
tokio = { workspace = true, features = ["full"]}
tokio = { workspace = true, features = ["full"] }
futures-util = "0.3.26"
thread-id = "3.3.0"
bytes.workspace = true
@ -53,6 +53,7 @@ tokio-postgres = { version = "0.7.8" }
chrono = "0.4.31"
zip = "0.6.6"
walkdir = "2.5.0"
futures = "0.3.30"
[features]
default = ["supabase_cloud_test"]

View File

@ -143,7 +143,8 @@ impl EventIntegrationTest {
let mutex_folder = self.appflowy_core.folder_manager.get_mutex_folder().clone();
let folder_lock_guard = mutex_folder.read();
let folder = folder_lock_guard.as_ref().unwrap();
folder.get_folder_data().clone().unwrap()
let workspace_id = self.appflowy_core.user_manager.workspace_id().unwrap();
folder.get_folder_data(&workspace_id).clone().unwrap()
}
pub async fn get_all_workspace_views(&self) -> Vec<ViewPB> {

View File

@ -20,7 +20,7 @@ pub async fn get_synced_workspaces(
&sub_id,
UserNotification::DidUpdateUserWorkspaces as i32,
);
receive_with_timeout(rx, Duration::from_secs(30))
receive_with_timeout(rx, Duration::from_secs(60))
.await
.unwrap()
.items

View File

@ -1,5 +1,11 @@
use collab::core::collab::DataSource::DocStateV1;
use collab::core::origin::CollabOrigin;
use collab_entity::CollabType;
use collab_folder::Folder;
use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest;
use std::time::Duration;
use tokio::time::sleep;
use crate::user::af_cloud_test::util::get_synced_workspaces;
@ -94,16 +100,143 @@ async fn af_cloud_open_workspace_test() {
user_localhost_af_cloud().await;
let test = EventIntegrationTest::new().await;
let _ = test.af_cloud_sign_up().await;
let default_document_name = "Getting started";
let workspace = test.create_workspace("my second workspace").await;
test.open_workspace(&workspace.workspace_id).await;
test.create_document("A").await;
test.create_document("B").await;
let first_workspace = test.get_current_workspace().await;
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3);
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "A");
assert_eq!(views[2].name, "B");
test.create_document("my first document").await;
test.create_document("my second document").await;
let user_workspace = test.create_workspace("second workspace").await;
test.open_workspace(&user_workspace.workspace_id).await;
let second_workspace = test.get_current_workspace().await;
test.create_document("C").await;
test.create_document("D").await;
let views = test.get_all_workspace_views().await;
assert_eq!(views.len(), 3);
// the first view is the default get started view
assert_eq!(views[1].name, "my first document".to_string());
assert_eq!(views[2].name, "my second document".to_string());
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "C");
assert_eq!(views[2].name, "D");
// simulate open workspace and check if the views are correct
for i in 0..30 {
if i % 2 == 0 {
test.open_workspace(&first_workspace.id).await;
sleep(Duration::from_millis(300)).await;
test
.create_document(&uuid::Uuid::new_v4().to_string())
.await;
} else {
test.open_workspace(&second_workspace.id).await;
sleep(Duration::from_millis(200)).await;
test
.create_document(&uuid::Uuid::new_v4().to_string())
.await;
}
}
test.open_workspace(&first_workspace.id).await;
let views = test.get_all_workspace_views().await;
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "A");
assert_eq!(views[2].name, "B");
test.open_workspace(&second_workspace.id).await;
let views = test.get_all_workspace_views().await;
assert_eq!(views[0].name, default_document_name);
assert_eq!(views[1].name, "C");
assert_eq!(views[2].name, "D");
}
#[tokio::test]
async fn af_cloud_different_open_same_workspace_test() {
user_localhost_af_cloud().await;
// Set up the primary client and sign them up to the cloud.
let client_1 = EventIntegrationTest::new().await;
let owner_profile = client_1.af_cloud_sign_up().await;
let shared_workspace_id = client_1.get_current_workspace().await.id.clone();
// Verify that the workspace ID from the profile matches the current session's workspace ID.
assert_eq!(shared_workspace_id, owner_profile.workspace_id);
// Define the number of additional clients
let num_clients = 5;
let mut clients = Vec::new();
// Initialize and sign up additional clients
for _ in 0..num_clients {
let client = EventIntegrationTest::new().await;
let client_profile = client.af_cloud_sign_up().await;
let views = client.get_all_workspace_views().await;
// only the getting started view should be present
assert_eq!(views.len(), 1);
for view in views {
client.delete_view(&view.id).await;
}
client_1
.add_workspace_member(&owner_profile.workspace_id, &client_profile.email)
.await;
clients.push((client, client_profile));
}
// Verify that each client has exactly two workspaces: one from sign-up and another from invitation
for (client, profile) in &clients {
let all_workspaces = get_synced_workspaces(client, profile.id).await;
assert_eq!(all_workspaces.len(), 2);
}
// Simulate each client open different workspace 30 times
let mut handles = vec![];
for client in clients.clone() {
let cloned_shared_workspace_id = shared_workspace_id.clone();
let handle = tokio::spawn(async move {
let (client, profile) = client;
let all_workspaces = get_synced_workspaces(&client, profile.id).await;
for i in 0..30 {
let index = i % 2;
let iter_workspace_id = &all_workspaces[index].workspace_id;
client.open_workspace(iter_workspace_id).await;
if iter_workspace_id == &cloned_shared_workspace_id {
let views = client.get_all_workspace_views().await;
assert_eq!(views.len(), 1);
sleep(Duration::from_millis(300)).await;
} else {
let views = client.get_all_workspace_views().await;
assert!(views.is_empty());
}
}
});
handles.push(handle);
}
futures::future::join_all(handles).await;
// Retrieve and verify the collaborative document state for Client 1's workspace.
let doc_state = client_1
.get_collab_doc_state(&shared_workspace_id, CollabType::Folder)
.await
.unwrap();
let folder = Folder::from_collab_doc_state(
owner_profile.id,
CollabOrigin::Empty,
DocStateV1(doc_state),
&shared_workspace_id,
vec![],
)
.unwrap();
// Retrieve and verify the views associated with the workspace.
let views = folder.get_views_belong_to(&shared_workspace_id);
let folder_workspace_id = folder.get_workspace_id();
assert_eq!(folder_workspace_id, shared_workspace_id);
assert_eq!(views.len(), 1, "only get: {:?}", views); // Expecting two views.
assert_eq!(views[0].name, "Getting started");
}

View File

@ -9,8 +9,9 @@ use flowy_sqlite::{
};
use flowy_user::services::authenticate_user::AuthenticateUser;
use collab_integrate::collab_builder::WorkspaceCollabIntegrate;
use lib_infra::util::timestamp;
use std::sync::Weak;
use std::sync::{Arc, Weak};
use tracing::debug;
pub struct SnapshotDBImpl(pub Weak<AuthenticateUser>);
@ -207,3 +208,26 @@ impl CollabSnapshotSql {
Ok(())
}
}
pub(crate) struct WorkspaceCollabIntegrateImpl(pub Weak<AuthenticateUser>);
impl WorkspaceCollabIntegrateImpl {
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
}
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, anyhow::Error> {
let workspace_id = self.upgrade_user()?.workspace_id()?;
Ok(workspace_id)
}
fn device_id(&self) -> Result<String, anyhow::Error> {
Ok(self.upgrade_user()?.user_config.device_id.clone())
}
}

View File

@ -27,20 +27,30 @@ impl DatabaseDepsResolver {
}
struct DatabaseUserImpl(Weak<AuthenticateUser>);
impl DatabaseUser for DatabaseUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self
impl DatabaseUserImpl {
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.user_id()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
}
impl DatabaseUser for DatabaseUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self.upgrade_user()?.user_id()
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.get_collab_db(uid)
self.upgrade_user()?.get_collab_db(uid)
}
fn workspace_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_id()
}
fn workspace_database_object_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_database_object_id()
}
}

View File

@ -18,7 +18,6 @@ use flowy_folder_pub::folder_builder::NestedViewBuilder;
use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_user::services::authenticate_user::AuthenticateUser;
use lib_dispatch::prelude::ToBytes;
use lib_infra::async_trait::async_trait;
use lib_infra::future::FutureResult;
use std::collections::HashMap;
use std::convert::TryFrom;
@ -76,22 +75,27 @@ struct FolderUserImpl {
authenticate_user: Weak<AuthenticateUser>,
}
#[async_trait]
impl FolderUser for FolderUserImpl {
fn user_id(&self) -> Result<i64, FlowyError> {
self
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"))?
.user_id()
.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 workspace_id(&self) -> Result<String, FlowyError> {
self.upgrade_user()?.workspace_id()
}
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError> {
self
.authenticate_user
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?
.get_collab_db(uid)
self.upgrade_user()?.get_collab_db(uid)
}
}

View File

@ -6,6 +6,7 @@ use parking_lot::RwLock;
use serde_repr::*;
use flowy_error::{FlowyError, FlowyResult};
use flowy_server::af_cloud::define::ServerUser;
use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::local_server::{LocalServer, LocalServerDB};
use flowy_server::supabase::SupabaseServer;
@ -63,6 +64,7 @@ pub struct ServerProvider {
/// The authenticator type of the user.
authenticator: RwLock<Authenticator>,
user: Arc<dyn ServerUser>,
pub(crate) uid: Arc<RwLock<Option<i64>>>,
}
@ -71,7 +73,9 @@ impl ServerProvider {
config: AppFlowyCoreConfig,
server: Server,
store_preferences: Weak<StorePreferences>,
server_user: impl ServerUser + 'static,
) -> Self {
let user = Arc::new(server_user);
let encryption = EncryptionImpl::new(None);
Self {
config,
@ -81,6 +85,7 @@ impl ServerProvider {
encryption: RwLock::new(Arc::new(encryption)),
store_preferences,
uid: Default::default(),
user,
}
}
@ -129,6 +134,7 @@ impl ServerProvider {
*self.user_enable_sync.read(),
self.config.device_id.clone(),
&self.config.app_version,
self.user.clone(),
));
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)

View File

@ -38,7 +38,6 @@ impl UserStatusCallback for UserStatusCallbackImpl {
) -> Fut<FlowyResult<()>> {
let user_id = user_id.to_owned();
let user_workspace = user_workspace.clone();
let collab_builder = self.collab_builder.clone();
let folder_manager = self.folder_manager.clone();
let database_manager = self.database_manager.clone();
let document_manager = self.document_manager.clone();
@ -59,7 +58,6 @@ impl UserStatusCallback for UserStatusCallbackImpl {
}
to_fut(async move {
collab_builder.initialize(user_workspace.id.clone());
folder_manager
.initialize(
user_id,
@ -69,16 +67,8 @@ impl UserStatusCallback for UserStatusCallbackImpl {
},
)
.await?;
database_manager
.initialize(
user_id,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await?;
document_manager
.initialize(user_id, user_workspace.id)
.await?;
database_manager.initialize(user_id).await?;
document_manager.initialize(user_id).await?;
Ok(())
})
}
@ -104,19 +94,9 @@ impl UserStatusCallback for UserStatusCallbackImpl {
device_id
);
folder_manager
.initialize_with_workspace_id(user_id, &user_workspace.id)
.await?;
database_manager
.initialize(
user_id,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await?;
document_manager
.initialize(user_id, user_workspace.id)
.await?;
folder_manager.initialize_with_workspace_id(user_id).await?;
database_manager.initialize(user_id).await?;
document_manager.initialize(user_id).await?;
Ok(())
})
}
@ -199,16 +179,12 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.context("FolderManager error")?;
database_manager
.initialize_with_new_user(
user_profile.uid,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.initialize_with_new_user(user_profile.uid)
.await
.context("DatabaseManager error")?;
document_manager
.initialize_with_new_user(user_profile.uid, user_workspace.id)
.initialize_with_new_user(user_profile.uid)
.await
.context("DocumentManager error")?;
Ok(())
@ -223,29 +199,15 @@ impl UserStatusCallback for UserStatusCallbackImpl {
})
}
fn open_workspace(&self, user_id: i64, user_workspace: &UserWorkspace) -> Fut<FlowyResult<()>> {
let user_workspace = user_workspace.clone();
let collab_builder = self.collab_builder.clone();
fn open_workspace(&self, user_id: i64, _user_workspace: &UserWorkspace) -> Fut<FlowyResult<()>> {
let folder_manager = self.folder_manager.clone();
let database_manager = self.database_manager.clone();
let document_manager = self.document_manager.clone();
to_fut(async move {
collab_builder.initialize(user_workspace.id.clone());
folder_manager
.initialize_with_workspace_id(user_id, &user_workspace.id)
.await?;
database_manager
.initialize(
user_id,
user_workspace.id.clone(),
user_workspace.workspace_database_object_id,
)
.await?;
document_manager
.initialize(user_id, user_workspace.id)
.await?;
folder_manager.initialize_with_workspace_id(user_id).await?;
database_manager.initialize(user_id).await?;
document_manager.initialize(user_id).await?;
Ok(())
})
}

View File

@ -4,7 +4,7 @@ use flowy_search::folder::indexer::FolderIndexManagerImpl;
use flowy_search::services::manager::SearchManager;
use flowy_storage::ObjectStorageService;
use semver::Version;
use std::sync::Arc;
use std::sync::{Arc, Weak};
use std::time::Duration;
use sysinfo::System;
use tokio::sync::RwLock;
@ -13,7 +13,9 @@ use tracing::{debug, error, event, info, instrument};
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabPluginProviderType};
use flowy_database2::DatabaseManager;
use flowy_document::manager::DocumentManager;
use flowy_error::{FlowyError, FlowyResult};
use flowy_folder::manager::FolderManager;
use flowy_server::af_cloud::define::ServerUser;
use flowy_sqlite::kv::StorePreferences;
use flowy_user::services::authenticate_user::AuthenticateUser;
@ -104,14 +106,29 @@ impl AppFlowyCore {
let task_dispatcher = Arc::new(RwLock::new(task_scheduler));
runtime.spawn(TaskRunner::run(task_dispatcher.clone()));
let app_version = Version::parse(&config.app_version).unwrap_or_else(|_| Version::new(0, 5, 4));
let user_config = UserConfig::new(
&config.name,
&config.storage_path,
&config.application_path,
&config.device_id,
app_version,
);
let authenticate_user = Arc::new(AuthenticateUser::new(
user_config.clone(),
store_preference.clone(),
));
let server_type = current_server_type();
debug!("🔥runtime:{}, server:{}", runtime, server_type);
let server_provider = Arc::new(ServerProvider::new(
config.clone(),
server_type,
Arc::downgrade(&store_preference),
ServerUserImpl(Arc::downgrade(&authenticate_user)),
));
let app_version = Version::parse(&config.app_version).unwrap_or_else(|_| Version::new(0, 5, 4));
event!(tracing::Level::DEBUG, "Init managers",);
let (
@ -127,20 +144,7 @@ impl AppFlowyCore {
/// on demand based on the [CollabPluginConfig].
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
server_provider.clone(),
config.device_id.clone(),
));
let user_config = UserConfig::new(
&config.name,
&config.storage_path,
&config.application_path,
&config.device_id,
app_version,
);
let authenticate_user = Arc::new(AuthenticateUser::new(
user_config.clone(),
store_preference.clone(),
WorkspaceCollabIntegrateImpl(Arc::downgrade(&authenticate_user)),
));
collab_builder
@ -260,3 +264,20 @@ impl From<Server> for CollabPluginProviderType {
}
}
}
struct ServerUserImpl(Weak<AuthenticateUser>);
impl ServerUserImpl {
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
let user = self
.0
.upgrade()
.ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?;
Ok(user)
}
}
impl ServerUser for ServerUserImpl {
fn workspace_id(&self) -> FlowyResult<String> {
self.upgrade_user()?.workspace_id()
}
}

View File

@ -32,6 +32,8 @@ use crate::services::share::csv::{CSVFormat, CSVImporter, ImportResult};
pub trait DatabaseUser: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn workspace_database_object_id(&self) -> Result<String, FlowyError>;
}
pub struct DatabaseManager {
@ -71,12 +73,7 @@ impl DatabaseManager {
}
/// When initialize with new workspace, all the resources will be cleared.
pub async fn initialize(
&self,
uid: i64,
workspace_id: String,
workspace_database_object_id: String,
) -> FlowyResult<()> {
pub async fn initialize(&self, uid: i64) -> FlowyResult<()> {
// 1. Clear all existing tasks
self.task_scheduler.write().await.clear_task();
// 2. Release all existing editors
@ -85,16 +82,21 @@ impl DatabaseManager {
}
self.editors.lock().await.clear();
// 3. Clear the workspace database
if let Some(old_workspace_database) = self.workspace_database.write().await.take() {
old_workspace_database.close();
}
*self.workspace_database.write().await = None;
let collab_db = self.user.collab_db(uid)?;
let collab_builder = UserDatabaseCollabServiceImpl {
workspace_id: workspace_id.clone(),
user: self.user.clone(),
collab_builder: self.collab_builder.clone(),
cloud_service: self.cloud_service.clone(),
};
let config = CollabPersistenceConfig::new().snapshot_per_update(100);
let workspace_id = self.user.workspace_id()?;
let workspace_database_object_id = self.user.workspace_database_object_id()?;
let mut workspace_database_doc_state = DataSource::Disk;
// If the workspace database not exist in disk, try to fetch from remote.
if !self.is_collab_exist(uid, &collab_db, &workspace_database_object_id) {
@ -151,15 +153,8 @@ impl DatabaseManager {
skip_all,
err
)]
pub async fn initialize_with_new_user(
&self,
user_id: i64,
workspace_id: String,
workspace_database_object_id: String,
) -> FlowyResult<()> {
self
.initialize(user_id, workspace_id, workspace_database_object_id)
.await?;
pub async fn initialize_with_new_user(&self, user_id: i64) -> FlowyResult<()> {
self.initialize(user_id).await?;
Ok(())
}
@ -222,7 +217,7 @@ impl DatabaseManager {
.await?
.get_database(database_id)
.await
.ok_or_else(FlowyError::collab_not_sync)?;
.ok_or_else(|| FlowyError::collab_not_sync().with_context("open database error"))?;
// Subscribe the [BlockEvent]
subscribe_block_event(&database);
@ -445,7 +440,7 @@ fn subscribe_block_event(database: &Arc<MutexDatabase>) {
}
struct UserDatabaseCollabServiceImpl {
workspace_id: String,
user: Arc<dyn DatabaseUser>,
collab_builder: Arc<AppFlowyCollabBuilder>,
cloud_service: Arc<dyn DatabaseCloudService>,
}
@ -456,7 +451,7 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
object_id: &str,
object_ty: CollabType,
) -> CollabFuture<Result<DataSource, DatabaseError>> {
let workspace_id = self.workspace_id.clone();
let workspace_id = self.user.workspace_id().unwrap();
let object_id = object_id.to_string();
let weak_cloud_service = Arc::downgrade(&self.cloud_service);
Box::pin(async move {
@ -480,9 +475,12 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
object_ids: Vec<String>,
object_ty: CollabType,
) -> CollabFuture<Result<CollabDocStateByOid, DatabaseError>> {
let workspace_id = self.workspace_id.clone();
let cloned_user = self.user.clone();
let weak_cloud_service = Arc::downgrade(&self.cloud_service);
Box::pin(async move {
let workspace_id = cloned_user
.workspace_id()
.map_err(|err| DatabaseError::Internal(err.into()))?;
match weak_cloud_service.upgrade() {
None => {
tracing::warn!("Cloud service is dropped");
@ -505,15 +503,19 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
object_type: CollabType,
collab_db: Weak<CollabKVDB>,
collab_raw_data: DataSource,
persistence_config: CollabPersistenceConfig,
_persistence_config: CollabPersistenceConfig,
) -> Result<Arc<MutexCollab>, DatabaseError> {
let workspace_id = self
.user
.workspace_id()
.map_err(|err| DatabaseError::Internal(err.into()))?;
let collab = self.collab_builder.build_with_config(
&workspace_id,
uid,
object_id,
object_type.clone(),
collab_db.clone(),
collab_raw_data,
persistence_config,
CollabBuilderConfig::default().sync_enable(true),
)?;
Ok(collab)

View File

@ -20,7 +20,6 @@ use tracing::{error, trace};
use tracing::{event, instrument};
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::CollabPersistenceConfig;
use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
use flowy_storage::ObjectStorageService;
@ -77,7 +76,7 @@ impl DocumentManager {
}
}
pub async fn initialize(&self, _uid: i64, _workspace_id: String) -> FlowyResult<()> {
pub async fn initialize(&self, _uid: i64) -> FlowyResult<()> {
self.documents.clear();
Ok(())
}
@ -88,8 +87,8 @@ impl DocumentManager {
skip_all,
err
)]
pub async fn initialize_with_new_user(&self, uid: i64, workspace_id: String) -> FlowyResult<()> {
self.initialize(uid, workspace_id).await?;
pub async fn initialize_with_new_user(&self, uid: i64) -> FlowyResult<()> {
self.initialize(uid).await?;
Ok(())
}
@ -382,13 +381,14 @@ impl DocumentManager {
sync_enable: bool,
) -> FlowyResult<Arc<MutexCollab>> {
let db = self.user_service.collab_db(uid)?;
let workspace_id = self.user_service.workspace_id()?;
let collab = self.collab_builder.build_with_config(
&workspace_id,
uid,
doc_id,
CollabType::Document,
db,
doc_state,
CollabPersistenceConfig::default().snapshot_per_update(1000),
CollabBuilderConfig::default().sync_enable(sync_enable),
)?;
Ok(collab)

View File

@ -9,11 +9,10 @@ use nanoid::nanoid;
use parking_lot::Once;
use tempfile::TempDir;
use tracing_subscriber::{fmt::Subscriber, util::SubscriberInitExt, EnvFilter};
use uuid::Uuid;
use collab_integrate::collab_builder::{
AppFlowyCollabBuilder, CollabCloudPluginProvider, CollabPluginProviderContext,
CollabPluginProviderType,
CollabPluginProviderType, WorkspaceCollabIntegrate,
};
use collab_integrate::CollabKVDB;
use flowy_document::document::MutexDocument;
@ -35,9 +34,17 @@ impl DocumentTest {
let cloud_service = Arc::new(LocalTestDocumentCloudServiceImpl());
let file_storage = Arc::new(DocumentTestFileStorageService) as Arc<dyn ObjectStorageService>;
let document_snapshot = Arc::new(DocumentTestSnapshot);
let builder = Arc::new(AppFlowyCollabBuilder::new(
DefaultCollabStorageProvider(),
WorkspaceCollabIntegrateImpl {
workspace_id: user.workspace_id.clone(),
},
));
let manager = DocumentManager::new(
Arc::new(user),
default_collab_builder(),
builder,
cloud_service,
Arc::downgrade(&file_storage),
document_snapshot,
@ -55,6 +62,7 @@ impl Deref for DocumentTest {
}
pub struct FakeUser {
workspace_id: String,
collab_db: Arc<CollabKVDB>,
}
@ -65,8 +73,12 @@ impl FakeUser {
let tempdir = TempDir::new().unwrap();
let path = tempdir.into_path();
let collab_db = Arc::new(CollabKVDB::open(path).unwrap());
let workspace_id = uuid::Uuid::new_v4().to_string();
Self { collab_db }
Self {
collab_db,
workspace_id,
}
}
}
@ -76,7 +88,7 @@ impl DocumentUserService for FakeUser {
}
fn workspace_id(&self) -> Result<String, FlowyError> {
Ok(Uuid::new_v4().to_string())
Ok(self.workspace_id.clone())
}
fn collab_db(&self, _uid: i64) -> Result<std::sync::Weak<CollabKVDB>, FlowyError> {
@ -100,13 +112,6 @@ pub fn setup_log() {
});
}
pub fn default_collab_builder() -> Arc<AppFlowyCollabBuilder> {
let builder =
AppFlowyCollabBuilder::new(DefaultCollabStorageProvider(), "fake_device_id".to_string());
builder.initialize(uuid::Uuid::new_v4().to_string());
Arc::new(builder)
}
pub async fn create_and_open_empty_document() -> (DocumentTest, Arc<MutexDocument>, String) {
let test = DocumentTest::new();
let doc_id: String = gen_document_id();
@ -222,3 +227,16 @@ impl DocumentSnapshotService for DocumentTestSnapshot {
todo!()
}
}
struct WorkspaceCollabIntegrateImpl {
workspace_id: String,
}
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
fn workspace_id(&self) -> Result<String, Error> {
Ok(self.workspace_id.clone())
}
fn device_id(&self) -> Result<String, Error> {
Ok("fake_device_id".to_string())
}
}

View File

@ -11,6 +11,9 @@ pub mod view_operation;
mod manager_init;
mod manager_observer;
#[cfg(debug_assertions)]
pub mod manager_test_util;
pub mod share;
#[cfg(feature = "test_helper")]
mod test_helper;

View File

@ -21,34 +21,28 @@ use collab::core::collab::{DataSource, MutexCollab};
use collab_entity::CollabType;
use collab_folder::error::FolderError;
use collab_folder::{
Folder, FolderData, FolderNotify, Section, SectionItem, TrashInfo, UserId, View, ViewLayout,
ViewUpdate, Workspace,
Folder, FolderNotify, Section, SectionItem, TrashInfo, UserId, View, ViewLayout, ViewUpdate,
Workspace,
};
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
use collab_integrate::{CollabKVDB, CollabPersistenceConfig};
use collab_integrate::CollabKVDB;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_pub::cloud::{gen_view_id, FolderCloudService};
use flowy_folder_pub::folder_builder::ParentChildViews;
use flowy_search_pub::entities::FolderIndexManager;
use lib_infra::conditional_send_sync_trait;
use parking_lot::RwLock;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::sync::{Arc, Weak};
use tracing::{error, info, instrument};
conditional_send_sync_trait! {
"[crate::manager::FolderUser] represents the user for folder.";
FolderUser {
fn user_id(&self) -> Result<i64, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
}
pub trait FolderUser: Send + Sync {
fn user_id(&self) -> Result<i64, FlowyError>;
fn workspace_id(&self) -> Result<String, FlowyError>;
fn collab_db(&self, uid: i64) -> Result<Weak<CollabKVDB>, FlowyError>;
}
pub struct FolderManager {
/// workspace_id represents as the id of the Folder.
pub(crate) workspace_id: RwLock<Option<String>>,
/// MutexFolder is the folder that is used to store the data.
pub(crate) mutex_folder: Arc<MutexFolder>,
pub(crate) collab_builder: Arc<AppFlowyCollabBuilder>,
@ -73,7 +67,6 @@ impl FolderManager {
collab_builder,
operation_handlers,
cloud_service,
workspace_id: Default::default(),
folder_indexer,
};
@ -82,25 +75,20 @@ impl FolderManager {
#[instrument(level = "debug", skip(self), err)]
pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> {
let workspace_id = self.user.workspace_id()?;
self.with_folder(
|| {
let uid = self.user.user_id()?;
let workspace_id = self
.workspace_id
.read()
.as_ref()
.cloned()
.ok_or_else(|| FlowyError::from(ErrorCode::WorkspaceInitializeError))?;
Err(workspace_data_not_sync_error(uid, &workspace_id))
},
|folder| {
let workspace_pb_from_workspace = |workspace: Workspace, folder: &Folder| {
let views = get_workspace_public_view_pbs(folder);
let views = get_workspace_public_view_pbs(&workspace_id, folder);
let workspace: WorkspacePB = (workspace, views).into();
Ok::<WorkspacePB, FlowyError>(workspace)
};
match folder.get_current_workspace() {
match folder.get_workspace_info(&workspace_id) {
None => Err(FlowyError::record_not_found().with_context("Can not find the workspace")),
Some(workspace) => workspace_pb_from_workspace(workspace, folder),
}
@ -111,28 +99,25 @@ impl FolderManager {
/// Return a list of views of the current workspace.
/// Only the first level of child views are included.
pub async fn get_current_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> {
let workspace_id = self
.mutex_folder
.read()
.as_ref()
.map(|folder| folder.get_workspace_id());
if workspace_id.is_some() {
self.get_workspace_public_views().await
} else {
tracing::warn!("Can't get the workspace id from the folder. Return empty list.");
Ok(vec![])
}
let views = self.get_workspace_public_views().await?;
Ok(views)
}
pub async fn get_workspace_public_views(&self) -> FlowyResult<Vec<ViewPB>> {
Ok(self.with_folder(Vec::new, get_workspace_public_view_pbs))
let workspace_id = self.user.workspace_id()?;
Ok(self.with_folder(Vec::new, |folder| {
get_workspace_public_view_pbs(&workspace_id, folder)
}))
}
pub async fn get_workspace_private_views(&self) -> FlowyResult<Vec<ViewPB>> {
Ok(self.with_folder(Vec::new, get_workspace_private_view_pbs))
let workspace_id = self.user.workspace_id()?;
Ok(self.with_folder(Vec::new, |folder| {
get_workspace_private_view_pbs(&workspace_id, folder)
}))
}
#[instrument(level = "trace", skip_all, err)]
pub(crate) async fn make_folder<T: Into<Option<FolderNotify>>>(
&self,
uid: i64,
@ -142,11 +127,6 @@ impl FolderManager {
folder_notifier: T,
) -> Result<Folder, FlowyError> {
let folder_notifier = folder_notifier.into();
// snapshot_config will be deprecated in the future.
let snapshot_config = CollabPersistenceConfig::new()
.enable_snapshot(true)
.snapshot_per_update(50);
// only need the check the workspace id when the doc state is not from the disk.
let should_check_workspace_id = !matches!(doc_state, DataSource::Disk);
let should_auto_initialize = !should_check_workspace_id;
@ -154,13 +134,14 @@ impl FolderManager {
.sync_enable(true)
.auto_initialize(should_auto_initialize);
let object_id = workspace_id;
let collab = self.collab_builder.build_with_config(
uid,
workspace_id,
uid,
object_id,
CollabType::Folder,
collab_db,
doc_state,
snapshot_config,
config,
)?;
let (should_clear, err) = match Folder::open(UserId::from(uid), collab.clone(), folder_notifier)
@ -172,7 +153,7 @@ impl FolderManager {
let folder_workspace_id = folder.get_workspace_id();
if folder_workspace_id != workspace_id {
error!(
"expected workspace id: {}, actual workspace id: {}",
"expect workspace_id: {}, actual workspace_id: {}",
workspace_id, folder_workspace_id
);
return Err(FlowyError::workspace_data_not_match());
@ -203,15 +184,14 @@ impl FolderManager {
workspace_id: &str,
collab_db: Weak<CollabKVDB>,
) -> Result<Arc<MutexCollab>, FlowyError> {
let object_id = workspace_id;
let collab = self.collab_builder.build_with_config(
uid,
workspace_id,
uid,
object_id,
CollabType::Folder,
collab_db,
DataSource::Disk,
CollabPersistenceConfig::new()
.enable_snapshot(true)
.snapshot_per_update(50),
CollabBuilderConfig::default().sync_enable(true),
)?;
Ok(collab)
@ -220,19 +200,17 @@ impl FolderManager {
/// Initialize the folder with the given workspace id.
/// Fetch the folder updates from the cloud service and initialize the folder.
#[tracing::instrument(skip(self, user_id), err)]
pub async fn initialize_with_workspace_id(
&self,
user_id: i64,
workspace_id: &str,
) -> FlowyResult<()> {
pub async fn initialize_with_workspace_id(&self, user_id: i64) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
let object_id = &workspace_id;
let folder_doc_state = self
.cloud_service
.get_folder_doc_state(workspace_id, user_id, CollabType::Folder, workspace_id)
.get_folder_doc_state(&workspace_id, user_id, CollabType::Folder, object_id)
.await?;
if let Err(err) = self
.initialize(
user_id,
workspace_id,
&workspace_id,
FolderInitDataSource::Cloud(folder_doc_state),
)
.await
@ -243,7 +221,7 @@ impl FolderManager {
self
.initialize(
user_id,
workspace_id,
&workspace_id,
FolderInitDataSource::LocalDisk {
create_if_not_exist: false,
},
@ -278,17 +256,17 @@ impl FolderManager {
.map_err(FlowyError::from);
match result {
Ok(folder_updates) => {
Ok(folder_doc_state) => {
info!(
"Get folder updates via {}, doc state len: {}",
self.cloud_service.service_name(),
folder_updates.len()
folder_doc_state.len()
);
self
.initialize(
user_id,
workspace_id,
FolderInitDataSource::Cloud(folder_updates),
FolderInitDataSource::Cloud(folder_doc_state),
)
.await?;
},
@ -318,12 +296,8 @@ impl FolderManager {
Ok(new_workspace)
}
pub async fn get_workspace(&self, _workspace_id: &str) -> Option<Workspace> {
self.with_folder(|| None, |folder| folder.get_current_workspace())
}
pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceSettingPB> {
let workspace_id = self.get_current_workspace_id().await?;
let workspace_id = self.user.workspace_id()?;
let latest_view = self.get_current_view().await;
Ok(WorkspaceSettingPB {
workspace_id,
@ -349,40 +323,29 @@ impl FolderManager {
}
pub async fn get_workspace_pb(&self) -> FlowyResult<WorkspacePB> {
let workspace_pb = {
let guard = self.mutex_folder.read();
let folder = guard
.as_ref()
.ok_or(FlowyError::internal().with_context("folder is not initialized"))?;
let workspace = folder.get_current_workspace().ok_or(
FlowyError::record_not_found().with_context("Can't find the current workspace id "),
)?;
let views = folder
.views
.get_views_belong_to(&workspace.id)
.into_iter()
.map(|view| view_pb_without_child_views(view.as_ref().clone()))
.collect::<Vec<ViewPB>>();
WorkspacePB {
id: workspace.id,
name: workspace.name,
views,
create_time: workspace.created_at,
}
};
Ok(workspace_pb)
}
async fn get_current_workspace_id(&self) -> FlowyResult<String> {
self
.mutex_folder
.read()
let workspace_id = self.user.workspace_id()?;
let guard = self.mutex_folder.read();
let folder = guard
.as_ref()
.map(|folder| folder.get_workspace_id())
.ok_or(FlowyError::internal().with_context("Unexpected empty workspace id"))
.ok_or(FlowyError::internal().with_context("folder is not initialized"))?;
let workspace = folder
.get_workspace_info(&workspace_id)
.ok_or_else(|| FlowyError::record_not_found().with_context("Can not find the workspace"))?;
let views = folder
.views
.get_views_belong_to(&workspace.id)
.into_iter()
.map(|view| view_pb_without_child_views(view.as_ref().clone()))
.collect::<Vec<ViewPB>>();
drop(guard);
Ok(WorkspacePB {
id: workspace.id,
name: workspace.name,
views,
create_time: workspace.created_at,
})
}
/// This function acquires a lock on the `mutex_folder` and checks its state.
@ -404,17 +367,8 @@ impl FolderManager {
}
}
pub async fn get_all_workspaces(&self) -> Vec<Workspace> {
self.with_folder(Vec::new, |folder| {
let mut workspaces = vec![];
if let Some(workspace) = folder.get_current_workspace() {
workspaces.push(workspace);
}
workspaces
})
}
pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> {
let workspace_id = self.user.workspace_id()?;
let view_layout: ViewLayout = params.layout.clone().into();
let handler = self.get_handler(&view_layout)?;
let user_id = self.user.user_id()?;
@ -453,11 +407,9 @@ impl FolderManager {
},
);
if let Ok(workspace_id) = self.get_current_workspace_id().await {
let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder);
}
let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder);
}
Ok(view)
@ -659,6 +611,7 @@ impl FolderManager {
///
#[tracing::instrument(level = "trace", skip(self), err)]
pub async fn move_nested_view(&self, params: MoveNestedViewParams) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
let view_id = params.view_id;
let new_parent_id = params.new_parent_id;
let prev_view_id = params.prev_view_id;
@ -681,6 +634,7 @@ impl FolderManager {
},
);
notify_parent_view_did_change(
&workspace_id,
self.mutex_folder.clone(),
vec![new_parent_id, old_parent_id],
);
@ -693,6 +647,7 @@ impl FolderManager {
/// We need to convert the index to the real index of the view in the parent view.
#[tracing::instrument(level = "trace", skip(self), err)]
pub async fn move_view(&self, view_id: &str, from: usize, to: usize) -> FlowyResult<()> {
let workspace_id = self.user.workspace_id()?;
if let Some((is_workspace, parent_view_id, child_views)) = self.get_view_relation(view_id).await
{
// The display parent view is the view that is displayed in the UI
@ -729,7 +684,11 @@ impl FolderManager {
folder.move_view(view_id, actual_from_index as u32, actual_to_index as u32);
},
);
notify_parent_view_did_change(self.mutex_folder.clone(), vec![parent_view_id]);
notify_parent_view_did_change(
&workspace_id,
self.mutex_folder.clone(),
vec![parent_view_id],
);
}
}
}
@ -819,12 +778,12 @@ impl FolderManager {
#[tracing::instrument(level = "trace", skip(self), err)]
pub(crate) async fn set_current_view(&self, view_id: &str) -> Result<(), FlowyError> {
let workspace_id = self.with_folder(
self.with_folder(
|| Err(FlowyError::record_not_found()),
|folder| {
folder.set_current_view(view_id);
folder.add_recent_view_ids(vec![view_id.to_string()]);
Ok(folder.get_workspace_id())
Ok(())
},
)?;
@ -836,6 +795,7 @@ impl FolderManager {
}
}
let workspace_id = self.user.workspace_id()?;
send_workspace_setting_notification(workspace_id, view);
Ok(())
}
@ -992,6 +952,7 @@ impl FolderManager {
}
pub(crate) async fn import(&self, import_data: ImportParams) -> FlowyResult<View> {
let workspace_id = self.user.workspace_id()?;
if import_data.data.is_none() && import_data.file_path.is_none() {
return Err(FlowyError::new(
ErrorCode::InvalidParams,
@ -1040,7 +1001,11 @@ impl FolderManager {
folder.insert_view(view.clone(), None);
},
);
notify_parent_view_did_change(self.mutex_folder.clone(), vec![view.parent_view_id.clone()]);
notify_parent_view_did_change(
&workspace_id,
self.mutex_folder.clone(),
vec![view.parent_view_id.clone()],
);
Ok(view)
}
@ -1049,6 +1014,7 @@ impl FolderManager {
where
F: FnOnce(ViewUpdate) -> Option<View>,
{
let workspace_id = self.user.workspace_id()?;
let value = self.with_folder(
|| None,
|folder| {
@ -1070,11 +1036,9 @@ impl FolderManager {
.payload(view_pb)
.send();
if let Ok(workspace_id) = self.get_current_workspace_id().await {
let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder);
}
let folder = &self.mutex_folder.read();
if let Some(folder) = folder.as_ref() {
notify_did_update_workspace(&workspace_id, folder);
}
}
@ -1100,12 +1064,13 @@ impl FolderManager {
/// Otherwise, the parent_view_id is the parent view id of the view. The child_view_ids is the
/// child view ids of the view.
async fn get_view_relation(&self, view_id: &str) -> Option<(bool, String, Vec<String>)> {
let workspace_id = self.user.workspace_id().ok()?;
self.with_folder(
|| None,
|folder| {
let view = folder.views.get_view(view_id)?;
match folder.views.get_view(&view.parent_view_id) {
None => folder.get_current_workspace().map(|workspace| {
None => folder.get_workspace_info(&workspace_id).map(|workspace| {
(
true,
workspace.id,
@ -1154,18 +1119,6 @@ impl FolderManager {
Ok(snapshots)
}
/// Only expose this method for testing
#[cfg(debug_assertions)]
pub fn get_mutex_folder(&self) -> &Arc<MutexFolder> {
&self.mutex_folder
}
/// Only expose this method for testing
#[cfg(debug_assertions)]
pub fn get_cloud_service(&self) -> &Arc<dyn FolderCloudService> {
&self.cloud_service
}
pub fn set_views_visibility(&self, view_ids: Vec<String>, is_public: bool) {
self.with_folder(
|| (),
@ -1239,7 +1192,7 @@ impl FolderManager {
}
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.
pub(crate) fn get_workspace_public_view_pbs(folder: &Folder) -> Vec<ViewPB> {
pub(crate) fn get_workspace_public_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids
let trash_ids = folder
.get_all_trash_sections()
@ -1254,8 +1207,7 @@ pub(crate) fn get_workspace_public_view_pbs(folder: &Folder) -> Vec<ViewPB> {
.map(|view| view.id)
.collect::<Vec<String>>();
let mut views = folder.get_workspace_views();
let mut views = folder.views.get_views_belong_to(workspace_id);
// filter the views that are in the trash and all the private views
views.retain(|view| !trash_ids.contains(&view.id) && !private_view_ids.contains(&view.id));
@ -1289,7 +1241,7 @@ fn get_all_child_view_ids(folder: &Folder, view_id: &str) -> Vec<String> {
}
/// Get the current private views of the user.
pub(crate) fn get_workspace_private_view_pbs(folder: &Folder) -> Vec<ViewPB> {
pub(crate) fn get_workspace_private_view_pbs(workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
// get the trash ids
let trash_ids = folder
.get_all_trash_sections()
@ -1304,8 +1256,7 @@ pub(crate) fn get_workspace_private_view_pbs(folder: &Folder) -> Vec<ViewPB> {
.map(|view| view.id)
.collect::<Vec<String>>();
let mut views = folder.get_workspace_views();
let mut views = folder.views.get_views_belong_to(workspace_id);
// filter the views that are in the trash and not in the private view ids
views.retain(|view| !trash_ids.contains(&view.id) && private_view_ids.contains(&view.id));
@ -1342,8 +1293,6 @@ pub enum FolderInitDataSource {
LocalDisk { create_if_not_exist: bool },
/// If there is no data stored on local disk, we will use the data from the server to initialize the folder
Cloud(Vec<u8>),
/// If the user is new, we use the [DefaultFolderBuilder] to create the default folder.
FolderData(FolderData),
}
impl Display for FolderInitDataSource {
@ -1351,7 +1300,6 @@ impl Display for FolderInitDataSource {
match self {
FolderInitDataSource::LocalDisk { .. } => f.write_fmt(format_args!("LocalDisk")),
FolderInitDataSource::Cloud(_) => f.write_fmt(format_args!("Cloud")),
FolderInitDataSource::FolderData(_) => f.write_fmt(format_args!("Custom FolderData")),
}
}
}

View File

@ -1,22 +1,14 @@
use collab_entity::CollabType;
use collab_folder::{Folder, FolderNotify, UserId};
use tokio::task::spawn_blocking;
use tracing::{event, Level};
use collab_integrate::CollabKVDB;
use flowy_error::{FlowyError, FlowyResult};
use collab::core::collab::DataSource;
use std::sync::{Arc, Weak};
use crate::manager::{FolderInitDataSource, FolderManager};
use crate::manager_observer::{
subscribe_folder_snapshot_state_changed, subscribe_folder_sync_state_changed,
subscribe_folder_trash_changed, subscribe_folder_view_changed,
};
use crate::manager_observer::*;
use crate::user_default::DefaultFolderBuilder;
use collab::core::collab::DataSource;
use collab_entity::CollabType;
use collab_folder::{Folder, FolderNotify, UserId};
use collab_integrate::CollabKVDB;
use flowy_error::{FlowyError, FlowyResult};
use std::sync::{Arc, Weak};
use tokio::task::spawn_blocking;
use tracing::{event, info, Level};
impl FolderManager {
/// Called immediately after the application launched if the user already sign in/sign up.
@ -34,9 +26,13 @@ impl FolderManager {
workspace_id,
initial_data
);
*self.workspace_id.write() = Some(workspace_id.to_string());
let workspace_id = workspace_id.to_string();
if let Some(old_folder) = self.mutex_folder.write().take() {
old_folder.close();
info!("remove old folder: {}", old_folder.get_workspace_id());
}
let workspace_id = workspace_id.to_string();
// Get the collab db for the user with given user id.
let collab_db = self.user.collab_db(uid)?;
@ -115,22 +111,6 @@ impl FolderManager {
.await?
}
},
FolderInitDataSource::FolderData(folder_data) => {
if folder_data.workspace.id != workspace_id {
return Err(FlowyError::workspace_data_not_match());
}
event!(Level::INFO, "Restore folder with passed-in folder data");
let collab = self
.create_empty_collab(uid, &workspace_id, collab_db)
.await?;
Folder::create(
UserId::from(uid),
collab,
Some(folder_notifier),
folder_data,
)
},
};
let folder_state_rx = folder.subscribe_sync_state();
@ -154,10 +134,28 @@ impl FolderManager {
*self.mutex_folder.write() = Some(folder);
let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
subscribe_folder_sync_state_changed(workspace_id.clone(), folder_state_rx, &weak_mutex_folder);
subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder);
subscribe_folder_trash_changed(section_change_rx, &weak_mutex_folder);
subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
subscribe_folder_sync_state_changed(
workspace_id.clone(),
folder_state_rx,
Arc::downgrade(&self.user),
);
subscribe_folder_snapshot_state_changed(
workspace_id.clone(),
&weak_mutex_folder,
Arc::downgrade(&self.user),
);
subscribe_folder_trash_changed(
workspace_id.clone(),
section_change_rx,
&weak_mutex_folder,
Arc::downgrade(&self.user),
);
subscribe_folder_view_changed(
workspace_id.clone(),
view_rx,
&weak_mutex_folder,
Arc::downgrade(&self.user),
);
Ok(())
}

View File

@ -1,32 +1,43 @@
use std::collections::HashSet;
use std::sync::{Arc, Weak};
use crate::entities::{
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
};
use crate::manager::{
get_workspace_private_view_pbs, get_workspace_public_view_pbs, FolderUser, MutexFolder,
};
use crate::notification::{send_notification, FolderNotification};
use collab::core::collab_state::SyncState;
use collab_folder::{
Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange,
ViewChangeReceiver,
};
use lib_dispatch::prelude::af_spawn;
use std::collections::HashSet;
use std::sync::{Arc, Weak};
use tokio_stream::wrappers::WatchStream;
use tokio_stream::StreamExt;
use tracing::{event, Level};
use lib_dispatch::prelude::af_spawn;
use crate::entities::{
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
};
use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, MutexFolder};
use crate::notification::{send_notification, FolderNotification};
use tracing::{event, trace, Level};
/// Listen on the [ViewChange] after create/delete/update events happened
pub(crate) fn subscribe_folder_view_changed(
workspace_id: String,
mut rx: ViewChangeReceiver,
weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) {
let weak_mutex_folder = weak_mutex_folder.clone();
af_spawn(async move {
while let Ok(value) = rx.recv().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
trace!("Did break the loop when the workspace id is not matched");
// break the loop when the workspace id is not matched.
break;
}
}
}
if let Some(folder) = weak_mutex_folder.upgrade() {
tracing::trace!("Did receive view change: {:?}", value);
match value {
@ -35,7 +46,7 @@ pub(crate) fn subscribe_folder_view_changed(
view_pb_without_child_views(view.clone()),
ChildViewChangeReason::Create,
);
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id]);
notify_parent_view_did_change(&workspace_id, folder.clone(), vec![view.parent_view_id]);
},
ViewChange::DidDeleteView { views } => {
for view in views {
@ -51,7 +62,11 @@ pub(crate) fn subscribe_folder_view_changed(
view_pb_without_child_views(view.clone()),
ChildViewChangeReason::Update,
);
notify_parent_view_did_change(folder.clone(), vec![view.parent_view_id.clone()]);
notify_parent_view_did_change(
&workspace_id,
folder.clone(),
vec![view.parent_view_id.clone()],
);
},
};
}
@ -62,6 +77,7 @@ pub(crate) fn subscribe_folder_view_changed(
pub(crate) fn subscribe_folder_snapshot_state_changed(
workspace_id: String,
weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) {
let weak_mutex_folder = weak_mutex_folder.clone();
af_spawn(async move {
@ -72,6 +88,14 @@ pub(crate) fn subscribe_folder_snapshot_state_changed(
.map(|folder| folder.subscribe_snapshot_state());
if let Some(mut state_stream) = stream {
while let Some(snapshot_state) = state_stream.next().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
// break the loop when the workspace id is not matched.
break;
}
}
}
if let Some(new_snapshot_id) = snapshot_state.snapshot_id() {
tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id);
send_notification(
@ -90,10 +114,19 @@ pub(crate) fn subscribe_folder_snapshot_state_changed(
pub(crate) fn subscribe_folder_sync_state_changed(
workspace_id: String,
mut folder_sync_state_rx: WatchStream<SyncState>,
_weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) {
af_spawn(async move {
while let Some(state) = folder_sync_state_rx.next().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
// break the loop when the workspace id is not matched.
break;
}
}
}
send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
.payload(FolderSyncStatePB::from(state))
.send();
@ -103,12 +136,23 @@ pub(crate) fn subscribe_folder_sync_state_changed(
/// Listen on the [TrashChange]s and notify the frontend some views were changed.
pub(crate) fn subscribe_folder_trash_changed(
workspace_id: String,
mut rx: SectionChangeReceiver,
weak_mutex_folder: &Weak<MutexFolder>,
user: Weak<dyn FolderUser>,
) {
let weak_mutex_folder = weak_mutex_folder.clone();
af_spawn(async move {
while let Ok(value) = rx.recv().await {
if let Some(user) = user.upgrade() {
if let Ok(actual_workspace_id) = user.workspace_id() {
if actual_workspace_id != workspace_id {
// break the loop when the workspace id is not matched.
break;
}
}
}
if let Some(folder) = weak_mutex_folder.upgrade() {
let mut unique_ids = HashSet::new();
tracing::trace!("Did receive trash change: {:?}", value);
@ -132,7 +176,7 @@ pub(crate) fn subscribe_folder_trash_changed(
}
let parent_view_ids = unique_ids.into_iter().collect();
notify_parent_view_did_change(folder.clone(), parent_view_ids);
notify_parent_view_did_change(&workspace_id, folder.clone(), parent_view_ids);
},
}
}
@ -143,12 +187,12 @@ pub(crate) fn subscribe_folder_trash_changed(
/// Notify the list of parent view ids that its child views were changed.
#[tracing::instrument(level = "debug", skip(folder, parent_view_ids))]
pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
workspace_id: &str,
folder: Arc<MutexFolder>,
parent_view_ids: Vec<T>,
) -> Option<()> {
let folder = folder.read();
let folder = folder.as_ref()?;
let workspace_id = folder.get_workspace_id();
let trash_ids = folder
.get_all_trash_sections()
.into_iter()
@ -161,8 +205,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
// if the view's parent id equal to workspace id. Then it will fetch the current
// workspace views. Because the workspace is not a view stored in the views map.
if parent_view_id == workspace_id {
notify_did_update_workspace(&workspace_id, folder);
notify_did_update_section_views(&workspace_id, folder);
notify_did_update_workspace(workspace_id, folder);
notify_did_update_section_views(workspace_id, folder);
} else {
// Parent view can contain a list of child views. Currently, only get the first level
// child views.
@ -183,8 +227,8 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
}
pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) {
let public_views = get_workspace_public_view_pbs(folder);
let private_views = get_workspace_private_view_pbs(folder);
let public_views = get_workspace_public_view_pbs(workspace_id, folder);
let private_views = get_workspace_private_view_pbs(workspace_id, folder);
tracing::trace!(
"Did update section views: public len = {}, private len = {}",
public_views.len(),
@ -210,7 +254,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde
}
pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(folder).into();
let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into();
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
.payload(repeated_view)
.send();

View File

@ -0,0 +1,32 @@
use crate::manager::{FolderManager, FolderUser, MutexFolder};
use crate::view_operation::FolderOperationHandlers;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_folder_pub::cloud::FolderCloudService;
use flowy_search_pub::entities::FolderIndexManager;
use std::sync::Arc;
impl FolderManager {
pub fn get_mutex_folder(&self) -> Arc<MutexFolder> {
self.mutex_folder.clone()
}
pub fn get_cloud_service(&self) -> Arc<dyn FolderCloudService> {
self.cloud_service.clone()
}
pub fn get_user(&self) -> Arc<dyn FolderUser> {
self.user.clone()
}
pub fn get_indexer(&self) -> Arc<dyn FolderIndexManager> {
self.folder_indexer.clone()
}
pub fn get_collab_builder(&self) -> Arc<AppFlowyCollabBuilder> {
self.collab_builder.clone()
}
pub fn get_operation_handlers(&self) -> FolderOperationHandlers {
self.operation_handlers.clone()
}
}

View File

@ -1,14 +1,11 @@
use std::collections::HashMap;
use std::sync::Arc;
use super::notifier::{SearchNotifier, SearchResultChanged, SearchResultReceiverRunner};
use crate::entities::{SearchFilterPB, SearchResultNotificationPB, SearchResultPB};
use flowy_error::FlowyResult;
use lib_dispatch::prelude::af_spawn;
use tokio::{sync::broadcast, task::spawn_blocking};
use crate::entities::{SearchFilterPB, SearchResultNotificationPB, SearchResultPB};
use super::notifier::{SearchNotifier, SearchResultChanged, SearchResultReceiverRunner};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum SearchType {
Folder,

View File

@ -1,4 +1,12 @@
use flowy_error::FlowyResult;
pub const USER_SIGN_IN_URL: &str = "sign_in_url";
pub const USER_UUID: &str = "uuid";
pub const USER_EMAIL: &str = "email";
pub const USER_DEVICE_ID: &str = "device_id";
/// Represents a user that is currently using the server.
pub trait ServerUser: Send + Sync {
/// different user might return different workspace id.
fn workspace_id(&self) -> FlowyResult<String>;
}

View File

@ -5,19 +5,26 @@ use client_api::error::ErrorCode::RecordNotFound;
use collab::core::collab::DataSource;
use collab::entity::EncodedCollab;
use collab_entity::CollabType;
use tracing::error;
use std::sync::Arc;
use tracing::{error, instrument};
use flowy_database_pub::cloud::{CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot};
use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer;
pub(crate) struct AFCloudDatabaseCloudServiceImpl<T>(pub T);
pub(crate) struct AFCloudDatabaseCloudServiceImpl<T> {
pub inner: T,
pub user: Arc<dyn ServerUser>,
}
impl<T> DatabaseCloudService for AFCloudDatabaseCloudServiceImpl<T>
where
T: AFServer,
{
#[instrument(level = "debug", skip_all)]
fn get_database_object_doc_state(
&self,
object_id: &str,
@ -26,17 +33,25 @@ where
) -> FutureResult<Option<Vec<u8>>, Error> {
let workspace_id = workspace_id.to_string();
let object_id = object_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams {
workspace_id,
workspace_id: workspace_id.clone(),
inner: QueryCollab {
object_id,
collab_type,
object_id: object_id.clone(),
collab_type: collab_type.clone(),
},
};
match try_get_client?.get_collab(params).await {
Ok(data) => Ok(Some(data.encode_collab.doc_state.to_vec())),
Ok(data) => {
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
format!("get database object: {}:{}", object_id, collab_type),
)?;
Ok(Some(data.encode_collab.doc_state.to_vec()))
},
Err(err) => {
if err.code == RecordNotFound {
Ok(None)
@ -48,6 +63,7 @@ where
})
}
#[instrument(level = "debug", skip_all)]
fn batch_get_database_object_doc_state(
&self,
object_ids: Vec<String>,
@ -55,7 +71,8 @@ where
workspace_id: &str,
) -> FutureResult<CollabDocStateByOid, Error> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let client = try_get_client?;
let params = object_ids
@ -66,6 +83,11 @@ where
})
.collect();
let results = client.batch_get_collab(&workspace_id, params).await?;
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
"batch get database object",
)?;
Ok(
results
.0

View File

@ -4,30 +4,39 @@ use collab::core::collab::DataSource;
use collab::core::origin::CollabOrigin;
use collab_document::document::Document;
use collab_entity::CollabType;
use std::sync::Arc;
use tracing::instrument;
use flowy_document_pub::cloud::*;
use flowy_error::FlowyError;
use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer;
pub(crate) struct AFCloudDocumentCloudServiceImpl<T>(pub T);
pub(crate) struct AFCloudDocumentCloudServiceImpl<T> {
pub inner: T,
pub user: Arc<dyn ServerUser>,
}
impl<T> DocumentCloudService for AFCloudDocumentCloudServiceImpl<T>
where
T: AFServer,
{
#[instrument(level = "debug", skip_all, fields(document_id = %document_id))]
fn get_document_doc_state(
&self,
document_id: &str,
workspace_id: &str,
) -> FutureResult<Vec<u8>, FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let document_id = document_id.to_string();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams {
workspace_id,
workspace_id: workspace_id.clone(),
inner: QueryCollab {
object_id: document_id.to_string(),
collab_type: CollabType::Document,
@ -40,6 +49,13 @@ where
.encode_collab
.doc_state
.to_vec();
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
format!("get document doc state:{}", document_id),
)?;
Ok(doc_state)
})
}
@ -53,17 +69,19 @@ where
FutureResult::new(async move { Ok(vec![]) })
}
#[instrument(level = "debug", skip_all)]
fn get_document_data(
&self,
document_id: &str,
workspace_id: &str,
) -> FutureResult<Option<DocumentData>, Error> {
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let document_id = document_id.to_string();
let workspace_id = workspace_id.to_string();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams {
workspace_id,
workspace_id: workspace_id.clone(),
inner: QueryCollab {
object_id: document_id.clone(),
collab_type: CollabType::Document,
@ -76,6 +94,11 @@ where
.encode_collab
.doc_state
.to_vec();
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
format!("Get {} document", document_id),
)?;
let document = Document::from_doc_state(
CollabOrigin::Empty,
DataSource::DocStateV1(doc_state),

View File

@ -6,6 +6,8 @@ use collab::core::collab::DataSource;
use collab::core::origin::CollabOrigin;
use collab_entity::CollabType;
use collab_folder::RepeatedViewIdentifier;
use std::sync::Arc;
use tracing::instrument;
use flowy_error::FlowyError;
use flowy_folder_pub::cloud::{
@ -14,16 +16,21 @@ use flowy_folder_pub::cloud::{
};
use lib_infra::future::FutureResult;
use crate::af_cloud::define::ServerUser;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::AFServer;
pub(crate) struct AFCloudFolderCloudServiceImpl<T>(pub T);
pub(crate) struct AFCloudFolderCloudServiceImpl<T> {
pub inner: T,
pub user: Arc<dyn ServerUser>,
}
impl<T> FolderCloudService for AFCloudFolderCloudServiceImpl<T>
where
T: AFServer,
{
fn create_workspace(&self, _uid: i64, name: &str) -> FutureResult<Workspace, Error> {
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let cloned_name = name.to_string();
FutureResult::new(async move {
let client = try_get_client?;
@ -47,7 +54,7 @@ where
fn open_workspace(&self, workspace_id: &str) -> FutureResult<(), Error> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
FutureResult::new(async move {
let client = try_get_client?;
let _ = client.open_workspace(&workspace_id).await?;
@ -56,7 +63,7 @@ where
}
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> {
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
FutureResult::new(async move {
let client = try_get_client?;
let records = client
@ -73,7 +80,7 @@ where
Ok(records)
})
}
#[instrument(level = "debug", skip_all)]
fn get_folder_data(
&self,
workspace_id: &str,
@ -81,7 +88,8 @@ where
) -> FutureResult<Option<FolderData>, Error> {
let uid = *uid;
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams {
workspace_id: workspace_id.clone(),
@ -97,6 +105,7 @@ where
.encode_collab
.doc_state
.to_vec();
check_request_workspace_id_is_match(&workspace_id, &cloned_user, "get folder data")?;
let folder = Folder::from_collab_doc_state(
uid,
CollabOrigin::Empty,
@ -104,7 +113,7 @@ where
&workspace_id,
vec![],
)?;
Ok(folder.get_folder_data())
Ok(folder.get_folder_data(&workspace_id))
})
}
@ -116,6 +125,7 @@ where
FutureResult::new(async move { Ok(vec![]) })
}
#[instrument(level = "debug", skip_all)]
fn get_folder_doc_state(
&self,
workspace_id: &str,
@ -125,10 +135,11 @@ where
) -> FutureResult<Vec<u8>, Error> {
let object_id = object_id.to_string();
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams {
workspace_id,
workspace_id: workspace_id.clone(),
inner: QueryCollab {
object_id,
collab_type,
@ -141,6 +152,7 @@ where
.encode_collab
.doc_state
.to_vec();
check_request_workspace_id_is_match(&workspace_id, &cloned_user, "get folder doc state")?;
Ok(doc_state)
})
}
@ -151,7 +163,7 @@ where
objects: Vec<FolderCollabParams>,
) -> FutureResult<(), Error> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let try_get_client = self.inner.try_get_client();
FutureResult::new(async move {
let params = objects
.into_iter()

View File

@ -9,3 +9,4 @@ mod document;
mod file_storage;
mod folder;
mod user;
mod util;

View File

@ -13,6 +13,7 @@ use client_api::entity::{QueryCollab, QueryCollabParams};
use client_api::{Client, ClientConfiguration};
use collab_entity::{CollabObject, CollabType};
use parking_lot::RwLock;
use tracing::instrument;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_user_pub::cloud::{UserCloudService, UserCollabParams, UserUpdate, UserUpdateReceiver};
@ -24,11 +25,12 @@ use lib_infra::box_any::BoxAny;
use lib_infra::future::FutureResult;
use uuid::Uuid;
use crate::af_cloud::define::USER_SIGN_IN_URL;
use crate::af_cloud::define::{ServerUser, USER_SIGN_IN_URL};
use crate::af_cloud::impls::user::dto::{
af_update_from_update_params, from_af_workspace_member, to_af_role, user_profile_from_af_profile,
};
use crate::af_cloud::impls::user::util::encryption_type_from_profile;
use crate::af_cloud::impls::util::check_request_workspace_id_is_match;
use crate::af_cloud::{AFCloudClient, AFServer};
use super::dto::{from_af_workspace_invitation_status, to_workspace_invitation_status};
@ -36,13 +38,19 @@ use super::dto::{from_af_workspace_invitation_status, to_workspace_invitation_st
pub(crate) struct AFCloudUserAuthServiceImpl<T> {
server: T,
user_change_recv: RwLock<Option<tokio::sync::mpsc::Receiver<UserUpdate>>>,
user: Arc<dyn ServerUser>,
}
impl<T> AFCloudUserAuthServiceImpl<T> {
pub(crate) fn new(server: T, user_change_recv: tokio::sync::mpsc::Receiver<UserUpdate>) -> Self {
pub(crate) fn new(
server: T,
user_change_recv: tokio::sync::mpsc::Receiver<UserUpdate>,
user: Arc<dyn ServerUser>,
) -> Self {
Self {
server,
user_change_recv: RwLock::new(Some(user_change_recv)),
user,
}
}
}
@ -166,16 +174,27 @@ where
})
}
#[instrument(level = "debug", skip_all)]
fn get_user_profile(
&self,
_credential: UserCredentials,
) -> FutureResult<UserProfile, FlowyError> {
let try_get_client = self.server.try_get_client();
let cloned_user = self.user.clone();
FutureResult::new(async move {
let expected_workspace_id = cloned_user.workspace_id()?;
let client = try_get_client?;
let profile = client.get_profile().await?;
let token = client.get_token()?;
let profile = user_profile_from_af_profile(token, profile)?;
// Discard the response if the user has switched to a new workspace. This avoids updating the
// user profile with potentially outdated information when the workspace ID no longer matches.
check_request_workspace_id_is_match(
&expected_workspace_id,
&cloned_user,
"get user profile",
)?;
Ok(profile)
})
}
@ -315,6 +334,7 @@ where
})
}
#[instrument(level = "debug", skip_all)]
fn get_user_awareness_doc_state(
&self,
_uid: i64,
@ -324,16 +344,21 @@ where
let workspace_id = workspace_id.to_string();
let object_id = object_id.to_string();
let try_get_client = self.server.try_get_client();
FutureResult::new(async {
let cloned_user = self.user.clone();
FutureResult::new(async move {
let params = QueryCollabParams {
workspace_id,
workspace_id: workspace_id.clone(),
inner: QueryCollab {
object_id,
collab_type: CollabType::UserAwareness,
},
};
let resp = try_get_client?.get_collab(params).await?;
check_request_workspace_id_is_match(
&workspace_id,
&cloned_user,
"get user awareness object",
)?;
Ok(resp.encode_collab.doc_state.to_vec())
})
}

View File

@ -0,0 +1,29 @@
use crate::af_cloud::define::ServerUser;
use flowy_error::{FlowyError, FlowyResult};
use std::sync::Arc;
use tracing::warn;
/// Validates the workspace_id provided in the request.
/// It checks that the workspace_id from the request matches the current user's active workspace_id.
/// This ensures that the operation is being performed in the correct workspace context, enhancing security.
pub fn check_request_workspace_id_is_match(
expected_workspace_id: &str,
user: &Arc<dyn ServerUser>,
action: impl AsRef<str>,
) -> FlowyResult<()> {
let actual_workspace_id = user.workspace_id()?;
if expected_workspace_id != actual_workspace_id {
warn!(
"{}, expect workspace_id: {}, actual workspace_id: {}",
action.as_ref(),
expected_workspace_id,
actual_workspace_id
);
return Err(
FlowyError::internal()
.with_context("Current workspace was changed when processing the request"),
);
}
Ok(())
}

View File

@ -17,6 +17,7 @@ use tokio_stream::wrappers::WatchStream;
use tracing::{error, event, info, warn};
use uuid::Uuid;
use crate::af_cloud::define::ServerUser;
use flowy_database_pub::cloud::DatabaseCloudService;
use flowy_document_pub::cloud::DocumentCloudService;
use flowy_error::{ErrorCode, FlowyError};
@ -42,6 +43,7 @@ pub struct AppFlowyCloudServer {
network_reachable: Arc<AtomicBool>,
pub device_id: String,
ws_client: Arc<WSClient>,
user: Arc<dyn ServerUser>,
}
impl AppFlowyCloudServer {
@ -50,6 +52,7 @@ impl AppFlowyCloudServer {
enable_sync: bool,
mut device_id: String,
client_version: &str,
user: Arc<dyn ServerUser>,
) -> Self {
// The device id can't be empty, so we generate a new one if it is.
if device_id.is_empty() {
@ -83,6 +86,7 @@ impl AppFlowyCloudServer {
network_reachable,
device_id,
ws_client,
user,
}
}
@ -159,28 +163,41 @@ impl AppFlowyServer for AppFlowyCloudServer {
}
});
Arc::new(AFCloudUserAuthServiceImpl::new(server, rx))
Arc::new(AFCloudUserAuthServiceImpl::new(
server,
rx,
self.user.clone(),
))
}
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
let server = AFServerImpl {
client: self.get_client(),
};
Arc::new(AFCloudFolderCloudServiceImpl(server))
Arc::new(AFCloudFolderCloudServiceImpl {
inner: server,
user: self.user.clone(),
})
}
fn database_service(&self) -> Arc<dyn DatabaseCloudService> {
let server = AFServerImpl {
client: self.get_client(),
};
Arc::new(AFCloudDatabaseCloudServiceImpl(server))
Arc::new(AFCloudDatabaseCloudServiceImpl {
inner: server,
user: self.user.clone(),
})
}
fn document_service(&self) -> Arc<dyn DocumentCloudService> {
let server = AFServerImpl {
client: self.get_client(),
};
Arc::new(AFCloudDocumentCloudServiceImpl(server))
Arc::new(AFCloudDocumentCloudServiceImpl {
inner: server,
user: self.user.clone(),
})
}
fn subscribe_ws_state(&self) -> Option<WSConnectStateReceiver> {

View File

@ -109,7 +109,7 @@ where
&workspace_id,
vec![],
)?;
Ok(folder.get_folder_data())
Ok(folder.get_folder_data(&workspace_id))
})
}

View File

@ -2,8 +2,10 @@ use client_api::ClientConfiguration;
use std::collections::HashMap;
use std::sync::Arc;
use flowy_error::FlowyResult;
use uuid::Uuid;
use flowy_server::af_cloud::define::ServerUser;
use flowy_server::af_cloud::AppFlowyCloudServer;
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_SIGN_IN_URL};
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
@ -31,9 +33,17 @@ pub fn af_cloud_server(config: AFCloudConfiguration) -> Arc<AppFlowyCloudServer>
true,
fake_device_id,
"0.5.1",
Arc::new(FakeServerUserImpl),
))
}
struct FakeServerUserImpl;
impl ServerUser for FakeServerUserImpl {
fn workspace_id(&self) -> FlowyResult<String> {
todo!()
}
}
pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguration) -> String {
let client = client_api::Client::new(
&config.base_url,

View File

@ -134,7 +134,7 @@ pub async fn print_encryption_folder_snapshot(
));
let folder_data = Folder::open(uid, collab, None)
.unwrap()
.get_folder_data()
.get_folder_data(folder_id)
.unwrap();
let json = serde_json::to_value(folder_data).unwrap();
println!("{}", serde_json::to_string_pretty(&json).unwrap());

View File

@ -226,11 +226,12 @@ where
None,
)
.map_err(|err| PersistenceError::InvalidData(err.to_string()))?;
let mut folder_data = old_folder
.get_folder_data()
.ok_or(PersistenceError::Internal(anyhow!(
"Can't migrate the folder data"
)))?;
let mut folder_data =
old_folder
.get_folder_data(old_workspace_id)
.ok_or(PersistenceError::Internal(anyhow!(
"Can't migrate the folder data"
)))?;
if let Some(old_fav_map) = folder_data.favorites.remove(&old_user_id) {
let fav_map = old_fav_map

View File

@ -49,7 +49,7 @@ pub async fn sync_supabase_user_data_to_cloud(
)
.await;
let views = folder.lock().get_current_workspace_views();
let views = folder.lock().get_views_belong_to(&workspace_id);
for view in views {
let view_id = view.id.clone();
if let Err(err) = sync_view(

View File

@ -53,17 +53,18 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
let folder = Folder::open(session.user_id, folder_collab, None)
.map_err(|err| PersistenceError::Internal(err.into()))?;
let migration_views = folder.get_workspace_views();
// For historical reasons, the first level documents are empty. So migrate them by inserting
// the default document data.
for view in migration_views {
if migrate_empty_document(write_txn, &origin, &view, session.user_id).is_err() {
event!(
tracing::Level::ERROR,
"Failed to migrate document {}",
view.id
);
if let Ok(workspace_id) = folder.try_get_workspace_id() {
let migration_views = folder.views.get_views_belong_to(&workspace_id);
// For historical reasons, the first level documents are empty. So migrate them by inserting
// the default document data.
for view in migration_views {
if migrate_empty_document(write_txn, &origin, &view, session.user_id).is_err() {
event!(
tracing::Level::ERROR,
"Failed to migrate document {}",
view.id
);
}
}
}

View File

@ -16,7 +16,7 @@ use tracing::{error, info};
const SQLITE_VACUUM_042: &str = "sqlite_vacuum_042_version";
pub struct AuthenticateUser {
pub(crate) user_config: UserConfig,
pub user_config: UserConfig,
pub(crate) database: Arc<UserDB>,
pub(crate) user_paths: UserPaths,
store_preferences: Arc<StorePreferences>,
@ -67,6 +67,11 @@ impl AuthenticateUser {
Ok(session.user_workspace.id)
}
pub fn workspace_database_object_id(&self) -> FlowyResult<String> {
let session = self.get_session()?;
Ok(session.user_workspace.workspace_database_object_id.clone())
}
pub fn get_collab_db(&self, uid: i64) -> FlowyResult<Weak<CollabKVDB>> {
self
.database

View File

@ -509,7 +509,7 @@ where
.map_err(|err| PersistenceError::InvalidData(err.to_string()))?;
let other_folder_data = other_folder
.get_folder_data()
.get_folder_data(&other_session.user_workspace.id)
.ok_or(PersistenceError::Internal(anyhow!(
"Can't read the folder data"
)))?;

View File

@ -518,7 +518,6 @@ impl UserManager {
pub async fn prepare_user(&self, session: &Session) {
let _ = self.authenticate_user.database.close(session.user_id);
self.prepare_collab(session);
}
pub async fn prepare_backup(&self, session: &Session) {
@ -724,11 +723,6 @@ impl UserManager {
Ok(())
}
fn prepare_collab(&self, session: &Session) {
let collab_builder = self.collab_builder.upgrade().unwrap();
collab_builder.initialize(session.user_workspace.id.clone());
}
async fn handler_user_update(&self, user_update: UserUpdate) -> FlowyResult<()> {
let session = self.get_session()?;
if session.user_id == user_update.uid {

View File

@ -136,6 +136,7 @@ impl UserManager {
let weak_builder = self.collab_builder.clone();
let cloned_is_loading = self.is_loading_awareness.clone();
let session = session.clone();
let workspace_id = session.user_workspace.id.clone();
tokio::spawn(async move {
if cloned_is_loading.load(Ordering::SeqCst) {
return Ok(());
@ -160,6 +161,7 @@ impl UserManager {
Ok(data) => {
trace!("Get user awareness collab from remote: {}", data.len());
let collab = Self::collab_for_user_awareness(
&workspace_id,
&weak_builder,
session.user_id,
&object_id,
@ -173,6 +175,7 @@ impl UserManager {
if err.is_record_not_found() {
info!("User awareness not found, creating new");
let collab = Self::collab_for_user_awareness(
&workspace_id,
&weak_builder,
session.user_id,
&object_id,
@ -206,6 +209,7 @@ impl UserManager {
/// using a collaboration builder. This instance is specifically geared towards handling
/// user awareness.
async fn collab_for_user_awareness(
workspace_id: &str,
collab_builder: &Weak<AppFlowyCollabBuilder>,
uid: i64,
object_id: &str,
@ -218,6 +222,7 @@ impl UserManager {
))?;
let collab = collab_builder
.build(
workspace_id,
uid,
object_id,
CollabType::UserAwareness,

View File

@ -136,7 +136,6 @@ impl UserManager {
#[instrument(skip(self), err)]
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
info!("open workspace: {}", workspace_id);
let uid = self.user_id()?;
let user_workspace = self
.cloud_services
.get_user_service()?
@ -146,6 +145,8 @@ impl UserManager {
self
.authenticate_user
.set_user_workspace(user_workspace.clone())?;
let uid = self.user_id()?;
if let Err(err) = self
.user_status_callback
.read()