mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-07-26 03:23:01 +00:00
chore: Ai chat context (#6929)
* chore: implement chat setting * chore: clippy * chore: rename * chore: set rag_ids when creating a chat * chore: clippy * chore: fix test * chore: fix test * chore: fix test * chore: clippy
This commit is contained in:
2
.github/workflows/rust_ci.yaml
vendored
2
.github/workflows/rust_ci.yaml
vendored
@ -18,7 +18,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
CLOUD_VERSION: 0.7.6-amd64
|
||||
CLOUD_VERSION: 0.8.3-amd64
|
||||
RUST_TOOLCHAIN: "1.80.1"
|
||||
|
||||
jobs:
|
||||
|
@ -77,19 +77,19 @@ class ChatFile extends Equatable {
|
||||
final fileName = path.basename(filePath);
|
||||
final extension = path.extension(filePath).toLowerCase();
|
||||
|
||||
ChatMessageMetaTypePB fileType;
|
||||
ContextLoaderTypePB fileType;
|
||||
switch (extension) {
|
||||
case '.pdf':
|
||||
fileType = ChatMessageMetaTypePB.PDF;
|
||||
fileType = ContextLoaderTypePB.PDF;
|
||||
break;
|
||||
case '.txt':
|
||||
fileType = ChatMessageMetaTypePB.Txt;
|
||||
fileType = ContextLoaderTypePB.Txt;
|
||||
break;
|
||||
case '.md':
|
||||
fileType = ChatMessageMetaTypePB.Markdown;
|
||||
fileType = ContextLoaderTypePB.Markdown;
|
||||
break;
|
||||
default:
|
||||
fileType = ChatMessageMetaTypePB.UnknownMetaType;
|
||||
fileType = ContextLoaderTypePB.UnknownLoaderType;
|
||||
}
|
||||
|
||||
return ChatFile(
|
||||
@ -101,7 +101,7 @@ class ChatFile extends Equatable {
|
||||
|
||||
final String filePath;
|
||||
final String fileName;
|
||||
final ChatMessageMetaTypePB fileType;
|
||||
final ContextLoaderTypePB fileType;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [filePath];
|
||||
|
@ -138,7 +138,7 @@ Future<List<ChatMessageMetaPB>> metadataPBFromMetadata(
|
||||
id: value.id,
|
||||
name: value.name,
|
||||
data: pb.text,
|
||||
dataType: ChatMessageMetaTypePB.Txt,
|
||||
loaderType: ContextLoaderTypePB.Txt,
|
||||
source: appflowySource,
|
||||
),
|
||||
);
|
||||
@ -156,7 +156,7 @@ Future<List<ChatMessageMetaPB>> metadataPBFromMetadata(
|
||||
id: nanoid(8),
|
||||
name: fileName,
|
||||
data: filePath,
|
||||
dataType: fileType,
|
||||
loaderType: fileType,
|
||||
source: filePath,
|
||||
),
|
||||
);
|
||||
|
@ -18,11 +18,11 @@ class ImportPayload {
|
||||
class ImportBackendService {
|
||||
static Future<FlowyResult<RepeatedViewPB, FlowyError>> importPages(
|
||||
String parentViewId,
|
||||
List<ImportValuePayloadPB> values,
|
||||
List<ImportItemPayloadPB> values,
|
||||
) async {
|
||||
final request = ImportPayloadPB(
|
||||
parentViewId: parentViewId,
|
||||
values: values,
|
||||
items: values,
|
||||
);
|
||||
|
||||
return FolderEventImportData(request).send();
|
||||
|
@ -151,7 +151,7 @@ class _ImportPanelState extends State<ImportPanel> {
|
||||
|
||||
showLoading.value = true;
|
||||
|
||||
final importValues = <ImportValuePayloadPB>[];
|
||||
final importValues = <ImportItemPayloadPB>[];
|
||||
for (final file in result.files) {
|
||||
final path = file.path;
|
||||
if (path == null) {
|
||||
@ -163,7 +163,7 @@ class _ImportPanelState extends State<ImportPanel> {
|
||||
case ImportType.historyDatabase:
|
||||
final data = await File(path).readAsString();
|
||||
importValues.add(
|
||||
ImportValuePayloadPB.create()
|
||||
ImportItemPayloadPB.create()
|
||||
..name = name
|
||||
..data = utf8.encode(data)
|
||||
..viewLayout = ViewLayoutPB.Grid
|
||||
@ -176,7 +176,7 @@ class _ImportPanelState extends State<ImportPanel> {
|
||||
final bytes = _documentDataFrom(importType, data);
|
||||
if (bytes != null) {
|
||||
importValues.add(
|
||||
ImportValuePayloadPB.create()
|
||||
ImportItemPayloadPB.create()
|
||||
..name = name
|
||||
..data = bytes
|
||||
..viewLayout = ViewLayoutPB.Document
|
||||
@ -187,7 +187,7 @@ class _ImportPanelState extends State<ImportPanel> {
|
||||
case ImportType.csv:
|
||||
final data = await File(path).readAsString();
|
||||
importValues.add(
|
||||
ImportValuePayloadPB.create()
|
||||
ImportItemPayloadPB.create()
|
||||
..name = name
|
||||
..data = utf8.encode(data)
|
||||
..viewLayout = ViewLayoutPB.Grid
|
||||
@ -197,7 +197,7 @@ class _ImportPanelState extends State<ImportPanel> {
|
||||
case ImportType.afDatabase:
|
||||
final data = await File(path).readAsString();
|
||||
importValues.add(
|
||||
ImportValuePayloadPB.create()
|
||||
ImportItemPayloadPB.create()
|
||||
..name = name
|
||||
..data = utf8.encode(data)
|
||||
..viewLayout = ViewLayoutPB.Grid
|
||||
|
@ -121,7 +121,7 @@ class AppFlowyGridTest {
|
||||
final context = await ImportBackendService.importPages(
|
||||
workspace.id,
|
||||
[
|
||||
ImportValuePayloadPB()
|
||||
ImportItemPayloadPB()
|
||||
..name = fileName
|
||||
..data = utf8.encode(data)
|
||||
..viewLayout = ViewLayoutPB.Grid
|
||||
|
17
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
17
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -1030,7 +1030,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1055,7 +1055,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1094,7 +1094,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1115,7 +1115,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -1135,7 +1135,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1157,7 +1157,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-importer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@ -1218,7 +1218,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1298,7 +1298,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -2484,6 +2484,7 @@ dependencies = [
|
||||
"collab-folder",
|
||||
"collab-integrate",
|
||||
"collab-plugins",
|
||||
"dashmap 6.0.1",
|
||||
"flowy-codegen",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
|
@ -120,14 +120,14 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
|
||||
# Working directory: frontend
|
||||
# To update the commit ID, run:
|
||||
|
17
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
17
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
@ -1028,7 +1028,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1053,7 +1053,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1092,7 +1092,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1113,7 +1113,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -1133,7 +1133,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1155,7 +1155,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-importer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@ -1216,7 +1216,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1296,7 +1296,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -2529,6 +2529,7 @@ dependencies = [
|
||||
"collab-folder",
|
||||
"collab-integrate",
|
||||
"collab-plugins",
|
||||
"dashmap 6.0.1",
|
||||
"flowy-codegen",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
|
@ -118,14 +118,14 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
|
||||
|
||||
# Working directory: frontend
|
||||
|
17
frontend/rust-lib/Cargo.lock
generated
17
frontend/rust-lib/Cargo.lock
generated
@ -891,7 +891,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -916,7 +916,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -955,7 +955,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -976,7 +976,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -996,7 +996,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
@ -1018,7 +1018,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-importer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@ -1079,7 +1079,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1159,7 +1159,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -2349,6 +2349,7 @@ dependencies = [
|
||||
"collab-folder",
|
||||
"collab-integrate",
|
||||
"collab-plugins",
|
||||
"dashmap 6.0.1",
|
||||
"flowy-codegen",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
|
@ -142,14 +142,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120
|
||||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" }
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" }
|
||||
|
||||
# Working directory: frontend
|
||||
# To update the commit ID, run:
|
||||
|
@ -1,4 +1,4 @@
|
||||
use flowy_folder::view_operation::{EncodedCollabWrapper, ViewData};
|
||||
use flowy_folder::view_operation::{EncodedCollabType, ViewData};
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_folder::{FolderData, View};
|
||||
@ -165,15 +165,12 @@ impl EventIntegrationTest {
|
||||
}
|
||||
|
||||
pub async fn get_folder_data(&self) -> FolderData {
|
||||
let mutex_folder = self
|
||||
self
|
||||
.appflowy_core
|
||||
.folder_manager
|
||||
.get_mutex_folder()
|
||||
.clone()
|
||||
.unwrap();
|
||||
let folder = mutex_folder.read().await;
|
||||
let workspace_id = self.appflowy_core.user_manager.workspace_id().unwrap();
|
||||
folder.get_folder_data(&workspace_id).clone().unwrap()
|
||||
.get_folder_data()
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn get_publish_payload(
|
||||
@ -197,13 +194,10 @@ impl EventIntegrationTest {
|
||||
&self,
|
||||
view_id: &str,
|
||||
layout: ViewLayout,
|
||||
) -> EncodedCollabWrapper {
|
||||
let manager = self.folder_manager.clone();
|
||||
let user = manager.get_user().clone();
|
||||
let handlers = manager.get_operation_handlers();
|
||||
let handler = handlers.get(&layout).unwrap();
|
||||
handler
|
||||
.get_encoded_collab_v1_from_disk(user, view_id)
|
||||
) -> EncodedCollabType {
|
||||
self
|
||||
.folder_manager
|
||||
.get_encode_collab_from_disk(view_id, &layout)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ use flowy_user::entities::{
|
||||
};
|
||||
use flowy_user::errors::{FlowyError, FlowyResult};
|
||||
use flowy_user::event_map::UserEvent;
|
||||
use lib_dispatch::prelude::{af_spawn, AFPluginDispatcher, AFPluginRequest, ToBytes};
|
||||
use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes};
|
||||
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
@ -328,7 +328,7 @@ impl TestNotificationSender {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<T>(10);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
let ty = ty.into();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
// DatabaseNotification::DidUpdateDatabaseSnapshotState
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id && value.ty == ty {
|
||||
@ -361,7 +361,7 @@ impl TestNotificationSender {
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<()>(10);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
let ty = ty.into();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
// DatabaseNotification::DidUpdateDatabaseSnapshotState
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id && value.ty == ty {
|
||||
@ -380,7 +380,7 @@ impl TestNotificationSender {
|
||||
let id = id.to_string();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel::<T>(1);
|
||||
let mut receiver = self.sender.subscribe();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(value) = receiver.recv().await {
|
||||
if value.id == id {
|
||||
if let Some(payload) = value.payload {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::util::unzip;
|
||||
use event_integration_test::EventIntegrationTest;
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_folder::entities::{ImportPayloadPB, ImportTypePB, ImportValuePayloadPB, ViewLayoutPB};
|
||||
use flowy_folder::entities::{ImportItemPayloadPB, ImportPayloadPB, ImportTypePB, ViewLayoutPB};
|
||||
|
||||
#[tokio::test]
|
||||
async fn import_492_row_csv_file_test() {
|
||||
@ -47,7 +47,7 @@ async fn import_10240_row_csv_file_test() {
|
||||
fn gen_import_data(file_name: String, csv_string: String, workspace_id: String) -> ImportPayloadPB {
|
||||
ImportPayloadPB {
|
||||
parent_view_id: workspace_id.clone(),
|
||||
values: vec![ImportValuePayloadPB {
|
||||
items: vec![ImportItemPayloadPB {
|
||||
name: file_name,
|
||||
data: Some(csv_string.as_bytes().to_vec()),
|
||||
file_path: None,
|
||||
|
@ -3,9 +3,9 @@ use std::collections::HashMap;
|
||||
use collab_folder::ViewLayout;
|
||||
use event_integration_test::EventIntegrationTest;
|
||||
use flowy_folder::entities::{
|
||||
ImportPayloadPB, ImportTypePB, ImportValuePayloadPB, ViewLayoutPB, ViewPB,
|
||||
ImportItemPayloadPB, ImportPayloadPB, ImportTypePB, ViewLayoutPB, ViewPB,
|
||||
};
|
||||
use flowy_folder::view_operation::EncodedCollabWrapper;
|
||||
use flowy_folder::view_operation::EncodedCollabType;
|
||||
|
||||
use crate::util::unzip;
|
||||
|
||||
@ -22,7 +22,7 @@ async fn publish_single_database_test() {
|
||||
.await;
|
||||
|
||||
match grid_encoded_collab {
|
||||
EncodedCollabWrapper::Database(encoded_collab) => {
|
||||
EncodedCollabType::Database(encoded_collab) => {
|
||||
// the len of row collabs should be the same as the number of rows in the csv file
|
||||
let rows_len = encoded_collab.database_row_encoded_collabs.len();
|
||||
assert_eq!(rows_len, 18);
|
||||
@ -110,7 +110,7 @@ async fn test_publish_encode_collab_result(
|
||||
.await;
|
||||
|
||||
match encoded_collab {
|
||||
EncodedCollabWrapper::Database(encoded_collab) => {
|
||||
EncodedCollabType::Database(encoded_collab) => {
|
||||
if let Some(rows_len) = expectations.get(&view.name.as_str()) {
|
||||
assert_eq!(encoded_collab.database_row_encoded_collabs.len(), *rows_len);
|
||||
}
|
||||
@ -144,7 +144,7 @@ async fn import_csv(file_name: &str, test: &EventIntegrationTest) -> ViewPB {
|
||||
fn gen_import_data(file_name: String, csv_string: String, workspace_id: String) -> ImportPayloadPB {
|
||||
ImportPayloadPB {
|
||||
parent_view_id: workspace_id.clone(),
|
||||
values: vec![ImportValuePayloadPB {
|
||||
items: vec![ImportItemPayloadPB {
|
||||
name: file_name,
|
||||
data: Some(csv_string.as_bytes().to_vec()),
|
||||
file_path: None,
|
||||
|
@ -2,7 +2,7 @@ use collab_folder::ViewLayout;
|
||||
use event_integration_test::EventIntegrationTest;
|
||||
use flowy_folder::entities::{ViewLayoutPB, ViewPB};
|
||||
use flowy_folder::publish_util::generate_publish_name;
|
||||
use flowy_folder::view_operation::EncodedCollabWrapper;
|
||||
use flowy_folder::view_operation::EncodedCollabType;
|
||||
use flowy_folder_pub::entities::{
|
||||
PublishDocumentPayload, PublishPayload, PublishViewInfo, PublishViewMeta, PublishViewMetaData,
|
||||
};
|
||||
@ -29,7 +29,7 @@ async fn mock_single_document_view_publish_payload(
|
||||
};
|
||||
|
||||
let data = match view_encoded_collab {
|
||||
EncodedCollabWrapper::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(),
|
||||
EncodedCollabType::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(),
|
||||
_ => panic!("Expected document collab"),
|
||||
};
|
||||
|
||||
@ -89,12 +89,12 @@ async fn mock_nested_document_view_publish_payload(
|
||||
let child_publish_name = generate_publish_name(&child_view.id, &child_view.name);
|
||||
|
||||
let data = match view_encoded_collab {
|
||||
EncodedCollabWrapper::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(),
|
||||
EncodedCollabType::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(),
|
||||
_ => panic!("Expected document collab"),
|
||||
};
|
||||
|
||||
let child_data = match child_view_encoded_collab {
|
||||
EncodedCollabWrapper::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(),
|
||||
EncodedCollabType::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(),
|
||||
_ => panic!("Expected document collab"),
|
||||
};
|
||||
|
||||
|
@ -5,8 +5,8 @@ pub use client_api::entity::ai_dto::{
|
||||
};
|
||||
pub use client_api::entity::billing_dto::SubscriptionPlan;
|
||||
pub use client_api::entity::chat_dto::{
|
||||
ChatMessage, ChatMessageMetadata, ChatMessageType, ChatRAGData, ContextLoader, MessageCursor,
|
||||
RepeatedChatMessage,
|
||||
ChatMessage, ChatMessageMetadata, ChatMessageType, ChatRAGData, ChatSettings, ContextLoader,
|
||||
MessageCursor, RepeatedChatMessage, UpdateChatParams,
|
||||
};
|
||||
pub use client_api::entity::QuestionStreamValue;
|
||||
use client_api::error::AppResponseError;
|
||||
@ -27,6 +27,7 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
||||
uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
) -> Result<(), FlowyError>;
|
||||
|
||||
async fn create_question(
|
||||
@ -97,4 +98,17 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
) -> Result<Vec<SubscriptionPlan>, FlowyError>;
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<ChatSettings, FlowyError>;
|
||||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError>;
|
||||
}
|
||||
|
@ -20,21 +20,4 @@ fn main() {
|
||||
flowy_codegen::Project::TauriApp,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "web_ts")]
|
||||
{
|
||||
flowy_codegen::ts_event::gen(
|
||||
"folder",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
flowy_codegen::protobuf_file::ts_gen(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"folder",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,15 @@ use crate::persistence::{insert_chat, read_chat_metadata, ChatTable};
|
||||
|
||||
use appflowy_plugin::manager::PluginManager;
|
||||
use dashmap::DashMap;
|
||||
use flowy_ai_pub::cloud::{ChatCloudService, ChatMessageMetadata, ChatMessageType};
|
||||
use flowy_ai_pub::cloud::{
|
||||
ChatCloudService, ChatMessageMetadata, ChatMessageType, UpdateChatParams,
|
||||
};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_sqlite::kv::KVStorePreferences;
|
||||
use flowy_sqlite::DBConnection;
|
||||
|
||||
use flowy_storage_pub::storage::StorageService;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::util::timestamp;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Weak};
|
||||
@ -27,9 +30,19 @@ pub trait AIUserService: Send + Sync + 'static {
|
||||
fn application_root_dir(&self) -> Result<PathBuf, FlowyError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait AIQueryService: Send + Sync + 'static {
|
||||
async fn query_chat_rag_ids(
|
||||
&self,
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<Vec<String>, FlowyError>;
|
||||
}
|
||||
|
||||
pub struct AIManager {
|
||||
pub cloud_service_wm: Arc<AICloudServiceMiddleware>,
|
||||
pub user_service: Arc<dyn AIUserService>,
|
||||
pub query_service: Arc<dyn AIQueryService>,
|
||||
chats: Arc<DashMap<String, Arc<Chat>>>,
|
||||
pub local_ai_controller: Arc<LocalAIController>,
|
||||
}
|
||||
@ -40,6 +53,7 @@ impl AIManager {
|
||||
user_service: impl AIUserService,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
storage_service: Weak<dyn StorageService>,
|
||||
query_service: impl AIQueryService,
|
||||
) -> AIManager {
|
||||
let user_service = Arc::new(user_service);
|
||||
let plugin_manager = Arc::new(PluginManager::new());
|
||||
@ -49,6 +63,7 @@ impl AIManager {
|
||||
user_service.clone(),
|
||||
chat_cloud_service.clone(),
|
||||
));
|
||||
let query_service = Arc::new(query_service);
|
||||
|
||||
// setup local chat service
|
||||
let cloud_service_wm = Arc::new(AICloudServiceMiddleware::new(
|
||||
@ -63,6 +78,7 @@ impl AIManager {
|
||||
user_service,
|
||||
chats: Arc::new(DashMap::new()),
|
||||
local_ai_controller,
|
||||
query_service,
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +89,6 @@ impl AIManager {
|
||||
}
|
||||
|
||||
pub async fn open_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
|
||||
trace!("open chat: {}", chat_id);
|
||||
self.chats.entry(chat_id.to_string()).or_insert_with(|| {
|
||||
Arc::new(Chat::new(
|
||||
self.user_service.user_id().unwrap(),
|
||||
@ -126,11 +141,23 @@ impl AIManager {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_chat(&self, uid: &i64, chat_id: &str) -> Result<Arc<Chat>, FlowyError> {
|
||||
pub async fn create_chat(
|
||||
&self,
|
||||
uid: &i64,
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<Arc<Chat>, FlowyError> {
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let rag_ids = self
|
||||
.query_service
|
||||
.query_chat_rag_ids(parent_view_id, chat_id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
info!("[Chat] create chat with rag_ids: {:?}", rag_ids);
|
||||
|
||||
self
|
||||
.cloud_service_wm
|
||||
.create_chat(uid, &workspace_id, chat_id)
|
||||
.create_chat(uid, &workspace_id, chat_id, rag_ids)
|
||||
.await?;
|
||||
save_chat(self.user_service.sqlite_connection(*uid)?, chat_id)?;
|
||||
|
||||
@ -257,6 +284,22 @@ impl AIManager {
|
||||
}
|
||||
|
||||
pub fn local_ai_purchased(&self) {}
|
||||
|
||||
pub async fn update_rag_ids(&self, chat_id: &str, rag_ids: Vec<String>) -> FlowyResult<()> {
|
||||
if !rag_ids.is_empty() {
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let update_setting = UpdateChatParams {
|
||||
name: None,
|
||||
metadata: None,
|
||||
rag_ids: Some(rag_ids),
|
||||
};
|
||||
self
|
||||
.cloud_service_wm
|
||||
.update_chat_settings(&workspace_id, chat_id, update_setting)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn save_chat(conn: DBConnection, chat_id: &str) -> FlowyResult<()> {
|
||||
|
@ -3,7 +3,7 @@ use crate::entities::{
|
||||
ChatMessageErrorPB, ChatMessageListPB, ChatMessagePB, RepeatedRelatedQuestionPB,
|
||||
};
|
||||
use crate::middleware::chat_service_mw::AICloudServiceMiddleware;
|
||||
use crate::notification::{make_notification, ChatNotification};
|
||||
use crate::notification::{chat_notification_builder, ChatNotification};
|
||||
use crate::persistence::{insert_chat_messages, select_chat_messages, ChatMessageTable};
|
||||
use crate::stream_message::StreamMessage;
|
||||
use allo_isolate::Isolate;
|
||||
@ -86,10 +86,6 @@ impl Chat {
|
||||
question_stream_port: i64,
|
||||
metadata: Vec<ChatMessageMetadata>,
|
||||
) -> Result<ChatMessagePB, FlowyError> {
|
||||
if message.len() > 2000 {
|
||||
return Err(FlowyError::text_too_long().with_context("Exceeds maximum message 2000 length"));
|
||||
}
|
||||
|
||||
trace!(
|
||||
"[Chat] stream chat message: chat_id={}, message={}, message_type={:?}, metadata={:?}",
|
||||
self.chat_id,
|
||||
@ -181,7 +177,7 @@ impl Chat {
|
||||
chat_id: chat_id.clone(),
|
||||
error_message: err.to_string(),
|
||||
};
|
||||
make_notification(&chat_id, ChatNotification::StreamChatMessageError)
|
||||
chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError)
|
||||
.payload(pb)
|
||||
.send();
|
||||
return Err(err);
|
||||
@ -201,14 +197,14 @@ impl Chat {
|
||||
chat_id: chat_id.clone(),
|
||||
error_message: err.to_string(),
|
||||
};
|
||||
make_notification(&chat_id, ChatNotification::StreamChatMessageError)
|
||||
chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError)
|
||||
.payload(pb)
|
||||
.send();
|
||||
return Err(err);
|
||||
},
|
||||
}
|
||||
|
||||
make_notification(&chat_id, ChatNotification::FinishStreaming).send();
|
||||
chat_notification_builder(&chat_id, ChatNotification::FinishStreaming).send();
|
||||
if answer_stream_buffer.lock().await.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@ -260,7 +256,7 @@ impl Chat {
|
||||
has_more: true,
|
||||
total: 0,
|
||||
};
|
||||
make_notification(&self.chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
chat_notification_builder(&self.chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
.payload(pb.clone())
|
||||
.send();
|
||||
return Ok(pb);
|
||||
@ -381,11 +377,11 @@ impl Chat {
|
||||
} else {
|
||||
*prev_message_state.write().await = PrevMessageState::NoMore;
|
||||
}
|
||||
make_notification(&chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
chat_notification_builder(&chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
.payload(pb)
|
||||
.send();
|
||||
} else {
|
||||
make_notification(&chat_id, ChatNotification::DidLoadLatestChatMessage)
|
||||
chat_notification_builder(&chat_id, ChatNotification::DidLoadLatestChatMessage)
|
||||
.payload(pb)
|
||||
.send();
|
||||
}
|
||||
@ -571,7 +567,7 @@ pub(crate) fn save_and_notify_message(
|
||||
vec![message.clone()],
|
||||
)?;
|
||||
let pb = ChatMessagePB::from(message);
|
||||
make_notification(chat_id, ChatNotification::DidReceiveChatMessage)
|
||||
chat_notification_builder(chat_id, ChatNotification::DidReceiveChatMessage)
|
||||
.payload(pb)
|
||||
.send();
|
||||
|
||||
|
@ -83,16 +83,16 @@ pub struct ChatMessageMetaPB {
|
||||
pub data: String,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub data_type: ChatMessageMetaTypePB,
|
||||
pub loader_type: ContextLoaderTypePB,
|
||||
|
||||
#[pb(index = 5)]
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, ProtoBuf_Enum, PartialEq, Eq, Copy)]
|
||||
pub enum ChatMessageMetaTypePB {
|
||||
pub enum ContextLoaderTypePB {
|
||||
#[default]
|
||||
UnknownMetaType = 0,
|
||||
UnknownLoaderType = 0,
|
||||
Txt = 1,
|
||||
Markdown = 2,
|
||||
PDF = 3,
|
||||
|
@ -5,7 +5,9 @@ use crate::ai_manager::AIManager;
|
||||
use crate::completion::AICompletion;
|
||||
use crate::entities::*;
|
||||
use crate::local_ai::local_llm_chat::LLMModelInfo;
|
||||
use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY};
|
||||
use crate::notification::{
|
||||
chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
};
|
||||
use allo_isolate::Isolate;
|
||||
use flowy_ai_pub::cloud::{ChatMessageMetadata, ChatMessageType, ChatRAGData, ContextLoader};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
@ -34,15 +36,16 @@ pub(crate) async fn stream_chat_message_handler(
|
||||
ChatMessageTypePB::System => ChatMessageType::System,
|
||||
ChatMessageTypePB::User => ChatMessageType::User,
|
||||
};
|
||||
|
||||
let metadata = data
|
||||
.metadata
|
||||
.into_iter()
|
||||
.map(|metadata| {
|
||||
let (content_type, content_len) = match metadata.data_type {
|
||||
ChatMessageMetaTypePB::Txt => (ContextLoader::Text, metadata.data.len()),
|
||||
ChatMessageMetaTypePB::Markdown => (ContextLoader::Markdown, metadata.data.len()),
|
||||
ChatMessageMetaTypePB::PDF => (ContextLoader::PDF, 0),
|
||||
ChatMessageMetaTypePB::UnknownMetaType => (ContextLoader::Unknown, 0),
|
||||
let (content_type, content_len) = match metadata.loader_type {
|
||||
ContextLoaderTypePB::Txt => (ContextLoader::Text, metadata.data.len()),
|
||||
ContextLoaderTypePB::Markdown => (ContextLoader::Markdown, metadata.data.len()),
|
||||
ContextLoaderTypePB::PDF => (ContextLoader::PDF, 0),
|
||||
ContextLoaderTypePB::UnknownLoaderType => (ContextLoader::Unknown, 0),
|
||||
};
|
||||
|
||||
ChatMessageMetadata {
|
||||
@ -298,7 +301,7 @@ pub(crate) async fn toggle_local_ai_chat_handler(
|
||||
file_enabled,
|
||||
plugin_state,
|
||||
};
|
||||
make_notification(
|
||||
chat_notification_builder(
|
||||
APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
ChatNotification::UpdateLocalChatAI,
|
||||
)
|
||||
@ -323,7 +326,7 @@ pub(crate) async fn toggle_local_ai_chat_file_handler(
|
||||
file_enabled,
|
||||
plugin_state,
|
||||
};
|
||||
make_notification(
|
||||
chat_notification_builder(
|
||||
APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
ChatNotification::UpdateLocalChatAI,
|
||||
)
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::ai_manager::AIUserService;
|
||||
use crate::entities::{LocalAIPluginStatePB, LocalModelResourcePB, RunningStatePB};
|
||||
use crate::local_ai::local_llm_resource::{LLMResourceService, LocalAIResourceController};
|
||||
use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY};
|
||||
use crate::notification::{
|
||||
chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
};
|
||||
use anyhow::Error;
|
||||
use appflowy_local_ai::chat_plugin::{AIPluginConfig, AppFlowyLocalAI};
|
||||
use appflowy_plugin::manager::PluginManager;
|
||||
@ -90,7 +92,7 @@ impl LocalAIController {
|
||||
info!("[AI Plugin] state: {:?}", state);
|
||||
let offline_ai_ready = cloned_llm_res.is_offline_app_ready();
|
||||
let new_state = RunningStatePB::from(state);
|
||||
make_notification(
|
||||
chat_notification_builder(
|
||||
APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
ChatNotification::UpdateChatPluginState,
|
||||
)
|
||||
@ -590,12 +592,6 @@ impl LLMResourceService for LLMResourceServiceImpl {
|
||||
.store_preferences
|
||||
.get_object::<LLMSetting>(LOCAL_AI_SETTING_KEY)
|
||||
}
|
||||
|
||||
fn is_rag_enabled(&self) -> bool {
|
||||
self
|
||||
.store_preferences
|
||||
.get_bool_or_default(APPFLOWY_LOCAL_AI_CHAT_RAG_ENABLED)
|
||||
}
|
||||
}
|
||||
|
||||
fn local_ai_enabled_key(workspace_id: &str) -> String {
|
||||
|
@ -28,7 +28,6 @@ pub trait LLMResourceService: Send + Sync + 'static {
|
||||
async fn fetch_local_ai_config(&self) -> Result<LocalAIConfig, anyhow::Error>;
|
||||
fn store_setting(&self, setting: LLMSetting) -> Result<(), anyhow::Error>;
|
||||
fn retrieve_setting(&self) -> Option<LLMSetting>;
|
||||
fn is_rag_enabled(&self) -> bool;
|
||||
}
|
||||
|
||||
const LLM_MODEL_DIR: &str = "models";
|
||||
|
@ -1,15 +1,17 @@
|
||||
use crate::ai_manager::AIUserService;
|
||||
use crate::entities::{ChatStatePB, ModelTypePB};
|
||||
use crate::local_ai::local_llm_chat::LocalAIController;
|
||||
use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY};
|
||||
use crate::notification::{
|
||||
chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
};
|
||||
use crate::persistence::{select_single_message, ChatMessageTable};
|
||||
use appflowy_plugin::error::PluginError;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use flowy_ai_pub::cloud::{
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, CompletionType,
|
||||
LocalAIConfig, MessageCursor, RelatedQuestion, RepeatedChatMessage, RepeatedRelatedQuestion,
|
||||
StreamAnswer, StreamComplete, SubscriptionPlan,
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings,
|
||||
CompletionType, LocalAIConfig, MessageCursor, RelatedQuestion, RepeatedChatMessage,
|
||||
RepeatedRelatedQuestion, StreamAnswer, StreamComplete, SubscriptionPlan, UpdateChatParams,
|
||||
};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use futures::{stream, Sink, StreamExt, TryStreamExt};
|
||||
@ -92,7 +94,7 @@ impl AICloudServiceMiddleware {
|
||||
err,
|
||||
PluginError::PluginNotConnected | PluginError::PeerDisconnect
|
||||
) {
|
||||
make_notification(
|
||||
chat_notification_builder(
|
||||
APPFLOWY_AI_NOTIFICATION_KEY,
|
||||
ChatNotification::UpdateChatPluginState,
|
||||
)
|
||||
@ -112,10 +114,11 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
||||
uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
.create_chat(uid, workspace_id, chat_id)
|
||||
.create_chat(uid, workspace_id, chat_id, rag_ids)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -314,4 +317,27 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
||||
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
|
||||
self.cloud_service.get_workspace_plan(workspace_id).await
|
||||
}
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<ChatSettings, FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
.get_chat_settings(workspace_id, chat_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
.update_chat_settings(workspace_id, chat_id, params)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,6 @@ impl std::convert::From<i32> for ChatNotification {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub(crate) fn make_notification(id: &str, ty: ChatNotification) -> NotificationBuilder {
|
||||
pub(crate) fn chat_notification_builder(id: &str, ty: ChatNotification) -> NotificationBuilder {
|
||||
NotificationBuilder::new(id, ty, CHAT_OBSERVABLE_SOURCE)
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
use flowy_ai::ai_manager::{AIManager, AIUserService};
|
||||
use flowy_ai::ai_manager::{AIManager, AIQueryService, AIUserService};
|
||||
use flowy_ai_pub::cloud::ChatCloudService;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_folder_pub::query::FolderQueryService;
|
||||
use flowy_sqlite::kv::KVStorePreferences;
|
||||
use flowy_sqlite::DBConnection;
|
||||
use flowy_storage_pub::storage::StorageService;
|
||||
use flowy_user::services::authenticate_user::AuthenticateUser;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
@ -16,6 +18,7 @@ impl ChatDepsResolver {
|
||||
cloud_service: Arc<dyn ChatCloudService>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
storage_service: Weak<dyn StorageService>,
|
||||
folder_query: impl FolderQueryService,
|
||||
) -> Arc<AIManager> {
|
||||
let user_service = ChatUserServiceImpl(authenticate_user);
|
||||
Arc::new(AIManager::new(
|
||||
@ -23,10 +26,34 @@ impl ChatDepsResolver {
|
||||
user_service,
|
||||
store_preferences,
|
||||
storage_service,
|
||||
ChatQueryServiceImpl {
|
||||
folder_query: Box::new(folder_query),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatQueryServiceImpl {
|
||||
folder_query: Box<dyn FolderQueryService>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AIQueryService for ChatQueryServiceImpl {
|
||||
async fn query_chat_rag_ids(
|
||||
&self,
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<Vec<String>, FlowyError> {
|
||||
let mut ids = self.folder_query.get_sibling_ids(parent_view_id).await;
|
||||
|
||||
if !ids.is_empty() {
|
||||
ids.retain(|id| id != chat_id);
|
||||
}
|
||||
|
||||
Ok(ids)
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatUserServiceImpl(Weak<AuthenticateUser>);
|
||||
impl ChatUserServiceImpl {
|
||||
fn upgrade_user(&self) -> Result<Arc<AuthenticateUser>, FlowyError> {
|
||||
|
@ -17,8 +17,8 @@ use flowy_folder::entities::{CreateViewParams, ViewLayoutPB};
|
||||
use flowy_folder::manager::{FolderManager, FolderUser};
|
||||
use flowy_folder::share::ImportType;
|
||||
use flowy_folder::view_operation::{
|
||||
DatabaseEncodedCollab, DocumentEncodedCollab, EncodedCollabWrapper, FolderOperationHandler,
|
||||
FolderOperationHandlers, ImportedData, View, ViewData,
|
||||
DatabaseEncodedCollab, DocumentEncodedCollab, EncodedCollabType, FolderOperationHandler,
|
||||
ImportedData, View, ViewData,
|
||||
};
|
||||
use flowy_folder::ViewLayout;
|
||||
use flowy_search::folder::indexer::FolderIndexManagerImpl;
|
||||
@ -34,6 +34,7 @@ use tokio::sync::RwLock;
|
||||
use crate::integrate::server::ServerProvider;
|
||||
|
||||
use collab_plugins::local_storage::kv::KVTransactionDB;
|
||||
use flowy_folder_pub::query::FolderQueryService;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
|
||||
pub struct FolderDepsResolver();
|
||||
@ -45,7 +46,6 @@ impl FolderDepsResolver {
|
||||
server_provider: Arc<ServerProvider>,
|
||||
folder_indexer: Arc<FolderIndexManagerImpl>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
operation_handlers: FolderOperationHandlers,
|
||||
) -> Arc<FolderManager> {
|
||||
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl {
|
||||
authenticate_user: authenticate_user.clone(),
|
||||
@ -55,7 +55,6 @@ impl FolderDepsResolver {
|
||||
FolderManager::new(
|
||||
user.clone(),
|
||||
collab_builder,
|
||||
operation_handlers,
|
||||
server_provider.clone(),
|
||||
folder_indexer,
|
||||
store_preferences,
|
||||
@ -65,23 +64,21 @@ impl FolderDepsResolver {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folder_operation_handlers(
|
||||
pub fn register_handlers(
|
||||
folder_manager: &Arc<FolderManager>,
|
||||
document_manager: Arc<DocumentManager>,
|
||||
database_manager: Arc<DatabaseManager>,
|
||||
chat_manager: Arc<AIManager>,
|
||||
) -> FolderOperationHandlers {
|
||||
let mut map: HashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>> = HashMap::new();
|
||||
|
||||
) {
|
||||
let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager));
|
||||
map.insert(ViewLayout::Document, document_folder_operation);
|
||||
folder_manager.register_operation_handler(ViewLayout::Document, document_folder_operation);
|
||||
|
||||
let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager));
|
||||
let chat_folder_operation = Arc::new(ChatFolderOperation(chat_manager));
|
||||
map.insert(ViewLayout::Board, database_folder_operation.clone());
|
||||
map.insert(ViewLayout::Grid, database_folder_operation.clone());
|
||||
map.insert(ViewLayout::Calendar, database_folder_operation);
|
||||
map.insert(ViewLayout::Chat, chat_folder_operation);
|
||||
Arc::new(map)
|
||||
folder_manager.register_operation_handler(ViewLayout::Board, database_folder_operation.clone());
|
||||
folder_manager.register_operation_handler(ViewLayout::Grid, database_folder_operation.clone());
|
||||
folder_manager.register_operation_handler(ViewLayout::Calendar, database_folder_operation);
|
||||
folder_manager.register_operation_handler(ViewLayout::Chat, chat_folder_operation);
|
||||
}
|
||||
|
||||
struct FolderUserImpl {
|
||||
@ -190,11 +187,33 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
||||
Ok(Some(encoded_collab))
|
||||
}
|
||||
|
||||
/// Create a view with built-in data.
|
||||
async fn create_default_view(
|
||||
&self,
|
||||
user_id: i64,
|
||||
_parent_view_id: &str,
|
||||
view_id: &str,
|
||||
_name: &str,
|
||||
layout: ViewLayout,
|
||||
) -> Result<(), FlowyError> {
|
||||
debug_assert_eq!(layout, ViewLayout::Document);
|
||||
match self.0.create_document(user_id, view_id, None).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
if err.is_already_exists() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_encoded_collab_v1_from_disk(
|
||||
&self,
|
||||
user: Arc<dyn FolderUser>,
|
||||
user: &Arc<dyn FolderUser>,
|
||||
view_id: &str,
|
||||
) -> Result<EncodedCollabWrapper, FlowyError> {
|
||||
) -> Result<EncodedCollabType, FlowyError> {
|
||||
// get the collab_object_id for the document.
|
||||
// the collab_object_id for the document is the view_id.
|
||||
let workspace_id = user.workspace_id()?;
|
||||
@ -226,32 +245,11 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
||||
FlowyError::internal().with_context(format!("encode document collab failed: {}", e))
|
||||
})?;
|
||||
|
||||
Ok(EncodedCollabWrapper::Document(DocumentEncodedCollab {
|
||||
Ok(EncodedCollabType::Document(DocumentEncodedCollab {
|
||||
document_encoded_collab: encoded_collab,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create a view with built-in data.
|
||||
async fn create_view_with_default_data(
|
||||
&self,
|
||||
user_id: i64,
|
||||
view_id: &str,
|
||||
_name: &str,
|
||||
layout: ViewLayout,
|
||||
) -> Result<(), FlowyError> {
|
||||
debug_assert_eq!(layout, ViewLayout::Document);
|
||||
match self.0.create_document(user_id, view_id, None).await {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
if err.is_already_exists() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn import_from_bytes(
|
||||
&self,
|
||||
uid: i64,
|
||||
@ -272,13 +270,13 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
||||
)])
|
||||
}
|
||||
|
||||
// will implement soon
|
||||
async fn import_from_file_path(
|
||||
&self,
|
||||
_view_id: &str,
|
||||
_name: &str,
|
||||
_path: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
// TODO(lucas): import file from local markdown file
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -311,9 +309,9 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
|
||||
async fn get_encoded_collab_v1_from_disk(
|
||||
&self,
|
||||
user: Arc<dyn FolderUser>,
|
||||
user: &Arc<dyn FolderUser>,
|
||||
view_id: &str,
|
||||
) -> Result<EncodedCollabWrapper, FlowyError> {
|
||||
) -> Result<EncodedCollabType, FlowyError> {
|
||||
let workspace_id = user.workspace_id()?;
|
||||
// get the collab_object_id for the database.
|
||||
//
|
||||
@ -405,7 +403,7 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, FlowyError>>()?;
|
||||
|
||||
Ok(EncodedCollabWrapper::Database(DatabaseEncodedCollab {
|
||||
Ok(EncodedCollabType::Database(DatabaseEncodedCollab {
|
||||
database_encoded_collab,
|
||||
database_row_encoded_collabs,
|
||||
database_row_document_encoded_collabs,
|
||||
@ -478,9 +476,10 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
/// If the ext contains the {"database_id": "xx"}, then it will link to
|
||||
/// the existing database. The data of the database will be shared within
|
||||
/// these references views.
|
||||
async fn create_view_with_default_data(
|
||||
async fn create_default_view(
|
||||
&self,
|
||||
_user_id: i64,
|
||||
_parent_view_id: &str,
|
||||
view_id: &str,
|
||||
name: &str,
|
||||
layout: ViewLayout,
|
||||
@ -551,7 +550,10 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
_name: &str,
|
||||
path: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
self.0.import_csv_from_file(path, CSVFormat::META).await?;
|
||||
self
|
||||
.0
|
||||
.import_csv_from_file(path, CSVFormat::Original)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -625,14 +627,18 @@ impl FolderOperationHandler for ChatFolderOperation {
|
||||
Err(FlowyError::not_support())
|
||||
}
|
||||
|
||||
async fn create_view_with_default_data(
|
||||
async fn create_default_view(
|
||||
&self,
|
||||
user_id: i64,
|
||||
parent_view_id: &str,
|
||||
view_id: &str,
|
||||
_name: &str,
|
||||
_layout: ViewLayout,
|
||||
) -> Result<(), FlowyError> {
|
||||
self.0.create_chat(&user_id, view_id).await?;
|
||||
self
|
||||
.0
|
||||
.create_chat(&user_id, parent_view_id, view_id)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -656,3 +662,36 @@ impl FolderOperationHandler for ChatFolderOperation {
|
||||
Err(FlowyError::not_support())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FolderQueryServiceImpl {
|
||||
folder_manager: Weak<FolderManager>,
|
||||
}
|
||||
|
||||
impl FolderQueryServiceImpl {
|
||||
pub fn new(folder_manager: Weak<FolderManager>) -> Self {
|
||||
Self { folder_manager }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl FolderQueryService for FolderQueryServiceImpl {
|
||||
async fn get_sibling_ids(&self, parent_view_id: &str) -> Vec<String> {
|
||||
match self.folder_manager.upgrade() {
|
||||
None => vec![],
|
||||
Some(folder_manager) => {
|
||||
if let Ok(parent_view) = folder_manager.get_view(parent_view_id).await {
|
||||
if parent_view.space_info().is_none() {
|
||||
if let Ok(views) = folder_manager.get_views_belong_to(parent_view_id).await {
|
||||
return views
|
||||
.into_iter()
|
||||
.map(|child| child.id.clone())
|
||||
.collect::<Vec<String>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
vec![]
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,9 @@ use collab_integrate::collab_builder::{
|
||||
CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType,
|
||||
};
|
||||
use flowy_ai_pub::cloud::{
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, LocalAIConfig,
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, LocalAIConfig,
|
||||
MessageCursor, RepeatedChatMessage, StreamAnswer, StreamComplete, SubscriptionPlan,
|
||||
UpdateChatParams,
|
||||
};
|
||||
use flowy_database_pub::cloud::{
|
||||
DatabaseAIService, DatabaseCloudService, DatabaseSnapshot, EncodeCollabByOid, SummaryRowContent,
|
||||
@ -643,11 +644,12 @@ impl ChatCloudService for ServerProvider {
|
||||
uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server();
|
||||
server?
|
||||
.chat_service()
|
||||
.create_chat(uid, workspace_id, chat_id)
|
||||
.create_chat(uid, workspace_id, chat_id, rag_ids)
|
||||
.await
|
||||
}
|
||||
|
||||
@ -786,6 +788,31 @@ impl ChatCloudService for ServerProvider {
|
||||
.get_workspace_plan(workspace_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<ChatSettings, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.chat_service()
|
||||
.get_chat_settings(workspace_id, chat_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.chat_service()
|
||||
.update_chat_settings(workspace_id, chat_id, params)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -161,11 +161,27 @@ impl AppFlowyCore {
|
||||
collab_builder
|
||||
.set_snapshot_persistence(Arc::new(SnapshotDBImpl(Arc::downgrade(&authenticate_user))));
|
||||
|
||||
let folder_indexer = Arc::new(FolderIndexManagerImpl::new(Some(Arc::downgrade(
|
||||
&authenticate_user,
|
||||
))));
|
||||
|
||||
let folder_manager = FolderDepsResolver::resolve(
|
||||
Arc::downgrade(&authenticate_user),
|
||||
collab_builder.clone(),
|
||||
server_provider.clone(),
|
||||
folder_indexer.clone(),
|
||||
store_preference.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let folder_query_service = FolderQueryServiceImpl::new(Arc::downgrade(&folder_manager));
|
||||
|
||||
let ai_manager = ChatDepsResolver::resolve(
|
||||
Arc::downgrade(&authenticate_user),
|
||||
server_provider.clone(),
|
||||
store_preference.clone(),
|
||||
Arc::downgrade(&storage_manager.storage_service),
|
||||
folder_query_service.clone(),
|
||||
);
|
||||
|
||||
let database_manager = DatabaseDepsResolver::resolve(
|
||||
@ -186,26 +202,6 @@ impl AppFlowyCore {
|
||||
Arc::downgrade(&storage_manager.storage_service),
|
||||
);
|
||||
|
||||
let folder_indexer = Arc::new(FolderIndexManagerImpl::new(Some(Arc::downgrade(
|
||||
&authenticate_user,
|
||||
))));
|
||||
|
||||
let folder_operation_handlers = folder_operation_handlers(
|
||||
document_manager.clone(),
|
||||
database_manager.clone(),
|
||||
ai_manager.clone(),
|
||||
);
|
||||
|
||||
let folder_manager = FolderDepsResolver::resolve(
|
||||
Arc::downgrade(&authenticate_user),
|
||||
collab_builder.clone(),
|
||||
server_provider.clone(),
|
||||
folder_indexer.clone(),
|
||||
store_preference.clone(),
|
||||
folder_operation_handlers,
|
||||
)
|
||||
.await;
|
||||
|
||||
let user_manager = UserDepsResolver::resolve(
|
||||
authenticate_user.clone(),
|
||||
collab_builder.clone(),
|
||||
@ -223,6 +219,14 @@ impl AppFlowyCore {
|
||||
)
|
||||
.await;
|
||||
|
||||
// Register the folder operation handlers
|
||||
register_handlers(
|
||||
&folder_manager,
|
||||
document_manager.clone(),
|
||||
database_manager.clone(),
|
||||
ai_manager.clone(),
|
||||
);
|
||||
|
||||
(
|
||||
user_manager,
|
||||
folder_manager,
|
||||
|
@ -29,8 +29,8 @@ pub struct DateFilterContent {
|
||||
pub timestamp: Option<i64>,
|
||||
}
|
||||
|
||||
impl ToString for DateFilterContent {
|
||||
fn to_string(&self) -> String {
|
||||
impl DateFilterContent {
|
||||
pub fn to_json_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
use crate::services::field::{CHECK, UNCHECK};
|
||||
use collab_database::fields::checkbox_type_option::CheckboxTypeOption;
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_derive::ProtoBuf;
|
||||
|
||||
#[derive(Default, Debug, Clone, ProtoBuf)]
|
||||
@ -13,6 +15,16 @@ impl CheckboxCellDataPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCellString for CheckboxCellDataPB {
|
||||
fn to_cell_string(&self) -> String {
|
||||
if self.is_checked {
|
||||
CHECK.to_string()
|
||||
} else {
|
||||
UNCHECK.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||
pub struct CheckboxTypeOptionPB {
|
||||
/// unused
|
||||
|
@ -6,7 +6,7 @@ use tokio::sync::oneshot;
|
||||
use tracing::{info, instrument};
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use lib_dispatch::prelude::{af_spawn, data_result_ok, AFPluginData, AFPluginState, DataResult};
|
||||
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
|
||||
|
||||
use crate::entities::*;
|
||||
use crate::manager::DatabaseManager;
|
||||
@ -1260,7 +1260,7 @@ pub(crate) async fn summarize_row_handler(
|
||||
let data = data.into_inner();
|
||||
let row_id = RowId::from(data.row_id);
|
||||
let (tx, rx) = oneshot::channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
let result = manager
|
||||
.summarize_row(data.view_id, row_id, data.field_id)
|
||||
.await;
|
||||
@ -1279,7 +1279,7 @@ pub(crate) async fn translate_row_handler(
|
||||
let data = data.try_into_inner()?;
|
||||
let row_id = RowId::from(data.row_id);
|
||||
let (tx, rx) = oneshot::channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
let result = manager
|
||||
.translate_row(data.view_id, row_id, data.field_id)
|
||||
.await;
|
||||
|
@ -6,7 +6,7 @@ use collab::core::origin::CollabOrigin;
|
||||
use collab::lock::RwLock;
|
||||
use collab::preclude::Collab;
|
||||
use collab_database::database::{Database, DatabaseData};
|
||||
use collab_database::entity::{CreateDatabaseParams, CreateViewParams};
|
||||
use collab_database::entity::{CreateDatabaseParams, CreateViewParams, EncodedDatabase};
|
||||
use collab_database::error::DatabaseError;
|
||||
use collab_database::fields::translate_type_option::TranslateTypeOption;
|
||||
use collab_database::rows::RowId;
|
||||
@ -31,7 +31,7 @@ use flowy_database_pub::cloud::{
|
||||
DatabaseAIService, DatabaseCloudService, SummaryRowContent, TranslateItem, TranslateRowContent,
|
||||
};
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::priority_task::TaskDispatcher;
|
||||
|
||||
@ -189,6 +189,17 @@ impl DatabaseManager {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn encode_database(&self, view_id: &str) -> FlowyResult<EncodedDatabase> {
|
||||
let editor = self.get_database_editor_with_view_id(view_id).await?;
|
||||
let collabs = editor
|
||||
.database
|
||||
.read()
|
||||
.await
|
||||
.encode_database_collabs()
|
||||
.await?;
|
||||
Ok(collabs)
|
||||
}
|
||||
|
||||
pub async fn get_database_row_ids_with_view_id(&self, view_id: &str) -> FlowyResult<Vec<RowId>> {
|
||||
let database = self.get_database_editor_with_view_id(view_id).await?;
|
||||
Ok(database.get_row_ids().await)
|
||||
@ -316,7 +327,7 @@ impl DatabaseManager {
|
||||
|
||||
let weak_workspace_database = Arc::downgrade(&self.workspace_database()?);
|
||||
let weak_removing_editors = Arc::downgrade(&self.removing_editor);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(120)).await;
|
||||
if let Some(removing_editors) = weak_removing_editors.upgrade() {
|
||||
if removing_editors.lock().await.remove(&database_id).is_some() {
|
||||
@ -891,6 +902,7 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl {
|
||||
"[Database]: load {} database row from local disk",
|
||||
local_disk_encoded_collab.len()
|
||||
);
|
||||
|
||||
object_ids.retain(|object_id| !local_disk_encoded_collab.contains_key(object_id));
|
||||
for (k, v) in local_disk_encoded_collab {
|
||||
encoded_collab_by_id.insert(k, v);
|
||||
|
@ -90,7 +90,7 @@ impl std::convert::From<i32> for DatabaseNotification {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub fn send_notification(id: &str, ty: DatabaseNotification) -> NotificationBuilder {
|
||||
pub fn database_notification_builder(id: &str, ty: DatabaseNotification) -> NotificationBuilder {
|
||||
#[cfg(feature = "verbose_log")]
|
||||
tracing::trace!("[Database Notification]: id:{}, ty:{:?}", id, ty);
|
||||
|
||||
|
@ -86,7 +86,7 @@ impl CalculationsController {
|
||||
let task = Task::new(
|
||||
&self.handler_id,
|
||||
task_id,
|
||||
TaskContent::Text(task_type.to_string()),
|
||||
TaskContent::Text(task_type.to_json_string()),
|
||||
qos,
|
||||
);
|
||||
self.task_scheduler.write().await.add_task(task);
|
||||
@ -322,7 +322,7 @@ impl CalculationsController {
|
||||
) -> Vec<CalculationPB> {
|
||||
let mut updates = vec![];
|
||||
let update = self
|
||||
.update_calculation(calculation, &field, field_cells)
|
||||
.update_calculation(calculation, field, field_cells)
|
||||
.await;
|
||||
if let Some(update) = update {
|
||||
updates.push(CalculationPB::from(&update));
|
||||
@ -415,8 +415,8 @@ pub(crate) enum CalculationEvent {
|
||||
FieldDeleted(String),
|
||||
}
|
||||
|
||||
impl ToString for CalculationEvent {
|
||||
fn to_string(&self) -> String {
|
||||
impl CalculationEvent {
|
||||
fn to_json_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ impl<'a> CellBuilder<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_text_cell(&mut self, field_id: &str, data: String) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the text field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self
|
||||
@ -296,7 +296,7 @@ impl<'a> CellBuilder<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_url_cell(&mut self, field_id: &str, data: String) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the url field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self
|
||||
@ -307,7 +307,7 @@ impl<'a> CellBuilder<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_number_cell(&mut self, field_id: &str, num: i64) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the number field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self
|
||||
@ -318,7 +318,7 @@ impl<'a> CellBuilder<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_checkbox_cell(&mut self, field_id: &str, is_checked: bool) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the checkbox field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self
|
||||
@ -329,7 +329,7 @@ impl<'a> CellBuilder<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_date_cell(&mut self, field_id: &str, timestamp: i64, include_time: Option<bool>) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the date field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self.cells.insert(
|
||||
@ -341,7 +341,7 @@ impl<'a> CellBuilder<'a> {
|
||||
}
|
||||
|
||||
pub fn insert_select_option_cell(&mut self, field_id: &str, option_ids: Vec<String>) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the select option field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self.cells.insert(
|
||||
@ -356,7 +356,7 @@ impl<'a> CellBuilder<'a> {
|
||||
field_id: &str,
|
||||
new_tasks: Vec<ChecklistCellInsertChangeset>,
|
||||
) {
|
||||
match self.field_maps.get(&field_id.to_owned()) {
|
||||
match self.field_maps.get(field_id) {
|
||||
None => tracing::warn!("Can't find the field with id: {}", field_id),
|
||||
Some(field) => {
|
||||
self
|
||||
|
@ -1,4 +1,5 @@
|
||||
use bytes::Bytes;
|
||||
use std::fmt::Display;
|
||||
|
||||
use flowy_error::{internal_error, FlowyResult};
|
||||
|
||||
@ -64,15 +65,10 @@ impl CellProtobufBlob {
|
||||
// }
|
||||
}
|
||||
|
||||
impl ToString for CellProtobufBlob {
|
||||
fn to_string(&self) -> String {
|
||||
match String::from_utf8(self.0.to_vec()) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
tracing::error!("DecodedCellData to string failed: {:?}", e);
|
||||
"".to_string()
|
||||
},
|
||||
}
|
||||
impl Display for CellProtobufBlob {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = String::from_utf8(self.0.to_vec()).unwrap_or_default();
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::entities::*;
|
||||
use crate::notification::{send_notification, DatabaseNotification};
|
||||
use crate::notification::{database_notification_builder, DatabaseNotification};
|
||||
use crate::services::calculations::Calculation;
|
||||
use crate::services::cell::{apply_cell_changeset, get_cell_protobuf, CellCache};
|
||||
use crate::services::database::database_observe::*;
|
||||
@ -226,7 +226,7 @@ impl DatabaseEditor {
|
||||
let field_type = FieldType::from(field.field_type);
|
||||
setting_content = group_config_pb_to_json_str(data, &field_type)?;
|
||||
let mut group_setting = default_group_setting(&field);
|
||||
group_setting.content = setting_content.clone();
|
||||
group_setting.content.clone_from(&setting_content);
|
||||
database.update_database_view(view_id, |view| {
|
||||
view.set_groups(vec![group_setting.into()]);
|
||||
});
|
||||
@ -252,7 +252,7 @@ impl DatabaseEditor {
|
||||
let changes = view_editor.v_delete_group(¶ms.group_id).await?;
|
||||
if !changes.is_empty() {
|
||||
for view in self.database_views.editors().await {
|
||||
send_notification(&view.view_id, DatabaseNotification::DidUpdateRow)
|
||||
database_notification_builder(&view.view_id, DatabaseNotification::DidUpdateRow)
|
||||
.payload(changes.clone())
|
||||
.send();
|
||||
}
|
||||
@ -762,7 +762,7 @@ impl DatabaseEditor {
|
||||
updated_fields: vec![],
|
||||
};
|
||||
|
||||
send_notification(¶ms.view_id, DatabaseNotification::DidUpdateFields)
|
||||
database_notification_builder(¶ms.view_id, DatabaseNotification::DidUpdateFields)
|
||||
.payload(notified_changeset)
|
||||
.send();
|
||||
}
|
||||
@ -879,7 +879,7 @@ impl DatabaseEditor {
|
||||
}
|
||||
|
||||
// Notifies the client that the row meta has been updated.
|
||||
send_notification(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta)
|
||||
database_notification_builder(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta)
|
||||
.payload(RowMetaPB::from(row_detail))
|
||||
.send();
|
||||
}
|
||||
@ -1403,7 +1403,7 @@ impl DatabaseEditor {
|
||||
) -> FlowyResult<()> {
|
||||
let views = self.database.read().await.get_all_database_views_meta();
|
||||
for view in views {
|
||||
send_notification(&view.id, DatabaseNotification::DidUpdateFields)
|
||||
database_notification_builder(&view.id, DatabaseNotification::DidUpdateFields)
|
||||
.payload(changeset.clone())
|
||||
.send();
|
||||
}
|
||||
@ -2219,7 +2219,7 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl {
|
||||
new_field_settings.clone(),
|
||||
);
|
||||
|
||||
send_notification(
|
||||
database_notification_builder(
|
||||
¶ms.view_id,
|
||||
DatabaseNotification::DidUpdateFieldSettings,
|
||||
)
|
||||
@ -2290,12 +2290,12 @@ fn notify_did_update_database_field(database: &Database, field_id: &str) -> Flow
|
||||
DatabaseFieldChangesetPB::update(&database_id, vec![updated_field.clone()]);
|
||||
|
||||
for view in views {
|
||||
send_notification(&view.id, DatabaseNotification::DidUpdateFields)
|
||||
database_notification_builder(&view.id, DatabaseNotification::DidUpdateFields)
|
||||
.payload(notified_changeset.clone())
|
||||
.send();
|
||||
}
|
||||
|
||||
send_notification(field_id, DatabaseNotification::DidUpdateField)
|
||||
database_notification_builder(field_id, DatabaseNotification::DidUpdateField)
|
||||
.payload(updated_field)
|
||||
.send();
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::entities::{DatabaseSyncStatePB, DidFetchRowPB, RowsChangePB};
|
||||
use crate::notification::{send_notification, DatabaseNotification, DATABASE_OBSERVABLE_SOURCE};
|
||||
use crate::notification::{
|
||||
database_notification_builder, DatabaseNotification, DATABASE_OBSERVABLE_SOURCE,
|
||||
};
|
||||
use crate::services::database::{DatabaseEditor, UpdatedRow};
|
||||
use crate::services::database_view::DatabaseViewEditor;
|
||||
use collab::lock::RwLock;
|
||||
@ -11,7 +13,7 @@ use collab_database::views::{DatabaseViewChange, RowOrder};
|
||||
use dashmap::DashMap;
|
||||
use flowy_notification::{DebounceNotificationSender, NotificationBuilder};
|
||||
use futures::StreamExt;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tracing::{error, trace, warn};
|
||||
|
||||
@ -19,13 +21,13 @@ pub(crate) async fn observe_sync_state(database_id: &str, database: &Arc<RwLock<
|
||||
let weak_database = Arc::downgrade(database);
|
||||
let mut sync_state = database.read().await.subscribe_sync_state();
|
||||
let database_id = database_id.to_string();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Some(sync_state) = sync_state.next().await {
|
||||
if weak_database.upgrade().is_none() {
|
||||
break;
|
||||
}
|
||||
|
||||
send_notification(
|
||||
database_notification_builder(
|
||||
&database_id,
|
||||
DatabaseNotification::DidUpdateDatabaseSyncUpdate,
|
||||
)
|
||||
@ -45,7 +47,7 @@ pub(crate) async fn observe_rows_change(
|
||||
let weak_database = Arc::downgrade(database);
|
||||
let sub = database.read().await.subscribe_row_change();
|
||||
if let Some(mut row_change) = sub {
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(row_change) = row_change.recv().await {
|
||||
trace!(
|
||||
"[Database Observe]: {} row change:{:?}",
|
||||
@ -88,7 +90,7 @@ pub(crate) async fn observe_field_change(database_id: &str, database: &Arc<RwLoc
|
||||
let weak_database = Arc::downgrade(database);
|
||||
let sub = database.read().await.subscribe_field_change();
|
||||
if let Some(mut field_change) = sub {
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(field_change) = field_change.recv().await {
|
||||
if weak_database.upgrade().is_none() {
|
||||
break;
|
||||
@ -120,7 +122,7 @@ pub(crate) async fn observe_view_change(database_id: &str, database_editor: &Arc
|
||||
.subscribe_view_change();
|
||||
|
||||
if let Some(mut view_change) = view_change {
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(view_change) = view_change.recv().await {
|
||||
trace!(
|
||||
"[Database View Observe]: {} view change:{:?}",
|
||||
@ -281,7 +283,7 @@ async fn handle_did_update_row_orders(
|
||||
for entry in row_changes.into_iter() {
|
||||
let (view_id, changes) = entry;
|
||||
trace!("[RowOrder]: {}", changes);
|
||||
send_notification(&view_id, DatabaseNotification::DidUpdateRow)
|
||||
database_notification_builder(&view_id, DatabaseNotification::DidUpdateRow)
|
||||
.payload(changes)
|
||||
.send();
|
||||
}
|
||||
@ -295,7 +297,7 @@ pub(crate) async fn observe_block_event(database_id: &str, database_editor: &Arc
|
||||
.await
|
||||
.subscribe_block_event();
|
||||
let database_editor = Arc::downgrade(database_editor);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(event) = block_event_rx.recv().await {
|
||||
if database_editor.upgrade().is_none() {
|
||||
break;
|
||||
@ -312,7 +314,7 @@ pub(crate) async fn observe_block_event(database_id: &str, database_editor: &Arc
|
||||
trace!("Did fetch row: {:?}", row_detail.row.id);
|
||||
let row_id = row_detail.row.id.clone();
|
||||
let pb = DidFetchRowPB::from(row_detail);
|
||||
send_notification(&row_id, DatabaseNotification::DidFetchRow)
|
||||
database_notification_builder(&row_id, DatabaseNotification::DidFetchRow)
|
||||
.payload(pb)
|
||||
.send();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::entities::{
|
||||
GroupChangesPB, GroupRowsNotificationPB, ReorderAllRowsPB, ReorderSingleRowPB,
|
||||
RowsVisibilityChangePB, SortChangesetNotificationPB,
|
||||
};
|
||||
use crate::notification::{send_notification, DatabaseNotification};
|
||||
use crate::notification::{database_notification_builder, DatabaseNotification};
|
||||
use crate::services::filter::FilterResultNotification;
|
||||
use crate::services::sort::{ReorderAllRowsResult, ReorderSingleRowResult};
|
||||
use async_stream::stream;
|
||||
@ -50,7 +50,7 @@ impl DatabaseViewChangedReceiverRunner {
|
||||
.collect(),
|
||||
};
|
||||
|
||||
send_notification(
|
||||
database_notification_builder(
|
||||
&changeset.view_id,
|
||||
DatabaseNotification::DidUpdateViewRowsVisibility,
|
||||
)
|
||||
@ -61,9 +61,12 @@ impl DatabaseViewChangedReceiverRunner {
|
||||
let row_orders = ReorderAllRowsPB {
|
||||
row_orders: notification.row_orders,
|
||||
};
|
||||
send_notification(¬ification.view_id, DatabaseNotification::DidReorderRows)
|
||||
.payload(row_orders)
|
||||
.send()
|
||||
database_notification_builder(
|
||||
¬ification.view_id,
|
||||
DatabaseNotification::DidReorderRows,
|
||||
)
|
||||
.payload(row_orders)
|
||||
.send()
|
||||
},
|
||||
DatabaseViewChanged::ReorderSingleRowNotification(notification) => {
|
||||
let reorder_row = ReorderSingleRowPB {
|
||||
@ -71,19 +74,21 @@ impl DatabaseViewChangedReceiverRunner {
|
||||
old_index: notification.old_index as i32,
|
||||
new_index: notification.new_index as i32,
|
||||
};
|
||||
send_notification(
|
||||
database_notification_builder(
|
||||
¬ification.view_id,
|
||||
DatabaseNotification::DidReorderSingleRow,
|
||||
)
|
||||
.payload(reorder_row)
|
||||
.send()
|
||||
},
|
||||
DatabaseViewChanged::CalculationValueNotification(notification) => send_notification(
|
||||
¬ification.view_id,
|
||||
DatabaseNotification::DidUpdateCalculation,
|
||||
)
|
||||
.payload(notification)
|
||||
.send(),
|
||||
DatabaseViewChanged::CalculationValueNotification(notification) => {
|
||||
database_notification_builder(
|
||||
¬ification.view_id,
|
||||
DatabaseNotification::DidUpdateCalculation,
|
||||
)
|
||||
.payload(notification)
|
||||
.send()
|
||||
},
|
||||
}
|
||||
})
|
||||
.await;
|
||||
@ -91,19 +96,19 @@ impl DatabaseViewChangedReceiverRunner {
|
||||
}
|
||||
|
||||
pub async fn notify_did_update_group_rows(payload: GroupRowsNotificationPB) {
|
||||
send_notification(&payload.group_id, DatabaseNotification::DidUpdateGroupRow)
|
||||
database_notification_builder(&payload.group_id, DatabaseNotification::DidUpdateGroupRow)
|
||||
.payload(payload)
|
||||
.send();
|
||||
}
|
||||
|
||||
pub async fn notify_did_update_filter(notification: FilterChangesetNotificationPB) {
|
||||
send_notification(¬ification.view_id, DatabaseNotification::DidUpdateFilter)
|
||||
database_notification_builder(¬ification.view_id, DatabaseNotification::DidUpdateFilter)
|
||||
.payload(notification)
|
||||
.send();
|
||||
}
|
||||
|
||||
pub async fn notify_did_update_calculation(notification: CalculationChangesetNotificationPB) {
|
||||
send_notification(
|
||||
database_notification_builder(
|
||||
¬ification.view_id,
|
||||
DatabaseNotification::DidUpdateCalculation,
|
||||
)
|
||||
@ -113,20 +118,20 @@ pub async fn notify_did_update_calculation(notification: CalculationChangesetNot
|
||||
|
||||
pub async fn notify_did_update_sort(notification: SortChangesetNotificationPB) {
|
||||
if !notification.is_empty() {
|
||||
send_notification(¬ification.view_id, DatabaseNotification::DidUpdateSort)
|
||||
database_notification_builder(¬ification.view_id, DatabaseNotification::DidUpdateSort)
|
||||
.payload(notification)
|
||||
.send();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn notify_did_update_num_of_groups(view_id: &str, changeset: GroupChangesPB) {
|
||||
send_notification(view_id, DatabaseNotification::DidUpdateNumOfGroups)
|
||||
database_notification_builder(view_id, DatabaseNotification::DidUpdateNumOfGroups)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
||||
pub(crate) async fn notify_did_update_setting(view_id: &str, setting: DatabaseViewSettingPB) {
|
||||
send_notification(view_id, DatabaseNotification::DidUpdateSettings)
|
||||
database_notification_builder(view_id, DatabaseNotification::DidUpdateSettings)
|
||||
.payload(setting)
|
||||
.send();
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use crate::entities::{
|
||||
RemoveCalculationChangesetPB, ReorderSortPayloadPB, RowMetaPB, RowsChangePB,
|
||||
SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB,
|
||||
};
|
||||
use crate::notification::{send_notification, DatabaseNotification};
|
||||
use crate::notification::{database_notification_builder, DatabaseNotification};
|
||||
use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController};
|
||||
use crate::services::cell::{CellBuilder, CellCache};
|
||||
use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow};
|
||||
@ -233,7 +233,7 @@ impl DatabaseViewEditor {
|
||||
if rows.pop().is_some() {
|
||||
let update_row = UpdatedRow::new(row_id.as_str()).with_row_meta(row_detail.clone());
|
||||
let changeset = RowsChangePB::from_update(update_row.into());
|
||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateRow)
|
||||
database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateRow)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
@ -706,7 +706,7 @@ impl DatabaseViewEditor {
|
||||
}))
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(|result| result)
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
// Pre-compute cells by field ID only for fields that have calculations
|
||||
@ -940,7 +940,7 @@ impl DatabaseViewEditor {
|
||||
};
|
||||
|
||||
if let Some(payload) = layout_setting_pb {
|
||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateLayoutSettings)
|
||||
database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateLayoutSettings)
|
||||
.payload(payload)
|
||||
.send();
|
||||
}
|
||||
@ -1060,7 +1060,7 @@ impl DatabaseViewEditor {
|
||||
|
||||
debug_assert!(!changeset.is_empty());
|
||||
if !changeset.is_empty() {
|
||||
send_notification(&changeset.view_id, DatabaseNotification::DidGroupByField)
|
||||
database_notification_builder(&changeset.view_id, DatabaseNotification::DidGroupByField)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
@ -1196,7 +1196,7 @@ impl DatabaseViewEditor {
|
||||
view_id: self.view_id.clone(),
|
||||
layout: new_layout_type.into(),
|
||||
};
|
||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout)
|
||||
database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout)
|
||||
.payload(payload)
|
||||
.send();
|
||||
|
||||
@ -1214,7 +1214,7 @@ impl DatabaseViewEditor {
|
||||
} => RowsChangePB::from_move(vec![deleted_row_id.into_inner()], vec![inserted_row.into()]),
|
||||
};
|
||||
|
||||
send_notification(&self.view_id, DatabaseNotification::DidUpdateRow)
|
||||
database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateRow)
|
||||
.payload(changeset)
|
||||
.send();
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ impl FieldBuilder {
|
||||
}
|
||||
|
||||
pub fn name(mut self, name: &str) -> Self {
|
||||
self.field.name = name.to_owned();
|
||||
name.clone_into(&mut self.field.name);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ mod tests {
|
||||
use crate::services::field::type_options::checkbox_type_option::*;
|
||||
use crate::services::field::FieldBuilder;
|
||||
use collab_database::fields::checkbox_type_option::CheckboxTypeOption;
|
||||
use collab_database::template::util::ToCellString;
|
||||
|
||||
#[test]
|
||||
fn checkout_box_description_test() {
|
||||
@ -46,7 +47,7 @@ mod tests {
|
||||
type_option
|
||||
.decode_cell(&CheckboxCellDataPB::from_str(input_str).unwrap().into())
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
.to_cell_string(),
|
||||
expected_str.to_owned()
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::str::FromStr;
|
||||
use collab_database::fields::checkbox_type_option::CheckboxTypeOption;
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::rows::Cell;
|
||||
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
use crate::entities::{CheckboxCellDataPB, CheckboxFilterPB, FieldType};
|
||||
@ -48,7 +48,7 @@ impl CellDataDecoder for CheckboxTypeOption {
|
||||
}
|
||||
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
cell_data.to_string()
|
||||
cell_data.to_cell_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::str::FromStr;
|
||||
use bytes::Bytes;
|
||||
use collab::util::AnyMapExt;
|
||||
use collab_database::rows::{new_cell_builder, Cell};
|
||||
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
|
||||
use crate::entities::{CheckboxCellDataPB, FieldType};
|
||||
@ -29,7 +29,7 @@ impl From<&Cell> for CheckboxCellDataPB {
|
||||
impl From<CheckboxCellDataPB> for Cell {
|
||||
fn from(data: CheckboxCellDataPB) -> Self {
|
||||
let mut cell = new_cell_builder(FieldType::Checkbox);
|
||||
cell.insert(CELL_DATA.into(), data.to_string().into());
|
||||
cell.insert(CELL_DATA.into(), data.to_cell_string().into());
|
||||
cell
|
||||
}
|
||||
}
|
||||
@ -49,16 +49,6 @@ impl FromStr for CheckboxCellDataPB {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for CheckboxCellDataPB {
|
||||
fn to_string(&self) -> String {
|
||||
if self.is_checked {
|
||||
CHECK.to_string()
|
||||
} else {
|
||||
UNCHECK.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CheckboxCellDataParser();
|
||||
impl CellProtobufBlobParser for CheckboxCellDataParser {
|
||||
type Object = CheckboxCellDataPB;
|
||||
|
@ -1,8 +1,3 @@
|
||||
use collab_database::fields::media_type_option::{MediaCellData, MediaTypeOption};
|
||||
use collab_database::{fields::Field, rows::Cell};
|
||||
use flowy_error::FlowyResult;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::{
|
||||
entities::{FieldType, MediaCellChangeset, MediaCellDataPB, MediaFilterPB},
|
||||
services::{
|
||||
@ -14,6 +9,11 @@ use crate::{
|
||||
sort::SortCondition,
|
||||
},
|
||||
};
|
||||
use collab_database::fields::media_type_option::{MediaCellData, MediaTypeOption};
|
||||
use collab_database::template::util::ToCellString;
|
||||
use collab_database::{fields::Field, rows::Cell};
|
||||
use flowy_error::FlowyResult;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
impl TypeOption for MediaTypeOption {
|
||||
type CellData = MediaCellData;
|
||||
@ -60,7 +60,7 @@ impl CellDataDecoder for MediaTypeOption {
|
||||
}
|
||||
|
||||
fn stringify_cell_data(&self, cell_data: <Self as TypeOption>::CellData) -> String {
|
||||
cell_data.to_string()
|
||||
cell_data.to_cell_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ use collab_database::fields::relation_type_option::RelationTypeOption;
|
||||
|
||||
use collab_database::rows::Cell;
|
||||
use collab_database::template::relation_parse::RelationCellData;
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
use crate::entities::{RelationCellDataPB, RelationFilterPB};
|
||||
@ -57,7 +58,7 @@ impl CellDataChangeset for RelationTypeOption {
|
||||
|
||||
impl CellDataDecoder for RelationTypeOption {
|
||||
fn stringify_cell_data(&self, cell_data: RelationCellData) -> String {
|
||||
cell_data.to_string()
|
||||
cell_data.to_cell_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ use collab_database::fields::TypeOptionData;
|
||||
use collab_database::rows::Cell;
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
use collab_database::template::util::ToCellString;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
impl TypeOption for MultiSelectTypeOption {
|
||||
@ -80,7 +81,7 @@ impl CellDataChangeset for MultiSelectTypeOption {
|
||||
select_ids.retain(|id| id != &delete_option_id);
|
||||
}
|
||||
|
||||
tracing::trace!("Multi-select cell data: {}", select_ids.to_string());
|
||||
tracing::trace!("Multi-select cell data: {}", select_ids.to_cell_string());
|
||||
select_ids
|
||||
},
|
||||
};
|
||||
@ -153,6 +154,7 @@ mod tests {
|
||||
use collab_database::fields::select_type_option::{
|
||||
MultiSelectTypeOption, SelectOption, SelectOptionIds, SelectTypeOption,
|
||||
};
|
||||
use collab_database::template::util::ToCellString;
|
||||
|
||||
#[test]
|
||||
fn multi_select_insert_multi_option_test() {
|
||||
@ -202,7 +204,7 @@ mod tests {
|
||||
|
||||
let changeset = SelectOptionCellChangeset::from_insert_option_id(&google.id);
|
||||
let select_option_ids = multi_select.apply_changeset(changeset, None).unwrap().1;
|
||||
assert_eq!(select_option_ids.to_string(), google.id);
|
||||
assert_eq!(select_option_ids.to_cell_string(), google.id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -13,6 +13,7 @@ use collab_database::fields::select_type_option::{
|
||||
};
|
||||
use collab_database::fields::{Field, TypeOptionData};
|
||||
use collab_database::rows::Cell;
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_error::{internal_error, ErrorCode, FlowyResult};
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -118,7 +119,7 @@ where
|
||||
Some(SelectOptionIds::from(transformed_ids))
|
||||
},
|
||||
FieldType::Checkbox => {
|
||||
let cell_content = CheckboxCellDataPB::from(cell).to_string();
|
||||
let cell_content = CheckboxCellDataPB::from(cell).to_cell_string();
|
||||
let mut transformed_ids = Vec::new();
|
||||
let options = self.options();
|
||||
if let Some(option) = options.iter().find(|option| option.name == cell_content) {
|
||||
|
@ -4,7 +4,7 @@ use std::cmp::Ordering;
|
||||
use collab_database::fields::text_type_option::RichTextTypeOption;
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::rows::{new_cell_builder, Cell};
|
||||
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
|
||||
use crate::entities::{FieldType, TextFilterPB};
|
||||
@ -159,8 +159,8 @@ impl std::convert::From<String> for StringCellData {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for StringCellData {
|
||||
fn to_string(&self) -> String {
|
||||
impl ToCellString for StringCellData {
|
||||
fn to_cell_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use collab_database::fields::translate_type_option::TranslateTypeOption;
|
||||
use collab_database::fields::url_type_option::URLTypeOption;
|
||||
use collab_database::fields::{TypeOptionCellReader, TypeOptionData};
|
||||
use collab_database::rows::Cell;
|
||||
use collab_database::template::util::ToCellString;
|
||||
pub use collab_database::template::util::TypeOptionCellData;
|
||||
use protobuf::ProtobufError;
|
||||
use std::cmp::Ordering;
|
||||
@ -42,7 +43,7 @@ pub trait TypeOption: From<TypeOptionData> + Into<TypeOptionData> + TypeOptionCe
|
||||
///
|
||||
type CellData: for<'a> From<&'a Cell>
|
||||
+ TypeOptionCellData
|
||||
+ ToString
|
||||
+ ToCellString
|
||||
+ Default
|
||||
+ Send
|
||||
+ Sync
|
||||
|
@ -1,5 +1,6 @@
|
||||
use bytes::Bytes;
|
||||
use protobuf::ProtobufError;
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct ProtobufStr(pub String);
|
||||
@ -23,9 +24,9 @@ impl std::convert::From<String> for ProtobufStr {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for ProtobufStr {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
impl Display for ProtobufStr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ impl FilterController {
|
||||
let task = Task::new(
|
||||
&self.handler_id,
|
||||
task_id,
|
||||
TaskContent::Text(task_type.to_string()),
|
||||
TaskContent::Text(task_type.to_json_string()),
|
||||
qos,
|
||||
);
|
||||
self.task_scheduler.write().await.add_task(task);
|
||||
@ -547,8 +547,8 @@ enum FilterEvent {
|
||||
RowDidChanged(RowId),
|
||||
}
|
||||
|
||||
impl ToString for FilterEvent {
|
||||
fn to_string(&self) -> String {
|
||||
impl FilterEvent {
|
||||
fn to_json_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use collab::util::AnyMapExt;
|
||||
use collab_database::database::gen_database_filter_id;
|
||||
use collab_database::fields::select_type_option::SelectOptionIds;
|
||||
use collab_database::rows::RowId;
|
||||
use collab_database::template::util::ToCellString;
|
||||
use collab_database::views::{FilterMap, FilterMapBuilder};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use lib_infra::box_any::BoxAny;
|
||||
@ -365,12 +366,12 @@ impl<'a> From<&'a Filter> for FilterMap {
|
||||
end: filter.end,
|
||||
timestamp: filter.timestamp,
|
||||
}
|
||||
.to_string();
|
||||
.to_json_string();
|
||||
(filter.condition as u8, content)
|
||||
},
|
||||
FieldType::SingleSelect | FieldType::MultiSelect => {
|
||||
let filter = condition_and_content.cloned::<SelectOptionFilterPB>()?;
|
||||
let content = SelectOptionIds::from(filter.option_ids).to_string();
|
||||
let content = SelectOptionIds::from(filter.option_ids).to_cell_string();
|
||||
(filter.condition as u8, content)
|
||||
},
|
||||
FieldType::Checkbox => {
|
||||
|
@ -4,7 +4,7 @@ use collab_database::rows::{Cell, Cells, Row, RowId};
|
||||
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
|
||||
use crate::entities::{GroupPB, GroupRowsNotificationPB, InsertedGroupPB};
|
||||
use crate::services::field::TypeOption;
|
||||
use crate::services::group::{GroupChangeset, GroupData, MoveGroupRowContext};
|
||||
|
||||
@ -166,11 +166,6 @@ pub trait GroupController: Send + Sync {
|
||||
/// * `context`: information about the row being moved and its destination
|
||||
fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult<DidMoveGroupRowResult>;
|
||||
|
||||
/// Updates the groups after a field change. (currently never does anything)
|
||||
///
|
||||
/// * `field`: new changeset
|
||||
fn did_update_group_field(&mut self, field: &Field) -> FlowyResult<Option<GroupChangesPB>>;
|
||||
|
||||
/// Delete a group from the group configuration.
|
||||
///
|
||||
/// Return a list of deleted row ids and/or a new `TypeOptionData` if
|
||||
|
@ -10,7 +10,6 @@ use serde::Serialize;
|
||||
use tracing::event;
|
||||
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use crate::entities::{GroupChangesPB, GroupPB, InsertedGroupPB};
|
||||
use crate::services::field::RowSingleCellData;
|
||||
@ -364,7 +363,7 @@ where
|
||||
let configuration = (*self.setting).clone();
|
||||
let delegate = self.delegate.clone();
|
||||
let view_id = self.view_id.clone();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
match delegate.save_configuration(&view_id, configuration).await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
|
@ -10,8 +10,7 @@ use serde::Serialize;
|
||||
use tracing::trace;
|
||||
|
||||
use crate::entities::{
|
||||
FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB,
|
||||
RowMetaPB,
|
||||
FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB,
|
||||
};
|
||||
use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser};
|
||||
use crate::services::field::{default_type_option_data_from_type, TypeOption, TypeOptionCellData};
|
||||
@ -382,10 +381,6 @@ where
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult<Option<GroupChangesPB>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn delete_group(
|
||||
&mut self,
|
||||
group_id: &str,
|
||||
|
@ -6,9 +6,7 @@ use collab_database::rows::{Cells, Row, RowId};
|
||||
use flowy_error::FlowyResult;
|
||||
use tracing::trace;
|
||||
|
||||
use crate::entities::{
|
||||
GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB,
|
||||
};
|
||||
use crate::entities::{GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB};
|
||||
use crate::services::group::action::{
|
||||
DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupController,
|
||||
};
|
||||
@ -140,10 +138,6 @@ impl GroupController for DefaultGroupController {
|
||||
})
|
||||
}
|
||||
|
||||
fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult<Option<GroupChangesPB>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
async fn delete_group(
|
||||
&mut self,
|
||||
_group_id: &str,
|
||||
|
@ -177,7 +177,7 @@ impl SortController {
|
||||
let task = Task::new(
|
||||
&self.handler_id,
|
||||
task_id,
|
||||
TaskContent::Text(task_type.to_string()),
|
||||
TaskContent::Text(task_type.to_json_string()),
|
||||
qos,
|
||||
);
|
||||
self.task_scheduler.write().await.add_task(task);
|
||||
@ -351,8 +351,8 @@ enum SortEvent {
|
||||
DeleteAllSorts,
|
||||
}
|
||||
|
||||
impl ToString for SortEvent {
|
||||
fn to_string(&self) -> String {
|
||||
impl SortEvent {
|
||||
fn to_json_string(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ use flowy_database2::entities::{
|
||||
DatabaseViewSettingPB, FieldType, FilterPB, FilterType, TextFilterConditionPB, TextFilterPB,
|
||||
};
|
||||
use flowy_database2::services::database_view::DatabaseViewChanged;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use crate::database::database_editor::DatabaseEditorTest;
|
||||
|
||||
@ -265,7 +264,7 @@ impl DatabaseFilterTest {
|
||||
}
|
||||
let change = change.unwrap();
|
||||
let mut receiver = self.recv.take().unwrap();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
match tokio::time::timeout(Duration::from_secs(2), receiver.recv()).await {
|
||||
Ok(changed) => {
|
||||
if let DatabaseViewChanged::FilterNotification(notification) = changed.unwrap() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::database::pre_fill_cell_test::script::DatabasePreFillRowCellTest;
|
||||
use collab_database::fields::date_type_option::DateCellData;
|
||||
use collab_database::fields::select_type_option::SELECTION_IDS_SEPARATOR;
|
||||
use collab_database::template::util::ToCellString;
|
||||
use flowy_database2::entities::{CreateRowPayloadPB, FieldType};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -182,7 +183,7 @@ async fn row_data_payload_with_invalid_date_time_test() {
|
||||
timestamp: Some(1710510086),
|
||||
..Default::default()
|
||||
}
|
||||
.to_string();
|
||||
.to_cell_string();
|
||||
|
||||
test
|
||||
.create_row_with_payload(CreateRowPayloadPB {
|
||||
|
@ -20,21 +20,4 @@ fn main() {
|
||||
flowy_codegen::Project::TauriApp,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "web_ts")]
|
||||
{
|
||||
flowy_codegen::ts_event::gen(
|
||||
"document",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
flowy_codegen::protobuf_file::ts_gen(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"document",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::entities::{
|
||||
DocEventPB, DocumentAwarenessStatesPB, DocumentSnapshotStatePB, DocumentSyncStatePB,
|
||||
};
|
||||
use crate::notification::{send_notification, DocumentNotification};
|
||||
use crate::notification::{document_notification_builder, DocumentNotification};
|
||||
use collab::preclude::Collab;
|
||||
use collab_document::document::Document;
|
||||
use futures::StreamExt;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
|
||||
let doc_id_clone_for_block_changed = doc_id.to_owned();
|
||||
@ -14,7 +13,7 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
|
||||
tracing::trace!("subscribe_document_changed: {:?}", events);
|
||||
|
||||
// send notification to the client.
|
||||
send_notification(
|
||||
document_notification_builder(
|
||||
&doc_id_clone_for_block_changed,
|
||||
DocumentNotification::DidReceiveUpdate,
|
||||
)
|
||||
@ -26,7 +25,7 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
|
||||
document.subscribe_awareness_state("key", move |events| {
|
||||
#[cfg(feature = "verbose_log")]
|
||||
tracing::trace!("subscribe_awareness_state: {:?}", events);
|
||||
send_notification(
|
||||
document_notification_builder(
|
||||
&doc_id_clone_for_awareness_state,
|
||||
DocumentNotification::DidUpdateDocumentAwarenessState,
|
||||
)
|
||||
@ -38,11 +37,11 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) {
|
||||
pub fn subscribe_document_snapshot_state(collab: &Collab) {
|
||||
let document_id = collab.object_id().to_string();
|
||||
let mut snapshot_state = collab.subscribe_snapshot_state();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Some(snapshot_state) = snapshot_state.next().await {
|
||||
if let Some(new_snapshot_id) = snapshot_state.snapshot_id() {
|
||||
tracing::debug!("Did create document remote snapshot: {}", new_snapshot_id);
|
||||
send_notification(
|
||||
document_notification_builder(
|
||||
&document_id,
|
||||
DocumentNotification::DidUpdateDocumentSnapshotState,
|
||||
)
|
||||
@ -56,9 +55,9 @@ pub fn subscribe_document_snapshot_state(collab: &Collab) {
|
||||
pub fn subscribe_document_sync_state(collab: &Collab) {
|
||||
let document_id = collab.object_id().to_string();
|
||||
let mut sync_state_stream = collab.subscribe_sync_state();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Some(sync_state) = sync_state_stream.next().await {
|
||||
send_notification(
|
||||
document_notification_builder(
|
||||
&document_id,
|
||||
DocumentNotification::DidUpdateDocumentSyncState,
|
||||
)
|
||||
|
@ -29,7 +29,6 @@ use collab_integrate::collab_builder::{
|
||||
use flowy_document_pub::cloud::DocumentCloudService;
|
||||
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_storage_pub::storage::{CreatedUpload, StorageService};
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use crate::entities::UpdateDocumentAwarenessStatePB;
|
||||
use crate::entities::{
|
||||
@ -328,7 +327,7 @@ impl DocumentManager {
|
||||
self.removing_documents.insert(doc_id, document);
|
||||
|
||||
let weak_removing_documents = Arc::downgrade(&self.removing_documents);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(120)).await;
|
||||
if let Some(removing_documents) = weak_removing_documents.upgrade() {
|
||||
if removing_documents.remove(&clone_doc_id).is_some() {
|
||||
|
@ -32,6 +32,9 @@ impl std::convert::From<i32> for DocumentNotification {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub(crate) fn send_notification(id: &str, ty: DocumentNotification) -> NotificationBuilder {
|
||||
pub(crate) fn document_notification_builder(
|
||||
id: &str,
|
||||
ty: DocumentNotification,
|
||||
) -> NotificationBuilder {
|
||||
NotificationBuilder::new(id, ty, DOCUMENT_OBSERVABLE_SOURCE)
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ async fn parse_readme_test() {
|
||||
let data = json_str_to_hashmap(&block.data).ok();
|
||||
assert!(data.is_some());
|
||||
if let Some(data) = data {
|
||||
assert!(data.get("delta").is_none());
|
||||
assert!(!data.contains_key("delta"));
|
||||
}
|
||||
|
||||
if let Some(external_id) = &block.external_id {
|
||||
|
@ -15,13 +15,4 @@ fn main() {
|
||||
flowy_codegen::Project::TauriApp,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "web_ts")]
|
||||
flowy_codegen::protobuf_file::ts_gen(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"error",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1,321 +0,0 @@
|
||||
use std::future::Future;
|
||||
|
||||
use crate::cloud::gen_view_id;
|
||||
use collab_folder::{RepeatedViewIdentifier, View, ViewIcon, ViewIdentifier, ViewLayout};
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
/// A builder for creating views, each able to have children views of
|
||||
/// their own.
|
||||
pub struct NestedViewBuilder {
|
||||
pub uid: i64,
|
||||
pub parent_view_id: String,
|
||||
pub views: Vec<ParentChildViews>,
|
||||
}
|
||||
|
||||
impl NestedViewBuilder {
|
||||
pub fn new(parent_view_id: String, uid: i64) -> Self {
|
||||
Self {
|
||||
uid,
|
||||
parent_view_id,
|
||||
views: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn with_view_builder<F, O>(&mut self, view_builder: F)
|
||||
where
|
||||
F: Fn(ViewBuilder) -> O,
|
||||
O: Future<Output = ParentChildViews>,
|
||||
{
|
||||
let builder = ViewBuilder::new(self.uid, self.parent_view_id.clone());
|
||||
self.views.push(view_builder(builder).await);
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> Vec<ParentChildViews> {
|
||||
std::mem::take(&mut self.views)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for creating a view.
|
||||
/// The default layout of the view is [ViewLayout::Document]
|
||||
pub struct ViewBuilder {
|
||||
uid: i64,
|
||||
parent_view_id: String,
|
||||
view_id: String,
|
||||
name: String,
|
||||
desc: String,
|
||||
layout: ViewLayout,
|
||||
child_views: Vec<ParentChildViews>,
|
||||
is_favorite: bool,
|
||||
icon: Option<ViewIcon>,
|
||||
}
|
||||
|
||||
impl ViewBuilder {
|
||||
pub fn new(uid: i64, parent_view_id: String) -> Self {
|
||||
Self {
|
||||
uid,
|
||||
parent_view_id,
|
||||
view_id: gen_view_id().to_string(),
|
||||
name: Default::default(),
|
||||
desc: Default::default(),
|
||||
layout: ViewLayout::Document,
|
||||
child_views: vec![],
|
||||
is_favorite: false,
|
||||
|
||||
icon: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_id(&self) -> &str {
|
||||
&self.view_id
|
||||
}
|
||||
|
||||
pub fn with_view_id<T: ToString>(mut self, view_id: T) -> Self {
|
||||
self.view_id = view_id.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_layout(mut self, layout: ViewLayout) -> Self {
|
||||
self.layout = layout;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_name<T: ToString>(mut self, name: T) -> Self {
|
||||
self.name = name.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_desc(mut self, desc: &str) -> Self {
|
||||
self.desc = desc.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_icon(mut self, icon: &str) -> Self {
|
||||
self.icon = Some(ViewIcon {
|
||||
ty: collab_folder::IconType::Emoji,
|
||||
value: icon.to_string(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_view(mut self, view: ParentChildViews) -> Self {
|
||||
self.child_views.push(view);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_child_views(mut self, mut views: Vec<ParentChildViews>) -> Self {
|
||||
self.child_views.append(&mut views);
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a child view for the current view.
|
||||
/// The view created by this builder will be the next level view of the current view.
|
||||
pub async fn with_child_view_builder<F, O>(mut self, child_view_builder: F) -> Self
|
||||
where
|
||||
F: Fn(ViewBuilder) -> O,
|
||||
O: Future<Output = ParentChildViews>,
|
||||
{
|
||||
let builder = ViewBuilder::new(self.uid, self.view_id.clone());
|
||||
self.child_views.push(child_view_builder(builder).await);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> ParentChildViews {
|
||||
let view = View {
|
||||
id: self.view_id,
|
||||
parent_view_id: self.parent_view_id,
|
||||
name: self.name,
|
||||
desc: self.desc,
|
||||
created_at: timestamp(),
|
||||
is_favorite: self.is_favorite,
|
||||
layout: self.layout,
|
||||
icon: self.icon,
|
||||
created_by: Some(self.uid),
|
||||
last_edited_time: 0,
|
||||
children: RepeatedViewIdentifier::new(
|
||||
self
|
||||
.child_views
|
||||
.iter()
|
||||
.map(|v| ViewIdentifier {
|
||||
id: v.parent_view.id.clone(),
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
last_edited_by: Some(self.uid),
|
||||
extra: None,
|
||||
};
|
||||
ParentChildViews {
|
||||
parent_view: view,
|
||||
child_views: self.child_views,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ParentChildViews {
|
||||
pub parent_view: View,
|
||||
pub child_views: Vec<ParentChildViews>,
|
||||
}
|
||||
|
||||
impl ParentChildViews {
|
||||
pub fn new(view: View) -> Self {
|
||||
Self {
|
||||
parent_view: view,
|
||||
child_views: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flatten(self) -> Vec<View> {
|
||||
FlattedViews::flatten_views(vec![self])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FlattedViews;
|
||||
|
||||
impl FlattedViews {
|
||||
pub fn flatten_views(views: Vec<ParentChildViews>) -> Vec<View> {
|
||||
let mut result = vec![];
|
||||
for view in views {
|
||||
result.push(view.parent_view);
|
||||
result.append(&mut Self::flatten_views(view.child_views));
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::folder_builder::{FlattedViews, NestedViewBuilder};
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_first_level_views_test() {
|
||||
let workspace_id = "w1".to_string();
|
||||
let mut builder = NestedViewBuilder::new(workspace_id, 1);
|
||||
builder
|
||||
.with_view_builder(|view_builder| async { view_builder.with_name("1").build() })
|
||||
.await;
|
||||
builder
|
||||
.with_view_builder(|view_builder| async { view_builder.with_name("2").build() })
|
||||
.await;
|
||||
builder
|
||||
.with_view_builder(|view_builder| async { view_builder.with_name("3").build() })
|
||||
.await;
|
||||
let workspace_views = builder.build();
|
||||
assert_eq!(workspace_views.len(), 3);
|
||||
|
||||
let views = FlattedViews::flatten_views(workspace_views);
|
||||
assert_eq!(views.len(), 3);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_view_with_child_views_test() {
|
||||
let workspace_id = "w1".to_string();
|
||||
let mut builder = NestedViewBuilder::new(workspace_id, 1);
|
||||
builder
|
||||
.with_view_builder(|view_builder| async {
|
||||
view_builder
|
||||
.with_name("1")
|
||||
.with_child_view_builder(|child_view_builder| async {
|
||||
child_view_builder.with_name("1_1").build()
|
||||
})
|
||||
.await
|
||||
.with_child_view_builder(|child_view_builder| async {
|
||||
child_view_builder.with_name("1_2").build()
|
||||
})
|
||||
.await
|
||||
.build()
|
||||
})
|
||||
.await;
|
||||
builder
|
||||
.with_view_builder(|view_builder| async {
|
||||
view_builder
|
||||
.with_name("2")
|
||||
.with_child_view_builder(|child_view_builder| async {
|
||||
child_view_builder.with_name("2_1").build()
|
||||
})
|
||||
.await
|
||||
.build()
|
||||
})
|
||||
.await;
|
||||
let workspace_views = builder.build();
|
||||
assert_eq!(workspace_views.len(), 2);
|
||||
|
||||
assert_eq!(workspace_views[0].parent_view.name, "1");
|
||||
assert_eq!(workspace_views[0].child_views.len(), 2);
|
||||
assert_eq!(workspace_views[0].child_views[0].parent_view.name, "1_1");
|
||||
assert_eq!(workspace_views[0].child_views[1].parent_view.name, "1_2");
|
||||
assert_eq!(workspace_views[1].child_views.len(), 1);
|
||||
assert_eq!(workspace_views[1].child_views[0].parent_view.name, "2_1");
|
||||
|
||||
let views = FlattedViews::flatten_views(workspace_views);
|
||||
assert_eq!(views.len(), 5);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_three_level_view_test() {
|
||||
let workspace_id = "w1".to_string();
|
||||
let mut builder = NestedViewBuilder::new(workspace_id, 1);
|
||||
builder
|
||||
.with_view_builder(|view_builder| async {
|
||||
view_builder
|
||||
.with_name("1")
|
||||
.with_child_view_builder(|child_view_builder| async {
|
||||
child_view_builder
|
||||
.with_name("1_1")
|
||||
.with_child_view_builder(|b| async { b.with_name("1_1_1").build() })
|
||||
.await
|
||||
.with_child_view_builder(|b| async { b.with_name("1_1_2").build() })
|
||||
.await
|
||||
.build()
|
||||
})
|
||||
.await
|
||||
.with_child_view_builder(|child_view_builder| async {
|
||||
child_view_builder
|
||||
.with_name("1_2")
|
||||
.with_child_view_builder(|b| async { b.with_name("1_2_1").build() })
|
||||
.await
|
||||
.with_child_view_builder(|b| async { b.with_name("1_2_2").build() })
|
||||
.await
|
||||
.build()
|
||||
})
|
||||
.await
|
||||
.build()
|
||||
})
|
||||
.await;
|
||||
let workspace_views = builder.build();
|
||||
assert_eq!(workspace_views.len(), 1);
|
||||
|
||||
assert_eq!(workspace_views[0].parent_view.name, "1");
|
||||
assert_eq!(workspace_views[0].child_views.len(), 2);
|
||||
assert_eq!(workspace_views[0].child_views[0].parent_view.name, "1_1");
|
||||
assert_eq!(workspace_views[0].child_views[1].parent_view.name, "1_2");
|
||||
|
||||
assert_eq!(
|
||||
workspace_views[0].child_views[0].child_views[0]
|
||||
.parent_view
|
||||
.name,
|
||||
"1_1_1"
|
||||
);
|
||||
assert_eq!(
|
||||
workspace_views[0].child_views[0].child_views[1]
|
||||
.parent_view
|
||||
.name,
|
||||
"1_1_2"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
workspace_views[0].child_views[1].child_views[0]
|
||||
.parent_view
|
||||
.name,
|
||||
"1_2_1"
|
||||
);
|
||||
assert_eq!(
|
||||
workspace_views[0].child_views[1].child_views[1]
|
||||
.parent_view
|
||||
.name,
|
||||
"1_2_2"
|
||||
);
|
||||
|
||||
let views = FlattedViews::flatten_views(workspace_views);
|
||||
assert_eq!(views.len(), 7);
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
pub mod cloud;
|
||||
pub mod entities;
|
||||
pub mod query;
|
||||
|
6
frontend/rust-lib/flowy-folder-pub/src/query.rs
Normal file
6
frontend/rust-lib/flowy-folder-pub/src/query.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use lib_infra::async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait FolderQueryService: Send + Sync + 'static {
|
||||
async fn get_sibling_ids(&self, parent_view_id: &str) -> Vec<String>;
|
||||
}
|
@ -21,8 +21,8 @@ arc-swap.workspace = true
|
||||
unicode-segmentation = "1.10"
|
||||
tracing.workspace = true
|
||||
flowy-error = { path = "../flowy-error", features = [
|
||||
"impl_from_dispatch_error",
|
||||
"impl_from_collab_folder",
|
||||
"impl_from_dispatch_error",
|
||||
"impl_from_collab_folder",
|
||||
] }
|
||||
lib-dispatch = { workspace = true }
|
||||
bytes.workspace = true
|
||||
@ -42,6 +42,7 @@ async-trait.workspace = true
|
||||
client-api = { workspace = true }
|
||||
regex = "1.9.5"
|
||||
futures = "0.3.30"
|
||||
dashmap.workspace = true
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -20,21 +20,4 @@ fn main() {
|
||||
flowy_codegen::Project::TauriApp,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "web_ts")]
|
||||
{
|
||||
flowy_codegen::ts_event::gen(
|
||||
"folder",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
flowy_codegen::protobuf_file::ts_gen(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"folder",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::entities::parser::empty_str::NotEmptyStr;
|
||||
use crate::entities::ViewLayoutPB;
|
||||
use crate::share::{ImportParams, ImportType, ImportValue};
|
||||
use crate::share::{ImportData, ImportItem, ImportParams, ImportType};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::FlowyError;
|
||||
use lib_infra::validator_fn::required_not_empty_str;
|
||||
@ -34,7 +34,7 @@ impl Default for ImportTypePB {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, ProtoBuf, Default)]
|
||||
pub struct ImportValuePayloadPB {
|
||||
pub struct ImportItemPayloadPB {
|
||||
// the name of the import page
|
||||
#[pb(index = 1)]
|
||||
pub name: String,
|
||||
@ -65,7 +65,7 @@ pub struct ImportPayloadPB {
|
||||
pub parent_view_id: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub values: Vec<ImportValuePayloadPB>,
|
||||
pub items: Vec<ImportItemPayloadPB>,
|
||||
}
|
||||
|
||||
impl TryInto<ImportParams> for ImportPayloadPB {
|
||||
@ -76,38 +76,39 @@ impl TryInto<ImportParams> for ImportPayloadPB {
|
||||
.map_err(|_| FlowyError::invalid_view_id())?
|
||||
.0;
|
||||
|
||||
let mut values = Vec::new();
|
||||
let items = self
|
||||
.items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
let name = if item.name.is_empty() {
|
||||
"Untitled".to_string()
|
||||
} else {
|
||||
item.name
|
||||
};
|
||||
|
||||
for value in self.values {
|
||||
let name = if value.name.is_empty() {
|
||||
"Untitled".to_string()
|
||||
} else {
|
||||
value.name
|
||||
};
|
||||
let data = match (item.file_path, item.data) {
|
||||
(Some(file_path), None) => ImportData::FilePath { file_path },
|
||||
(None, Some(bytes)) => ImportData::Bytes { bytes },
|
||||
(None, None) => {
|
||||
return Err(FlowyError::invalid_data().with_context("The import data is empty"));
|
||||
},
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(FlowyError::invalid_data().with_context("The import data is ambiguous"));
|
||||
},
|
||||
};
|
||||
|
||||
let file_path = match value.file_path {
|
||||
None => None,
|
||||
Some(file_path) => Some(
|
||||
NotEmptyStr::parse(file_path)
|
||||
.map_err(|_| FlowyError::invalid_data().with_context("The import file path is empty"))?
|
||||
.0,
|
||||
),
|
||||
};
|
||||
|
||||
let params = ImportValue {
|
||||
name,
|
||||
data: value.data,
|
||||
file_path,
|
||||
view_layout: value.view_layout.into(),
|
||||
import_type: value.import_type.into(),
|
||||
};
|
||||
|
||||
values.push(params);
|
||||
}
|
||||
Ok(ImportItem {
|
||||
name,
|
||||
data,
|
||||
view_layout: item.view_layout.into(),
|
||||
import_type: item.import_type.into(),
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok(ImportParams {
|
||||
parent_view_id,
|
||||
values,
|
||||
items,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ pub mod view_operation;
|
||||
|
||||
mod manager_init;
|
||||
mod manager_observer;
|
||||
#[cfg(debug_assertions)]
|
||||
pub mod manager_test_util;
|
||||
|
||||
pub mod publish_util;
|
||||
pub mod share;
|
||||
|
@ -10,13 +10,13 @@ use crate::manager_observer::{
|
||||
ChildViewChangeReason,
|
||||
};
|
||||
use crate::notification::{
|
||||
send_current_workspace_notification, send_notification, FolderNotification,
|
||||
folder_notification_builder, send_current_workspace_notification, FolderNotification,
|
||||
};
|
||||
use crate::publish_util::{generate_publish_name, view_pb_to_publish_view};
|
||||
use crate::share::{ImportParams, ImportValue};
|
||||
use crate::share::{ImportData, ImportItem, ImportParams};
|
||||
use crate::util::{folder_not_init_error, workspace_data_not_sync_error};
|
||||
use crate::view_operation::{
|
||||
create_view, EncodedCollabWrapper, FolderOperationHandler, FolderOperationHandlers, ViewData,
|
||||
create_view, EncodedCollabType, FolderOperationHandler, FolderOperationHandlers, ViewData,
|
||||
};
|
||||
use arc_swap::ArcSwapOption;
|
||||
use client_api::entity::workspace_dto::PublishInfoView;
|
||||
@ -57,11 +57,6 @@ pub trait FolderUser: Send + Sync {
|
||||
}
|
||||
|
||||
pub struct FolderManager {
|
||||
//FIXME: there's no sense in having a mutex_folder behind an RwLock. It's being obtained multiple
|
||||
// times in the same function. FolderManager itself should be hidden behind RwLock if necessary.
|
||||
// Unfortunately, this would require a changing the SyncPlugin architecture which requires access
|
||||
// to Arc<RwLock<BorrowMut<Collab>>>. Eventually SyncPlugin should be refactored.
|
||||
/// MutexFolder is the folder that is used to store the data.
|
||||
pub(crate) mutex_folder: ArcSwapOption<RwLock<Folder>>,
|
||||
pub(crate) collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||
pub(crate) user: Arc<dyn FolderUser>,
|
||||
@ -75,7 +70,6 @@ impl FolderManager {
|
||||
pub fn new(
|
||||
user: Arc<dyn FolderUser>,
|
||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
||||
operation_handlers: FolderOperationHandlers,
|
||||
cloud_service: Arc<dyn FolderCloudService>,
|
||||
folder_indexer: Arc<dyn FolderIndexManager>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
@ -84,7 +78,7 @@ impl FolderManager {
|
||||
user,
|
||||
mutex_folder: Default::default(),
|
||||
collab_builder,
|
||||
operation_handlers,
|
||||
operation_handlers: Default::default(),
|
||||
cloud_service,
|
||||
folder_indexer,
|
||||
store_preferences,
|
||||
@ -93,10 +87,17 @@ impl FolderManager {
|
||||
Ok(manager)
|
||||
}
|
||||
|
||||
pub fn register_operation_handler(
|
||||
&self,
|
||||
layout: ViewLayout,
|
||||
handler: Arc<dyn FolderOperationHandler + Send + Sync>,
|
||||
) {
|
||||
self.operation_handlers.insert(layout, handler);
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self), err)]
|
||||
pub async fn get_current_workspace(&self) -> FlowyResult<WorkspacePB> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
|
||||
match self.mutex_folder.load_full() {
|
||||
None => {
|
||||
let uid = self.user.user_id()?;
|
||||
@ -118,6 +119,31 @@ impl FolderManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_folder_data(&self) -> FlowyResult<FolderData> {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let data = self
|
||||
.mutex_folder
|
||||
.load_full()
|
||||
.ok_or_else(|| internal_error("The folder is not initialized"))?
|
||||
.read()
|
||||
.await
|
||||
.get_folder_data(&workspace_id)
|
||||
.ok_or_else(|| internal_error("Workspace id not match the id in current folder"))?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub async fn get_encode_collab_from_disk(
|
||||
&self,
|
||||
view_id: &str,
|
||||
layout: &ViewLayout,
|
||||
) -> FlowyResult<EncodedCollabType> {
|
||||
let handler = self.get_handler(layout)?;
|
||||
let encoded_collab = handler
|
||||
.get_encoded_collab_v1_from_disk(&self.user, view_id)
|
||||
.await?;
|
||||
Ok(encoded_collab)
|
||||
}
|
||||
|
||||
/// 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>> {
|
||||
@ -372,7 +398,7 @@ impl FolderManager {
|
||||
.ok_or_else(|| FlowyError::internal().with_context("Cannot find the workspace ID"))?;
|
||||
|
||||
views.iter_mut().for_each(|view| {
|
||||
view.view.parent_view_id = workspace_id.clone();
|
||||
view.view.parent_view_id.clone_from(&workspace_id);
|
||||
view.view.extra = Some(
|
||||
serde_json::to_string(
|
||||
&ViewExtraBuilder::new()
|
||||
@ -460,7 +486,7 @@ impl FolderManager {
|
||||
latest_view.space_info(),
|
||||
);
|
||||
views.iter_mut().for_each(|child_view| {
|
||||
child_view.view.parent_view_id = latest_view.id.clone();
|
||||
child_view.view.parent_view_id.clone_from(&latest_view.id);
|
||||
});
|
||||
},
|
||||
}
|
||||
@ -517,7 +543,13 @@ impl FolderManager {
|
||||
);
|
||||
if params.meta.is_empty() && params.initial_data.is_empty() {
|
||||
handler
|
||||
.create_view_with_default_data(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone())
|
||||
.create_default_view(
|
||||
user_id,
|
||||
¶ms.parent_view_id,
|
||||
¶ms.view_id,
|
||||
¶ms.name,
|
||||
view_layout.clone(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
encoded_collab = handler
|
||||
@ -555,7 +587,13 @@ impl FolderManager {
|
||||
let handler = self.get_handler(&view_layout)?;
|
||||
let user_id = self.user.user_id()?;
|
||||
handler
|
||||
.create_view_with_default_data(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone())
|
||||
.create_default_view(
|
||||
user_id,
|
||||
¶ms.parent_view_id,
|
||||
¶ms.view_id,
|
||||
¶ms.name,
|
||||
view_layout.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let view = create_view(self.user.user_id()?, params, view_layout);
|
||||
@ -707,7 +745,7 @@ impl FolderManager {
|
||||
break;
|
||||
}
|
||||
ancestors.push(view_pb_without_child_views(view.as_ref().clone()));
|
||||
parent_view_id = view.parent_view_id.clone();
|
||||
parent_view_id.clone_from(&view.parent_view_id);
|
||||
}
|
||||
ancestors.reverse();
|
||||
}
|
||||
@ -740,7 +778,7 @@ impl FolderManager {
|
||||
drop(folder);
|
||||
|
||||
// notify the parent view that the view is moved to trash
|
||||
send_notification(view_id, FolderNotification::DidMoveViewToTrash)
|
||||
folder_notification_builder(view_id, FolderNotification::DidMoveViewToTrash)
|
||||
.payload(DeletedViewPB {
|
||||
view_id: view_id.to_string(),
|
||||
index: None,
|
||||
@ -774,7 +812,7 @@ impl FolderManager {
|
||||
.map(|v| v.id.clone())
|
||||
.collect(),
|
||||
);
|
||||
send_notification("favorite", FolderNotification::DidUnfavoriteView)
|
||||
folder_notification_builder("favorite", FolderNotification::DidUnfavoriteView)
|
||||
.payload(RepeatedViewPB {
|
||||
items: favorite_descendant_views,
|
||||
})
|
||||
@ -881,6 +919,20 @@ impl FolderManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_view(&self, view_id: &str) -> FlowyResult<Arc<View>> {
|
||||
match self.mutex_folder.load_full() {
|
||||
Some(folder) => {
|
||||
let folder = folder.read().await;
|
||||
Ok(
|
||||
folder
|
||||
.get_view(view_id)
|
||||
.ok_or_else(FlowyError::record_not_found)?,
|
||||
)
|
||||
},
|
||||
None => Err(FlowyError::internal().with_context("The folder is not initialized")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the view with the given params.
|
||||
#[tracing::instrument(level = "trace", skip(self), err)]
|
||||
pub async fn update_view_with_params(&self, params: UpdateViewParams) -> FlowyResult<()> {
|
||||
@ -1066,7 +1118,7 @@ impl FolderManager {
|
||||
.await?;
|
||||
|
||||
if is_source_view {
|
||||
new_view_id = duplicated_view.id.clone();
|
||||
new_view_id.clone_from(&duplicated_view.id);
|
||||
}
|
||||
|
||||
if sync_after_create {
|
||||
@ -1248,7 +1300,9 @@ impl FolderManager {
|
||||
.into_iter()
|
||||
.map(|mut p| {
|
||||
if let PublishPayload::Database(p) = &mut p {
|
||||
p.data.visible_database_view_ids = selected_view_ids.clone();
|
||||
p.data
|
||||
.visible_database_view_ids
|
||||
.clone_from(&selected_view_ids);
|
||||
}
|
||||
p
|
||||
})
|
||||
@ -1448,9 +1502,9 @@ impl FolderManager {
|
||||
publish_name: Option<String>,
|
||||
layout: ViewLayout,
|
||||
) -> FlowyResult<PublishPayload> {
|
||||
let handler: Arc<dyn FolderOperationHandler + Sync + Send> = self.get_handler(&layout)?;
|
||||
let encoded_collab_wrapper: EncodedCollabWrapper = handler
|
||||
.get_encoded_collab_v1_from_disk(self.user.clone(), view_id)
|
||||
let handler = self.get_handler(&layout)?;
|
||||
let encoded_collab_wrapper: EncodedCollabType = handler
|
||||
.get_encoded_collab_v1_from_disk(&self.user, view_id)
|
||||
.await?;
|
||||
let view = self.get_view_pb(view_id).await?;
|
||||
|
||||
@ -1481,7 +1535,7 @@ impl FolderManager {
|
||||
};
|
||||
|
||||
let payload = match encoded_collab_wrapper {
|
||||
EncodedCollabWrapper::Database(v) => {
|
||||
EncodedCollabType::Database(v) => {
|
||||
let database_collab = v.database_encoded_collab.doc_state.to_vec();
|
||||
let database_relations = v.database_relations;
|
||||
let database_row_collabs = v
|
||||
@ -1504,11 +1558,11 @@ impl FolderManager {
|
||||
};
|
||||
PublishPayload::Database(PublishDatabasePayload { meta, data })
|
||||
},
|
||||
EncodedCollabWrapper::Document(v) => {
|
||||
EncodedCollabType::Document(v) => {
|
||||
let data = v.document_encoded_collab.doc_state.to_vec();
|
||||
PublishPayload::Document(PublishDocumentPayload { meta, data })
|
||||
},
|
||||
EncodedCollabWrapper::Unknown => PublishPayload::Unknown,
|
||||
EncodedCollabType::Unknown => PublishPayload::Unknown,
|
||||
};
|
||||
|
||||
Ok(payload)
|
||||
@ -1522,13 +1576,13 @@ impl FolderManager {
|
||||
} else {
|
||||
FolderNotification::DidUnfavoriteView
|
||||
};
|
||||
send_notification("favorite", notification_type)
|
||||
folder_notification_builder("favorite", notification_type)
|
||||
.payload(RepeatedViewPB {
|
||||
items: vec![view.clone()],
|
||||
})
|
||||
.send();
|
||||
|
||||
send_notification(&view.id, FolderNotification::DidUpdateView)
|
||||
folder_notification_builder(&view.id, FolderNotification::DidUpdateView)
|
||||
.payload(view)
|
||||
.send()
|
||||
}
|
||||
@ -1536,7 +1590,7 @@ impl FolderManager {
|
||||
|
||||
async fn send_update_recent_views_notification(&self) {
|
||||
let recent_views = self.get_my_recent_sections().await;
|
||||
send_notification("recent_views", FolderNotification::DidUpdateRecentViews)
|
||||
folder_notification_builder("recent_views", FolderNotification::DidUpdateRecentViews)
|
||||
.payload(RepeatedViewIdPB {
|
||||
items: recent_views.into_iter().map(|item| item.id).collect(),
|
||||
})
|
||||
@ -1566,7 +1620,7 @@ impl FolderManager {
|
||||
if let Some(lock) = self.mutex_folder.load_full() {
|
||||
let mut folder = lock.write().await;
|
||||
folder.remove_all_my_trash_sections();
|
||||
send_notification("trash", FolderNotification::DidUpdateTrash)
|
||||
folder_notification_builder("trash", FolderNotification::DidUpdateTrash)
|
||||
.payload(RepeatedTrashPB { items: vec![] })
|
||||
.send();
|
||||
}
|
||||
@ -1592,7 +1646,7 @@ impl FolderManager {
|
||||
for trash in deleted_trash {
|
||||
let _ = self.delete_trash(&trash.id).await;
|
||||
}
|
||||
send_notification("trash", FolderNotification::DidUpdateTrash)
|
||||
folder_notification_builder("trash", FolderNotification::DidUpdateTrash)
|
||||
.payload(RepeatedTrashPB { items: vec![] })
|
||||
.send();
|
||||
}
|
||||
@ -1622,42 +1676,36 @@ impl FolderManager {
|
||||
}
|
||||
|
||||
/// Imports a single file to the folder and returns the encoded collab for immediate cloud sync.
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[instrument(level = "debug", skip_all, err)]
|
||||
pub(crate) async fn import_single_file(
|
||||
&self,
|
||||
parent_view_id: String,
|
||||
import_data: ImportValue,
|
||||
import_data: ImportItem,
|
||||
) -> FlowyResult<(View, Vec<(String, CollabType, EncodedCollab)>)> {
|
||||
// Ensure either data or file_path is provided
|
||||
if import_data.data.is_none() && import_data.file_path.is_none() {
|
||||
return Err(FlowyError::new(
|
||||
ErrorCode::InvalidParams,
|
||||
"Either data or file_path is required",
|
||||
));
|
||||
}
|
||||
|
||||
let handler = self.get_handler(&import_data.view_layout)?;
|
||||
let view_id = gen_view_id().to_string();
|
||||
let uid = self.user.user_id()?;
|
||||
let mut encoded_collab = vec![];
|
||||
|
||||
// Import data from bytes if available
|
||||
if let Some(data) = import_data.data {
|
||||
encoded_collab = handler
|
||||
.import_from_bytes(
|
||||
uid,
|
||||
&view_id,
|
||||
&import_data.name,
|
||||
import_data.import_type,
|
||||
data,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Import data from file path if available
|
||||
if let Some(file_path) = import_data.file_path {
|
||||
handler
|
||||
.import_from_file_path(&view_id, &import_data.name, file_path)
|
||||
.await?;
|
||||
info!("import single file from:{}", import_data.data);
|
||||
match import_data.data {
|
||||
ImportData::FilePath { file_path } => {
|
||||
handler
|
||||
.import_from_file_path(&view_id, &import_data.name, file_path)
|
||||
.await?;
|
||||
},
|
||||
ImportData::Bytes { bytes } => {
|
||||
encoded_collab = handler
|
||||
.import_from_bytes(
|
||||
uid,
|
||||
&view_id,
|
||||
&import_data.name,
|
||||
import_data.import_type,
|
||||
bytes,
|
||||
)
|
||||
.await?;
|
||||
},
|
||||
}
|
||||
|
||||
let params = CreateViewParams {
|
||||
@ -1695,7 +1743,7 @@ impl FolderManager {
|
||||
let workspace_id = self.user.workspace_id()?;
|
||||
let mut objects = vec![];
|
||||
let mut views = vec![];
|
||||
for data in import_data.values {
|
||||
for data in import_data.items {
|
||||
// Import a single file and get the view and encoded collab data
|
||||
let (view, encoded_collabs) = self
|
||||
.import_single_file(import_data.parent_view_id.clone(), data)
|
||||
@ -1751,7 +1799,7 @@ impl FolderManager {
|
||||
}
|
||||
|
||||
if let Ok(view_pb) = self.get_view_pb(view_id).await {
|
||||
send_notification(&view_pb.id, FolderNotification::DidUpdateView)
|
||||
folder_notification_builder(&view_pb.id, FolderNotification::DidUpdateView)
|
||||
.payload(view_pb)
|
||||
.send();
|
||||
|
||||
@ -1765,10 +1813,7 @@ impl FolderManager {
|
||||
}
|
||||
|
||||
/// Returns a handler that implements the [FolderOperationHandler] trait
|
||||
fn get_handler(
|
||||
&self,
|
||||
view_layout: &ViewLayout,
|
||||
) -> FlowyResult<Arc<dyn FolderOperationHandler + Send + Sync>> {
|
||||
fn get_handler(&self, view_layout: &ViewLayout) -> FlowyResult<Arc<dyn FolderOperationHandler>> {
|
||||
match self.operation_handlers.get(view_layout) {
|
||||
None => Err(FlowyError::internal().with_context(format!(
|
||||
"Get data processor failed. Unknown layout type: {:?}",
|
||||
|
@ -128,11 +128,6 @@ impl FolderManager {
|
||||
folder_state_rx,
|
||||
Arc::downgrade(&self.user),
|
||||
);
|
||||
subscribe_folder_snapshot_state_changed(
|
||||
workspace_id.clone(),
|
||||
weak_mutex_folder.clone(),
|
||||
Arc::downgrade(&self.user),
|
||||
);
|
||||
subscribe_folder_trash_changed(
|
||||
workspace_id.clone(),
|
||||
section_change_rx,
|
||||
|
@ -1,16 +1,16 @@
|
||||
use crate::entities::{
|
||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB,
|
||||
FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
|
||||
view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSyncStatePB,
|
||||
RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB,
|
||||
};
|
||||
use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, FolderUser};
|
||||
use crate::notification::{send_notification, FolderNotification};
|
||||
use crate::notification::{folder_notification_builder, FolderNotification};
|
||||
use collab::core::collab_state::SyncState;
|
||||
use collab::lock::RwLock;
|
||||
use collab_folder::{
|
||||
Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange,
|
||||
ViewChangeReceiver,
|
||||
};
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Weak;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
@ -24,7 +24,7 @@ pub(crate) fn subscribe_folder_view_changed(
|
||||
weak_mutex_folder: Weak<RwLock<Folder>>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
af_spawn(async move {
|
||||
tokio::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() {
|
||||
@ -37,7 +37,7 @@ pub(crate) fn subscribe_folder_view_changed(
|
||||
}
|
||||
|
||||
if let Some(lock) = weak_mutex_folder.upgrade() {
|
||||
tracing::trace!("Did receive view change: {:?}", value);
|
||||
trace!("Did receive view change: {:?}", value);
|
||||
match value {
|
||||
ViewChange::DidCreateView { view } => {
|
||||
notify_child_views_changed(
|
||||
@ -70,44 +70,12 @@ pub(crate) fn subscribe_folder_view_changed(
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn subscribe_folder_snapshot_state_changed(
|
||||
workspace_id: String,
|
||||
weak_mutex_folder: Weak<RwLock<Folder>>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
af_spawn(async move {
|
||||
if let Some(folder) = weak_mutex_folder.upgrade() {
|
||||
let mut state_stream = folder.read().await.subscribe_snapshot_state();
|
||||
|
||||
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(
|
||||
&workspace_id,
|
||||
FolderNotification::DidUpdateFolderSnapshotState,
|
||||
)
|
||||
.payload(FolderSnapshotStatePB { new_snapshot_id })
|
||||
.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn subscribe_folder_sync_state_changed(
|
||||
workspace_id: String,
|
||||
mut folder_sync_state_rx: WatchStream<SyncState>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
af_spawn(async move {
|
||||
tokio::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() {
|
||||
@ -118,7 +86,7 @@ pub(crate) fn subscribe_folder_sync_state_changed(
|
||||
}
|
||||
}
|
||||
|
||||
send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
|
||||
folder_notification_builder(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate)
|
||||
.payload(FolderSyncStatePB::from(state))
|
||||
.send();
|
||||
}
|
||||
@ -132,7 +100,7 @@ pub(crate) fn subscribe_folder_trash_changed(
|
||||
weak_mutex_folder: Weak<RwLock<Folder>>,
|
||||
user: Weak<dyn FolderUser>,
|
||||
) {
|
||||
af_spawn(async move {
|
||||
tokio::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() {
|
||||
@ -160,7 +128,7 @@ pub(crate) fn subscribe_folder_trash_changed(
|
||||
}
|
||||
|
||||
let repeated_trash: RepeatedTrashPB = folder.get_my_trash_info().into();
|
||||
send_notification("trash", FolderNotification::DidUpdateTrash)
|
||||
folder_notification_builder("trash", FolderNotification::DidUpdateTrash)
|
||||
.payload(repeated_trash)
|
||||
.send();
|
||||
|
||||
@ -204,7 +172,7 @@ pub(crate) fn notify_parent_view_did_change<T: AsRef<str>>(
|
||||
|
||||
// Post the notification
|
||||
let parent_view_pb = view_pb_with_child_views(parent_view, child_views);
|
||||
send_notification(parent_view_id, FolderNotification::DidUpdateView)
|
||||
folder_notification_builder(parent_view_id, FolderNotification::DidUpdateView)
|
||||
.payload(parent_view_pb)
|
||||
.send();
|
||||
}
|
||||
@ -216,14 +184,14 @@ 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(workspace_id, folder);
|
||||
let private_views = get_workspace_private_view_pbs(workspace_id, folder);
|
||||
tracing::trace!(
|
||||
trace!(
|
||||
"Did update section views: public len = {}, private len = {}",
|
||||
public_views.len(),
|
||||
private_views.len()
|
||||
);
|
||||
|
||||
// Notify the public views
|
||||
send_notification(workspace_id, FolderNotification::DidUpdateSectionViews)
|
||||
folder_notification_builder(workspace_id, FolderNotification::DidUpdateSectionViews)
|
||||
.payload(SectionViewsPB {
|
||||
section: ViewSectionPB::Public,
|
||||
views: public_views,
|
||||
@ -231,7 +199,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde
|
||||
.send();
|
||||
|
||||
// Notify the private views
|
||||
send_notification(workspace_id, FolderNotification::DidUpdateSectionViews)
|
||||
folder_notification_builder(workspace_id, FolderNotification::DidUpdateSectionViews)
|
||||
.payload(SectionViewsPB {
|
||||
section: ViewSectionPB::Private,
|
||||
views: private_views,
|
||||
@ -241,7 +209,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(workspace_id, folder).into();
|
||||
send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
||||
folder_notification_builder(workspace_id, FolderNotification::DidUpdateWorkspaceViews)
|
||||
.payload(repeated_view)
|
||||
.send();
|
||||
}
|
||||
@ -249,7 +217,7 @@ pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) {
|
||||
fn notify_view_did_change(view: View) -> Option<()> {
|
||||
let view_id = view.id.clone();
|
||||
let view_pb = view_pb_without_child_views(view);
|
||||
send_notification(&view_id, FolderNotification::DidUpdateView)
|
||||
folder_notification_builder(&view_id, FolderNotification::DidUpdateView)
|
||||
.payload(view_pb)
|
||||
.send();
|
||||
None
|
||||
@ -282,7 +250,7 @@ pub(crate) fn notify_child_views_changed(view_pb: ViewPB, reason: ChildViewChang
|
||||
},
|
||||
}
|
||||
|
||||
send_notification(&parent_view_id, FolderNotification::DidUpdateChildViews)
|
||||
folder_notification_builder(&parent_view_id, FolderNotification::DidUpdateChildViews)
|
||||
.payload(payload)
|
||||
.send();
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
use crate::manager::{FolderManager, FolderUser};
|
||||
use crate::view_operation::FolderOperationHandlers;
|
||||
use collab::lock::RwLock;
|
||||
use collab_folder::Folder;
|
||||
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) -> Option<Arc<RwLock<Folder>>> {
|
||||
self.mutex_folder.load_full()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
@ -69,7 +69,7 @@ impl std::convert::From<i32> for FolderNotification {
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub(crate) fn send_notification(id: &str, ty: FolderNotification) -> NotificationBuilder {
|
||||
pub(crate) fn folder_notification_builder(id: &str, ty: FolderNotification) -> NotificationBuilder {
|
||||
NotificationBuilder::new(id, ty, FOLDER_OBSERVABLE_SOURCE)
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ pub(crate) fn send_notification(id: &str, ty: FolderNotification) -> Notificatio
|
||||
/// user. Only one workspace can be opened at a time.
|
||||
const CURRENT_WORKSPACE: &str = "current-workspace";
|
||||
pub(crate) fn send_current_workspace_notification<T: ToBytes>(ty: FolderNotification, payload: T) {
|
||||
send_notification(CURRENT_WORKSPACE, ty)
|
||||
folder_notification_builder(CURRENT_WORKSPACE, ty)
|
||||
.payload(payload)
|
||||
.send();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use collab_folder::ViewLayout;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ImportType {
|
||||
@ -10,16 +11,30 @@ pub enum ImportType {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImportValue {
|
||||
pub struct ImportItem {
|
||||
pub name: String,
|
||||
pub data: Option<Vec<u8>>,
|
||||
pub file_path: Option<String>,
|
||||
pub data: ImportData,
|
||||
pub view_layout: ViewLayout,
|
||||
pub import_type: ImportType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ImportData {
|
||||
FilePath { file_path: String },
|
||||
Bytes { bytes: Vec<u8> },
|
||||
}
|
||||
|
||||
impl Display for ImportData {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ImportData::FilePath { file_path } => write!(f, "file: {}", file_path),
|
||||
ImportData::Bytes { .. } => write!(f, "binary"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImportParams {
|
||||
pub parent_view_id: String,
|
||||
pub values: Vec<ImportValue>,
|
||||
pub items: Vec<ImportItem>,
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use tokio::sync::RwLock;
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
use crate::entities::{view_pb_with_child_views, ViewPB};
|
||||
use crate::view_operation::FolderOperationHandlers;
|
||||
use crate::view_operation::{FolderOperationHandler, FolderOperationHandlers};
|
||||
|
||||
pub struct DefaultFolderBuilder();
|
||||
impl DefaultFolderBuilder {
|
||||
@ -20,7 +20,19 @@ impl DefaultFolderBuilder {
|
||||
workspace_id.clone(),
|
||||
uid,
|
||||
)));
|
||||
for handler in handlers.values() {
|
||||
|
||||
// Collect all handlers from the DashMap into a vector.
|
||||
//
|
||||
// - `DashMap::iter()` returns references to the stored values, which are not `Send`
|
||||
// and can cause issues in an `async` context where thread-safety is required.
|
||||
// - By cloning the values into a `Vec`, we ensure they are owned and implement
|
||||
// `Send + Sync`, making them safe to use in asynchronous operations.
|
||||
// - This avoids lifetime conflicts and allows the handlers to be used in the
|
||||
// asynchronous loop without tying their lifetimes to the DashMap.
|
||||
//
|
||||
let handler_clones: Vec<Arc<dyn FolderOperationHandler + Send + Sync>> =
|
||||
handlers.iter().map(|entry| entry.value().clone()).collect();
|
||||
for handler in handler_clones {
|
||||
let _ = handler
|
||||
.create_workspace_view(uid, workspace_view_builder.clone())
|
||||
.await;
|
||||
|
@ -5,6 +5,7 @@ use collab_entity::CollabType;
|
||||
use collab_folder::hierarchy_builder::NestedViewBuilder;
|
||||
pub use collab_folder::View;
|
||||
use collab_folder::ViewLayout;
|
||||
use dashmap::DashMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
@ -18,7 +19,7 @@ use crate::manager::FolderUser;
|
||||
use crate::share::ImportType;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum EncodedCollabWrapper {
|
||||
pub enum EncodedCollabType {
|
||||
Document(DocumentEncodedCollab),
|
||||
Database(DatabaseEncodedCollab),
|
||||
Unknown,
|
||||
@ -43,7 +44,7 @@ pub type ImportedData = (String, CollabType, EncodedCollab);
|
||||
/// view layout. Each [ViewLayout] will have a handler. So when creating a new
|
||||
/// view, the [ViewLayout] will be used to get the handler.
|
||||
#[async_trait]
|
||||
pub trait FolderOperationHandler {
|
||||
pub trait FolderOperationHandler: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
/// Create the view for the workspace of new user.
|
||||
/// Only called once when the user is created.
|
||||
@ -70,9 +71,9 @@ pub trait FolderOperationHandler {
|
||||
/// get the encoded collab data from the disk.
|
||||
async fn get_encoded_collab_v1_from_disk(
|
||||
&self,
|
||||
_user: Arc<dyn FolderUser>,
|
||||
_user: &Arc<dyn FolderUser>,
|
||||
_view_id: &str,
|
||||
) -> Result<EncodedCollabWrapper, FlowyError> {
|
||||
) -> Result<EncodedCollabType, FlowyError> {
|
||||
Err(FlowyError::not_support())
|
||||
}
|
||||
|
||||
@ -103,9 +104,10 @@ pub trait FolderOperationHandler {
|
||||
/// Create a view with the pre-defined data.
|
||||
/// For example, the initial data of the grid/calendar/kanban board when
|
||||
/// you create a new view.
|
||||
async fn create_view_with_default_data(
|
||||
async fn create_default_view(
|
||||
&self,
|
||||
user_id: i64,
|
||||
parent_view_id: &str,
|
||||
view_id: &str,
|
||||
name: &str,
|
||||
layout: ViewLayout,
|
||||
@ -138,7 +140,7 @@ pub trait FolderOperationHandler {
|
||||
}
|
||||
|
||||
pub type FolderOperationHandlers =
|
||||
Arc<HashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>>>;
|
||||
Arc<DashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>>>;
|
||||
|
||||
impl From<ViewLayoutPB> for ViewLayout {
|
||||
fn from(pb: ViewLayoutPB) -> Self {
|
||||
|
@ -15,13 +15,4 @@ fn main() {
|
||||
flowy_codegen::Project::TauriApp,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "web_ts")]
|
||||
flowy_codegen::protobuf_file::ts_gen(
|
||||
env!("CARGO_PKG_NAME"),
|
||||
"notification",
|
||||
flowy_codegen::Project::Web {
|
||||
relative_path: "../../".to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use collab_folder::{folder_diff::FolderViewChange, View, ViewIcon, ViewIndexCont
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_search_pub::entities::{FolderIndexManager, IndexManager, IndexableData};
|
||||
use flowy_user::services::authenticate_user::AuthenticateUser;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use strsim::levenshtein;
|
||||
use tantivy::{
|
||||
collector::TopDocs, directory::MmapDirectory, doc, query::QueryParser, schema::Field, Document,
|
||||
@ -296,7 +296,7 @@ impl IndexManager for FolderIndexManagerImpl {
|
||||
fn set_index_content_receiver(&self, mut rx: IndexContentReceiver, workspace_id: String) {
|
||||
let indexer = self.clone();
|
||||
let wid = workspace_id.clone();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(msg) = rx.recv().await {
|
||||
match msg {
|
||||
IndexContent::Create(value) => match serde_json::from_value::<ViewIndexContent>(value) {
|
||||
|
@ -4,7 +4,7 @@ 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 lib_infra::async_trait::async_trait;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
@ -48,7 +48,7 @@ impl SearchManager {
|
||||
|
||||
// Initialize Search Notifier
|
||||
let (notifier, _) = broadcast::channel(100);
|
||||
af_spawn(SearchResultReceiverRunner(Some(notifier.subscribe())).run());
|
||||
tokio::spawn(SearchResultReceiverRunner(Some(notifier.subscribe())).run());
|
||||
|
||||
Self { handlers, notifier }
|
||||
}
|
||||
@ -71,7 +71,7 @@ impl SearchManager {
|
||||
let ch = channel.clone();
|
||||
let notifier = self.notifier.clone();
|
||||
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
let res = handler.perform_search(q.clone(), f).await;
|
||||
|
||||
let items = res.unwrap_or_default();
|
||||
|
@ -5,8 +5,8 @@ use client_api::entity::chat_dto::{
|
||||
RepeatedChatMessage,
|
||||
};
|
||||
use flowy_ai_pub::cloud::{
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, LocalAIConfig, StreamAnswer,
|
||||
StreamComplete, SubscriptionPlan,
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, LocalAIConfig,
|
||||
StreamAnswer, StreamComplete, SubscriptionPlan, UpdateChatParams,
|
||||
};
|
||||
use flowy_error::FlowyError;
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
@ -30,13 +30,14 @@ where
|
||||
_uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let chat_id = chat_id.to_string();
|
||||
let try_get_client = self.inner.try_get_client();
|
||||
let params = CreateChatParams {
|
||||
chat_id,
|
||||
name: "".to_string(),
|
||||
rag_ids: vec![],
|
||||
rag_ids,
|
||||
};
|
||||
try_get_client?
|
||||
.create_chat(workspace_id, params)
|
||||
@ -216,4 +217,31 @@ where
|
||||
.await?;
|
||||
Ok(plans)
|
||||
}
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<ChatSettings, FlowyError> {
|
||||
let settings = self
|
||||
.inner
|
||||
.try_get_client()?
|
||||
.get_chat_settings(workspace_id, chat_id)
|
||||
.await?;
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.inner
|
||||
.try_get_client()?
|
||||
.update_chat_settings(workspace_id, chat_id, params)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_storage_pub::cloud::StorageCloudService;
|
||||
use flowy_user_pub::cloud::{UserCloudService, UserUpdate};
|
||||
use flowy_user_pub::entities::UserTokenState;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use rand::Rng;
|
||||
use semver::Version;
|
||||
use tokio::select;
|
||||
@ -132,7 +132,7 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
let mut token_state_rx = self.client.subscribe_token_state();
|
||||
let (watch_tx, watch_rx) = watch::channel(UserTokenState::Init);
|
||||
let weak_client = Arc::downgrade(&self.client);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(token_state) = token_state_rx.recv().await {
|
||||
if let Some(client) = weak_client.upgrade() {
|
||||
match token_state {
|
||||
@ -172,7 +172,7 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
};
|
||||
let mut user_change = self.ws_client.subscribe_user_changed();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(1);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(user_message) = user_change.recv().await {
|
||||
if let UserMessage::ProfileChange(change) = user_message {
|
||||
let user_update = UserUpdate {
|
||||
@ -302,7 +302,7 @@ fn spawn_ws_conn(
|
||||
let cancellation_token = Arc::new(ArcSwap::new(Arc::new(CancellationToken::new())));
|
||||
let cloned_cancellation_token = cancellation_token.clone();
|
||||
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
if let Some(ws_client) = weak_ws_client.upgrade() {
|
||||
let mut state_recv = ws_client.subscribe_connect_state();
|
||||
while let Ok(state) = state_recv.recv().await {
|
||||
@ -331,7 +331,7 @@ fn spawn_ws_conn(
|
||||
});
|
||||
|
||||
let weak_ws_client = Arc::downgrade(ws_client);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Ok(token_state) = token_state_rx.recv().await {
|
||||
info!("🟢token state: {:?}", token_state);
|
||||
match token_state {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use client_api::entity::ai_dto::{CompletionType, LocalAIConfig, RepeatedRelatedQuestion};
|
||||
use flowy_ai_pub::cloud::{
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, MessageCursor,
|
||||
RepeatedChatMessage, StreamAnswer, StreamComplete, SubscriptionPlan,
|
||||
ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, MessageCursor,
|
||||
RepeatedChatMessage, StreamAnswer, StreamComplete, SubscriptionPlan, UpdateChatParams,
|
||||
};
|
||||
use flowy_error::FlowyError;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
@ -18,6 +18,7 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
|
||||
_uid: &i64,
|
||||
_workspace_id: &str,
|
||||
_chat_id: &str,
|
||||
_rag_ids: Vec<String>,
|
||||
) -> Result<(), FlowyError> {
|
||||
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
|
||||
}
|
||||
@ -116,4 +117,21 @@ impl ChatCloudService for DefaultChatCloudServiceImpl {
|
||||
.with_context("Get local ai config is not supported in local server."),
|
||||
)
|
||||
}
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
_workspace_id: &str,
|
||||
_chat_id: &str,
|
||||
) -> Result<ChatSettings, FlowyError> {
|
||||
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
|
||||
}
|
||||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
_workspace_id: &str,
|
||||
_chat_id: &str,
|
||||
_params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
Err(FlowyError::not_support().with_context("Chat is not supported in local server."))
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::Error;
|
||||
use bytes::Bytes;
|
||||
use reqwest::{Response, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use lib_infra::future::{to_fut, Fut};
|
||||
use flowy_error::ErrorCode;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct HttpResponse {
|
||||
@ -34,116 +30,3 @@ impl fmt::Display for HttpError {
|
||||
write!(f, "{:?}: {}", self.code, self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait `ExtendedResponse` provides an extension method to handle and transform the response data.
|
||||
///
|
||||
/// This trait introduces a single method:
|
||||
///
|
||||
/// - `get_value`: It extracts the value from the response, and returns it as an instance of a type `T`.
|
||||
/// This method will return an error if the status code of the response signifies a failure (not success).
|
||||
/// Otherwise, it attempts to parse the response body into an instance of type `T`, which must implement
|
||||
/// `serde::de::DeserializeOwned`, `Send`, `Sync`, and have a static lifetime ('static).
|
||||
pub trait ExtendedResponse {
|
||||
/// Returns the value of the response as a Future of `Result<T, Error>`.
|
||||
///
|
||||
/// If the status code of the response is not a success, returns an `Error`.
|
||||
/// Otherwise, attempts to parse the response into an instance of type `T`.
|
||||
///
|
||||
/// # Type Parameters
|
||||
///
|
||||
/// * `T`: The type of the value to be returned. Must implement `serde::de::DeserializeOwned`,
|
||||
/// `Send`, `Sync`, and have a static lifetime ('static).
|
||||
fn get_value<T>(self) -> Fut<Result<T, Error>>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + Send + Sync + 'static;
|
||||
|
||||
fn get_bytes(self) -> Fut<Result<Bytes, Error>>;
|
||||
|
||||
fn get_json(self) -> Fut<Result<Value, Error>>;
|
||||
|
||||
fn success(self) -> Fut<Result<(), Error>>;
|
||||
|
||||
fn success_with_body(self) -> Fut<Result<String, Error>>;
|
||||
}
|
||||
|
||||
impl ExtendedResponse for Response {
|
||||
fn get_value<T>(self) -> Fut<Result<T, Error>>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + Send + Sync + 'static,
|
||||
{
|
||||
to_fut(async move {
|
||||
let status_code = self.status();
|
||||
if !status_code.is_success() {
|
||||
return Err(parse_response_as_error(self).await.into());
|
||||
}
|
||||
let bytes = self.bytes().await?;
|
||||
let value = serde_json::from_slice(&bytes).map_err(|e| {
|
||||
FlowyError::new(
|
||||
ErrorCode::Serde,
|
||||
format!(
|
||||
"failed to parse json: {}, body: {}",
|
||||
e,
|
||||
String::from_utf8_lossy(&bytes)
|
||||
),
|
||||
)
|
||||
})?;
|
||||
Ok(value)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_bytes(self) -> Fut<Result<Bytes, Error>> {
|
||||
to_fut(async move {
|
||||
let status_code = self.status();
|
||||
if !status_code.is_success() {
|
||||
return Err(parse_response_as_error(self).await.into());
|
||||
}
|
||||
let bytes = self.bytes().await?;
|
||||
Ok(bytes)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_json(self) -> Fut<Result<Value, Error>> {
|
||||
to_fut(async move {
|
||||
if !self.status().is_success() {
|
||||
return Err(parse_response_as_error(self).await.into());
|
||||
}
|
||||
let bytes = self.bytes().await?;
|
||||
let value = serde_json::from_slice::<Value>(&bytes)?;
|
||||
Ok(value)
|
||||
})
|
||||
}
|
||||
|
||||
fn success(self) -> Fut<Result<(), Error>> {
|
||||
to_fut(async move {
|
||||
if !self.status().is_success() {
|
||||
return Err(parse_response_as_error(self).await.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn success_with_body(self) -> Fut<Result<String, Error>> {
|
||||
to_fut(async move {
|
||||
if !self.status().is_success() {
|
||||
return Err(parse_response_as_error(self).await.into());
|
||||
}
|
||||
Ok(self.text().await?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn parse_response_as_error(response: Response) -> FlowyError {
|
||||
let status_code = response.status();
|
||||
let msg = response.text().await.unwrap_or_default();
|
||||
if status_code == StatusCode::CONFLICT {
|
||||
return FlowyError::new(ErrorCode::Conflict, msg);
|
||||
}
|
||||
|
||||
FlowyError::new(
|
||||
ErrorCode::HttpError,
|
||||
format!(
|
||||
"expected status code 2XX, but got {}, body: {}",
|
||||
status_code, msg
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use tokio::sync::oneshot::channel;
|
||||
|
||||
use flowy_database_pub::cloud::{CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot};
|
||||
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
@ -37,7 +36,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let object_id = object_id.to_string();
|
||||
let (tx, rx) = channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -60,7 +59,7 @@ where
|
||||
) -> FutureResult<CollabDocStateByOid, Error> {
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let (tx, rx) = channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
|
@ -8,7 +8,7 @@ use tokio::sync::oneshot::channel;
|
||||
|
||||
use flowy_document_pub::cloud::{DocumentCloudService, DocumentSnapshot};
|
||||
use flowy_error::FlowyError;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
use crate::supabase::api::request::{get_snapshots_from_server, FetchObjectUpdateAction};
|
||||
@ -37,7 +37,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let document_id = document_id.to_string();
|
||||
let (tx, rx) = channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -87,7 +87,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let document_id = document_id.to_string();
|
||||
let (tx, rx) = channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
|
@ -14,7 +14,7 @@ use flowy_folder_pub::cloud::{
|
||||
Workspace, WorkspaceRecord,
|
||||
};
|
||||
use flowy_folder_pub::entities::PublishPayload;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use lib_infra::future::FutureResult;
|
||||
use lib_infra::util::timestamp;
|
||||
|
||||
@ -144,7 +144,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let object_id = object_id.to_string();
|
||||
let (tx, rx) = channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
|
@ -21,7 +21,7 @@ use flowy_folder_pub::cloud::{Folder, FolderData, Workspace};
|
||||
use flowy_user_pub::cloud::*;
|
||||
use flowy_user_pub::entities::*;
|
||||
use flowy_user_pub::DEFAULT_USER_NAME;
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use lib_infra::box_any::BoxAny;
|
||||
use lib_infra::future::FutureResult;
|
||||
|
||||
@ -271,7 +271,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let (tx, rx) = channel();
|
||||
let object_id = object_id.to_string();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -313,7 +313,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let (tx, rx) = channel();
|
||||
let init_update = default_workspace_doc_state(&collab_object);
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?
|
||||
@ -351,7 +351,7 @@ where
|
||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||
let cloned_collab_object = collab_object.clone();
|
||||
let (tx, rx) = channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
tx.send(
|
||||
async move {
|
||||
CreateCollabAction::new(cloned_collab_object, try_get_postgrest?, data)
|
||||
|
@ -51,6 +51,7 @@ pub trait PragmaExtension: ConnectionExtension {
|
||||
self.query::<ST, T>(&query)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pragma_get<'query, ST, T>(&mut self, key: &str, schema: Option<&str>) -> Result<T>
|
||||
where
|
||||
SqlLiteral<ST>: LoadQuery<'query, SqliteConnection, T>,
|
||||
@ -64,10 +65,12 @@ pub trait PragmaExtension: ConnectionExtension {
|
||||
self.query::<ST, T>(&query)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pragma_set_busy_timeout(&mut self, timeout_ms: i32) -> Result<i32> {
|
||||
self.pragma_ret::<Integer, i32, i32>("busy_timeout", timeout_ms, None)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pragma_get_busy_timeout(&mut self) -> Result<i32> {
|
||||
self.pragma_get::<Integer, i32>("busy_timeout", None)
|
||||
}
|
||||
@ -80,12 +83,14 @@ pub trait PragmaExtension: ConnectionExtension {
|
||||
self.pragma_ret::<Integer, i32, SQLiteJournalMode>("journal_mode", mode, schema)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pragma_get_journal_mode(&mut self, schema: Option<&str>) -> Result<SQLiteJournalMode> {
|
||||
self
|
||||
.pragma_get::<Text, String>("journal_mode", schema)?
|
||||
.parse()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pragma_set_synchronous(
|
||||
&mut self,
|
||||
synchronous: SQLiteSynchronous,
|
||||
@ -94,6 +99,7 @@ pub trait PragmaExtension: ConnectionExtension {
|
||||
self.pragma("synchronous", synchronous as u8, schema)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn pragma_get_synchronous(&mut self, schema: Option<&str>) -> Result<SQLiteSynchronous> {
|
||||
self
|
||||
.pragma_get::<Integer, i32>("synchronous", schema)?
|
||||
|
@ -107,7 +107,7 @@ pub async fn get_user_profile_handler(
|
||||
let cloned_user_profile = user_profile.clone();
|
||||
|
||||
// Refresh the user profile in the background
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
if let Some(manager) = weak_manager.upgrade() {
|
||||
let _ = manager.refresh_user_profile(&cloned_user_profile).await;
|
||||
}
|
||||
@ -274,7 +274,7 @@ pub async fn import_appflowy_data_folder_handler(
|
||||
) -> Result<(), FlowyError> {
|
||||
let data = data.try_into_inner()?;
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
let result = async {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let imported_folder = prepare_import(
|
||||
|
@ -347,7 +347,10 @@ pub(crate) fn generate_import_data(
|
||||
invalid_orphan_views
|
||||
.iter_mut()
|
||||
.for_each(|parent_child_views| {
|
||||
parent_child_views.view.parent_view_id = other_view_id.clone();
|
||||
parent_child_views
|
||||
.view
|
||||
.parent_view_id
|
||||
.clone_from(&other_view_id);
|
||||
});
|
||||
let mut other_view = create_new_container_view(
|
||||
current_session,
|
||||
@ -364,7 +367,10 @@ pub(crate) fn generate_import_data(
|
||||
views.push(other_view);
|
||||
} else {
|
||||
let first_view = views.first_mut().unwrap();
|
||||
other_view.view.parent_view_id = first_view.view.id.clone();
|
||||
other_view
|
||||
.view
|
||||
.parent_view_id
|
||||
.clone_from(&first_view.view.id);
|
||||
first_view.children.push(other_view);
|
||||
}
|
||||
}
|
||||
@ -1250,7 +1256,7 @@ pub async fn upload_collab_objects_data(
|
||||
|
||||
// Spawn a new task to upload the collab objects data in the background. If the
|
||||
// upload fails, we will retry the upload later.
|
||||
// af_spawn(async move {
|
||||
// tokio::spawn(async move {
|
||||
if !objects.is_empty() {
|
||||
batch_create(
|
||||
uid,
|
||||
|
@ -15,7 +15,7 @@ use flowy_sqlite::{
|
||||
DBConnection, Database, ExpressionMethods,
|
||||
};
|
||||
use flowy_user_pub::entities::{UserProfile, UserWorkspace};
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
|
||||
use lib_infra::file_util::{unzip_and_replace, zip_folder};
|
||||
use tracing::{error, event, info, instrument};
|
||||
|
||||
@ -60,7 +60,7 @@ impl UserDB {
|
||||
if is_ok {
|
||||
// If database is valid, update the shared map and initiate backup.
|
||||
// Asynchronous backup operation.
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
if let Err(err) = tokio::task::spawn_blocking(move || zip_backup.backup()).await {
|
||||
error!("Backup of collab db failed: {:?}", err);
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ use tokio::sync::Mutex;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::{debug, error, event, info, instrument, warn};
|
||||
|
||||
use lib_dispatch::prelude::af_spawn;
|
||||
use lib_infra::box_any::BoxAny;
|
||||
|
||||
use crate::entities::{AuthStateChangedPB, AuthStatePB, UserProfilePB, UserSettingPB};
|
||||
@ -91,7 +90,7 @@ impl UserManager {
|
||||
let weak_user_manager = Arc::downgrade(&user_manager);
|
||||
if let Ok(user_service) = user_manager.cloud_services.get_user_service() {
|
||||
if let Some(mut rx) = user_service.subscribe_user_update() {
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Some(update) = rx.recv().await {
|
||||
if let Some(user_manager) = weak_user_manager.upgrade() {
|
||||
if let Err(err) = user_manager.handler_user_update(update).await {
|
||||
@ -184,7 +183,7 @@ impl UserManager {
|
||||
event!(tracing::Level::DEBUG, "Listen token state change");
|
||||
let user_uid = user.uid;
|
||||
let local_token = user.token.clone();
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
while let Some(token_state) = token_state_rx.next().await {
|
||||
debug!("Token state changed: {:?}", token_state);
|
||||
match token_state {
|
||||
@ -678,7 +677,7 @@ impl UserManager {
|
||||
params: UpdateUserProfileParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.cloud_services.get_user_service()?;
|
||||
af_spawn(async move {
|
||||
tokio::spawn(async move {
|
||||
let credentials = UserCredentials::new(Some(token), Some(uid), None);
|
||||
server.update_user(credentials, params).await
|
||||
})
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user