Skip to content

Commit 69469e9

Browse files
authored
feat: Import appflowy data (#4236)
* refactor: traits * feat: import data * chore: track database view * fix: import * refactor: collab doc state * refactor: get collab doc state * feat: batch create collab object * fix: test * ci: run docker compose if the server is not up * chore: bump collab * chore: update ci * chore: update ci * chore: update ci * chore: implement ui * chore: implement ui * chore: implement ui
1 parent c821b8c commit 69469e9

File tree

100 files changed

+2727
-885
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+2727
-885
lines changed

.github/workflows/flutter_ci.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ jobs:
253253
- name: Run Docker-Compose
254254
working-directory: AppFlowy-Cloud
255255
run: |
256+
#docker compose down -v --remove-orphans
256257
docker compose up -d
257258
258259
- name: Checkout source code

.github/workflows/rust_ci.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ jobs:
9090
- name: Run Docker-Compose
9191
working-directory: AppFlowy-Cloud
9292
run: |
93-
docker compose down -v --remove-orphans
9493
docker compose up -d
9594
9695
- name: Run rust-lib tests
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:appflowy_backend/dispatch/dispatch.dart';
2+
import 'package:appflowy_backend/log.dart';
3+
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
4+
import 'package:appflowy_backend/protobuf/flowy-folder2/import.pb.dart';
5+
import 'package:dartz/dartz.dart';
6+
import 'package:flutter_bloc/flutter_bloc.dart';
7+
import 'package:freezed_annotation/freezed_annotation.dart';
8+
9+
part 'setting_file_importer_bloc.freezed.dart';
10+
11+
class SettingFileImporterBloc
12+
extends Bloc<SettingFileImportEvent, SettingFileImportState> {
13+
SettingFileImporterBloc() : super(SettingFileImportState.initial()) {
14+
on<SettingFileImportEvent>((event, emit) async {
15+
await event.when(
16+
importAppFlowyDataFolder: (String path) async {
17+
final payload = ImportAppFlowyDataPB.create()..path = path;
18+
final result =
19+
await FolderEventImportAppFlowyDataFolder(payload).send();
20+
result.fold(
21+
(l) {
22+
emit(
23+
state.copyWith(
24+
successOrFail: some(left(unit)),
25+
),
26+
);
27+
},
28+
(err) {
29+
Log.error(err);
30+
emit(
31+
state.copyWith(
32+
successOrFail: some(right(err)),
33+
),
34+
);
35+
},
36+
);
37+
},
38+
);
39+
});
40+
}
41+
}
42+
43+
@freezed
44+
class SettingFileImportEvent with _$SettingFileImportEvent {
45+
const factory SettingFileImportEvent.importAppFlowyDataFolder(String path) =
46+
_ImportAppFlowyDataFolder;
47+
}
48+
49+
@freezed
50+
class SettingFileImportState with _$SettingFileImportState {
51+
const factory SettingFileImportState({
52+
required Option<Either<Unit, FlowyError>> successOrFail,
53+
}) = _SettingFileImportState;
54+
55+
factory SettingFileImportState.initial() => SettingFileImportState(
56+
successOrFail: none(),
57+
);
58+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import 'package:appflowy/generated/locale_keys.g.dart';
2+
import 'package:appflowy/startup/startup.dart';
3+
import 'package:appflowy/workspace/application/settings/setting_file_importer_bloc.dart';
4+
import 'package:appflowy/workspace/presentation/home/toast.dart';
5+
import 'package:easy_localization/easy_localization.dart';
6+
import 'package:flowy_infra/file_picker/file_picker_service.dart';
7+
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
8+
import 'package:flutter/material.dart';
9+
import 'package:flutter_bloc/flutter_bloc.dart';
10+
import 'package:fluttertoast/fluttertoast.dart';
11+
12+
class ImportAppFlowyData extends StatefulWidget {
13+
const ImportAppFlowyData({super.key});
14+
15+
@override
16+
State<ImportAppFlowyData> createState() => _ImportAppFlowyDataState();
17+
}
18+
19+
class _ImportAppFlowyDataState extends State<ImportAppFlowyData> {
20+
final _fToast = FToast();
21+
@override
22+
void initState() {
23+
super.initState();
24+
_fToast.init(context);
25+
}
26+
27+
@override
28+
Widget build(BuildContext context) {
29+
return BlocProvider(
30+
create: (context) => SettingFileImporterBloc(),
31+
child: BlocListener<SettingFileImporterBloc, SettingFileImportState>(
32+
listener: (context, state) {
33+
state.successOrFail.fold(
34+
() {},
35+
(either) {
36+
either.fold(
37+
(unit) {
38+
_showToast(LocaleKeys.settings_menu_importSuccess.tr());
39+
},
40+
(err) {
41+
_showToast(LocaleKeys.settings_menu_importFailed.tr());
42+
},
43+
);
44+
},
45+
);
46+
},
47+
child: BlocBuilder<SettingFileImporterBloc, SettingFileImportState>(
48+
builder: (context, state) {
49+
return Column(
50+
children: [
51+
const ImportAppFlowyDataButton(),
52+
const VSpace(6),
53+
IntrinsicHeight(
54+
child: Opacity(
55+
opacity: 0.6,
56+
child: FlowyText.medium(
57+
LocaleKeys.settings_menu_importAppFlowyDataDescription
58+
.tr(),
59+
maxLines: 13,
60+
),
61+
),
62+
),
63+
],
64+
);
65+
},
66+
),
67+
),
68+
);
69+
}
70+
71+
void _showToast(String message) {
72+
_fToast.showToast(
73+
child: FlowyMessageToast(message: message),
74+
gravity: ToastGravity.CENTER,
75+
);
76+
}
77+
}
78+
79+
class ImportAppFlowyDataButton extends StatefulWidget {
80+
const ImportAppFlowyDataButton({super.key});
81+
82+
@override
83+
State<ImportAppFlowyDataButton> createState() =>
84+
_ImportAppFlowyDataButtonState();
85+
}
86+
87+
class _ImportAppFlowyDataButtonState extends State<ImportAppFlowyDataButton> {
88+
@override
89+
Widget build(BuildContext context) {
90+
return SizedBox(
91+
height: 40,
92+
child: FlowyButton(
93+
text: FlowyText(LocaleKeys.settings_menu_importAppFlowyData.tr()),
94+
onTap: () async {
95+
final path = await getIt<FilePickerService>().getDirectoryPath();
96+
if (path == null) {
97+
return;
98+
}
99+
if (!mounted) {
100+
return;
101+
}
102+
103+
context
104+
.read<SettingFileImporterBloc>()
105+
.add(SettingFileImportEvent.importAppFlowyDataFolder(path));
106+
},
107+
),
108+
);
109+
}
110+
}

frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_system_view.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:appflowy/workspace/presentation/settings/widgets/setting_file_import_appflowy_data_view.dart';
12
import 'package:appflowy/workspace/presentation/settings/widgets/settings_export_file_widget.dart';
23
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart';
34
import 'package:flutter/foundation.dart';
@@ -17,6 +18,7 @@ class _SettingsFileSystemViewState extends State<SettingsFileSystemView> {
1718
const SettingsFileLocationCustomizer(),
1819
// disable export data for v0.2.0 in release mode.
1920
if (kDebugMode) const SettingsExportFileWidget(),
21+
const ImportAppFlowyData(),
2022
];
2123

2224
@override

frontend/appflowy_tauri/src-tauri/Cargo.lock

Lines changed: 19 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/appflowy_tauri/src-tauri/Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "a45
6767
# To switch to the local path, run:
6868
# scripts/tool/update_collab_source.sh
6969
# ⚠️⚠️⚠️️
70-
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
71-
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
72-
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
73-
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
74-
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
75-
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
76-
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
77-
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "c74b3c3fe500305cc114a06fce911be9269b61bd" }
70+
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
71+
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
72+
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
73+
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
74+
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
75+
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
76+
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
77+
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bdc26b9a37399c9bc02e2309c54e31c664a9574d" }
7878

7979

8080

frontend/resources/translations/en.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,11 @@
299299
"historicalUserList": "User login history",
300300
"historicalUserListTooltip": "This list displays your anonymous accounts. You can click on an account to view its details. Anonymous accounts are created by clicking the 'Get Started' button",
301301
"openHistoricalUser": "Click to open the anonymous account",
302-
"customPathPrompt": "Storing the AppFlowy data folder in a cloud-synced folder such as Google Drive can pose risks. If the database within this folder is accessed or modified from multiple locations at the same time, it may result in synchronization conflicts and potential data corruption"
302+
"customPathPrompt": "Storing the AppFlowy data folder in a cloud-synced folder such as Google Drive can pose risks. If the database within this folder is accessed or modified from multiple locations at the same time, it may result in synchronization conflicts and potential data corruption",
303+
"importAppFlowyData": "Import Data from External AppFlowy Folder",
304+
"importAppFlowyDataDescription": "Copy data from an external AppFlowy data folder and import it into the current AppFlowy data folder",
305+
"importSuccess": "Successfully imported the AppFlowy data folder",
306+
"importFailed": "Importing the AppFlowy data folder failed"
303307
},
304308
"notifications": {
305309
"enableNotifications": {

0 commit comments

Comments
 (0)