tests: more cloud test (#4204)

* test: add anon user test

* chore: add to runner

* test: fix

* test: fix

* test: add tests

* chore: add test

* chore: fix warn

* chore: fix warn

* fix: build

* fix: test

* chore: rename

* chore: fix test

* chore: fix test

* chore: fix test

* chore: disable test
This commit is contained in:
Nathan.fooo 2023-12-26 02:03:42 +08:00 committed by GitHub
parent 9b55f5bc7f
commit a49b009980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 776 additions and 450 deletions

View File

@ -68,6 +68,31 @@ jobs:
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
- name: Checkout appflowy cloud code
uses: actions/checkout@v3
with:
repository: AppFlowy-IO/AppFlowy-Cloud
path: AppFlowy-Cloud
depth: 1
- name: Prepare appflowy cloud env
working-directory: AppFlowy-Cloud
run: |
# log level
cp dev.env .env
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i 's/GOTRUE_SMTP_USER=.*/GOTRUE_SMTP_USER=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_USER }}/' .env
sed -i 's/GOTRUE_SMTP_PASS=.*/GOTRUE_SMTP_PASS=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_PASS }}/' .env
sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_ADMIN_EMAIL }}/' .env
sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
cat .env
- name: Run Docker-Compose
working-directory: AppFlowy-Cloud
run: |
docker compose up -d
- name: Run rust-lib tests - name: Run rust-lib tests
working-directory: frontend/rust-lib working-directory: frontend/rust-lib
run: RUST_LOG=info RUST_BACKTRACE=1 cargo test --no-default-features --features="rev-sqlite" run: RUST_LOG=info RUST_BACKTRACE=1 cargo test --no-default-features --features="rev-sqlite"

View File

@ -16,7 +16,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.appearance); await tester.openSettingsPage(SettingsPage.appearance);
@ -48,7 +48,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.appearance); await tester.openSettingsPage(SettingsPage.appearance);

View File

@ -0,0 +1,68 @@
// ignore_for_file: unused_import
import 'dart:io';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p;
import 'package:integration_test/integration_test.dart';
import '../util/dir.dart';
import '../util/mock/mock_file_picker.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('appflowy cloud', () {
testWidgets('anon user and then sign in', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloud,
);
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
await tester.tapGoButton();
await tester.expectToSeeHomePage();
// reanme the name of the anon user
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameFinder = find.descendant(
of: find.byType(SettingsUserView),
matching: find.byType(UserNameInput),
);
await tester.enterText(userNameFinder, 'local_user');
await tester.pumpAndSettle();
// sign up with Google
await tester.tapGoogleLoginInButton();
await tester.pumpAndSettle();
// sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.logout();
await tester.pumpAndSettle();
// tap the continue as anonymous button
await tester
.tapButton(find.text(LocaleKeys.signIn_continueAnonymousUser.tr()));
await tester.expectToSeeHomePage();
// assert the name of the anon user is local_user
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameInput = tester.widget(userNameFinder) as UserNameInput;
expect(userNameInput.name, 'local_user');
});
});
}

View File

@ -0,0 +1,74 @@
// ignore_for_file: unused_import
import 'dart:io';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/sign_in_anonymous_button.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p;
import 'package:integration_test/integration_test.dart';
import '../util/database_test_op.dart';
import '../util/dir.dart';
import '../util/mock/mock_file_picker.dart';
import '../util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('appflowy cloud', () {
testWidgets('anon user -> af cloud -> continue anon', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloud,
);
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
await tester.tapGoButton();
await tester.expectToSeeHomePage();
// reanme the name of the anon user
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameFinder = find.descendant(
of: find.byType(SettingsUserView),
matching: find.byType(UserNameInput),
);
await tester.enterText(userNameFinder, 'local_user');
await tester.openSettingsPage(SettingsPage.user);
await tester.tapEscButton();
await tester.expectToSeeHomePage();
// sign up with Google
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePage();
// sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.logout();
await tester.pumpAndSettle();
tester.expectToSeeText(LocaleKeys.signIn_continueAnonymousUser.tr());
// tap the continue as anonymous button
await tester.tapButton(find.byType(SignInAnonymousButton));
await tester.expectToSeeHomePage();
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameInput = tester.widget(userNameFinder) as UserNameInput;
expect(userNameInput.name, 'local_user');
});
});
}

View File

@ -25,7 +25,7 @@ void main() {
cloudType: AuthenticatorType.appflowyCloud, cloudType: AuthenticatorType.appflowyCloud,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
}); });
testWidgets('sign out', (tester) async { testWidgets('sign out', (tester) async {

View File

@ -1,14 +1,17 @@
import 'empty_test.dart' as empty_test; import 'empty_test.dart' as preset_af_cloud_env_test;
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test; import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
import 'document_sync_test.dart' as document_sync_test; import 'document_sync_test.dart' as document_sync_test;
import 'user_setting_sync_test.dart' as user_sync_test; import 'user_setting_sync_test.dart' as user_sync_test;
// import 'anon_user_continue_test.dart' as anon_user_continue_test;
Future<void> main() async { Future<void> main() async {
empty_test.main(); preset_af_cloud_env_test.main();
appflowy_cloud_auth_test.main(); appflowy_cloud_auth_test.main();
document_sync_test.main(); document_sync_test.main();
user_sync_test.main(); user_sync_test.main();
// anon_user_continue_test.main();
} }

View File

@ -35,7 +35,7 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
// create a new document called Sample // create a new document called Sample
await tester.createNewPageWithName( await tester.createNewPageWithName(
@ -52,8 +52,7 @@ void main() {
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
await tester.tapButton(find.byType(SettingLogoutButton)); await tester.logout();
tester.expectToSeeText(LocaleKeys.button_ok.tr());
}); });
testWidgets('sync doc from server', (tester) async { testWidgets('sync doc from server', (tester) async {
@ -62,7 +61,7 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.pumpAndSettle(const Duration(seconds: 2)); await tester.pumpAndSettle(const Duration(seconds: 2));
// The document will be synced from the server // The document will be synced from the server

View File

@ -15,7 +15,7 @@ void main() {
testWidgets('sign in with supabase', (tester) async { testWidgets('sign in with supabase', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase); await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
}); });
testWidgets('sign out with supabase', (tester) async { testWidgets('sign out with supabase', (tester) async {

View File

@ -36,7 +36,7 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user); await tester.openSettingsPage(SettingsPage.user);
@ -74,7 +74,7 @@ void main() {
email: email, email: email,
); );
await tester.tapGoogleLoginInButton(); await tester.tapGoogleLoginInButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.openSettings(); await tester.openSettings();

View File

@ -19,7 +19,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.appearance); await tester.openSettingsPage(SettingsPage.appearance);
@ -71,7 +71,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.pumpAndSettle(); await tester.pumpAndSettle();

View File

@ -14,7 +14,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
await tester.openSettings(); await tester.openSettings();
await tester.openSettingsPage(SettingsPage.language); await tester.openSettingsPage(SettingsPage.language);

View File

@ -33,7 +33,7 @@ void main() {
); );
await tester.tapGoButton(); await tester.tapGoButton();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
// switch to user B // switch to user B
{ {
@ -51,7 +51,7 @@ void main() {
); );
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
// set user name for userB // set user name for userB
await tester.openSettings(); await tester.openSettings();
@ -71,7 +71,7 @@ void main() {
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
tester.expectToSeeUserName(userA); tester.expectToSeeUserName(userA);
} }
@ -88,7 +88,7 @@ void main() {
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
tester.expectToSeeUserName(userB); tester.expectToSeeUserName(userB);
} }
}); });
@ -99,7 +99,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// home and readme document // home and readme document
tester.expectToSeeHomePage(); await tester.expectToSeeHomePage();
// open settings and restore the location // open settings and restore the location
await tester.openSettings(); await tester.openSettings();

View File

@ -1,16 +1,27 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart'; import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'base.dart'; import 'base.dart';
import 'expectation.dart';
extension AppFlowyAuthTest on WidgetTester { extension AppFlowyAuthTest on WidgetTester {
Future<void> tapGoogleLoginInButton() async { Future<void> tapGoogleLoginInButton() async {
await tapButton(find.byKey(const Key('signInWithGoogleButton'))); await tapButton(find.byKey(const Key('signInWithGoogleButton')));
} }
Future<void> logout() async {
await tapButton(find.byType(SettingLogoutButton));
expectToSeeText(LocaleKeys.button_ok.tr());
await tapButtonWithName(LocaleKeys.button_ok.tr());
}
Future<void> tapSignInAsGuest() async { Future<void> tapSignInAsGuest() async {
await tapButton(find.byType(SignInAnonymousButton)); await tapButton(find.byType(SignInAnonymousButton));
} }

View File

@ -15,7 +15,6 @@ import 'package:dartz/dartz.dart';
import 'package:flowy_infra/uuid.dart'; import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
@ -33,21 +32,24 @@ extension AppFlowyTestBase on WidgetTester {
Future<FlowyTestContext> initializeAppFlowy({ Future<FlowyTestContext> initializeAppFlowy({
// use to append after the application data directory // use to append after the application data directory
String? pathExtension, String? pathExtension,
// use to specify the application data directory, if not specified, a temporary directory will be used.
String? dataDirectory,
Size windowsSize = const Size(1600, 1200), Size windowsSize = const Size(1600, 1200),
AuthenticatorType? cloudType, AuthenticatorType? cloudType,
String? email, String? email,
}) async { }) async {
// view.physicalSize = windowsSize;
binding.setSurfaceSize(windowsSize); binding.setSurfaceSize(windowsSize);
// addTearDown(() => binding.setSurfaceSize(null));
mockHotKeyManagerHandlers(); mockHotKeyManagerHandlers();
final directory = await mockApplicationDataStorage( final applicationDataDirectory = dataDirectory ??
await mockApplicationDataStorage(
pathExtension: pathExtension, pathExtension: pathExtension,
); );
WidgetsFlutterBinding.ensureInitialized();
await FlowyRunner.run( await FlowyRunner.run(
FlowyApp(), AppFlowyApplication(),
IntegrationMode.integrationTest, IntegrationMode.integrationTest,
rustEnvsBuilder: () { rustEnvsBuilder: () {
final rustEnvs = <String, String>{}; final rustEnvs = <String, String>{};
@ -93,9 +95,10 @@ extension AppFlowyTestBase on WidgetTester {
); );
}, },
); );
await waitUntilSignInPageShow(); await waitUntilSignInPageShow();
return FlowyTestContext( return FlowyTestContext(
applicationDataDirectory: directory, applicationDataDirectory: applicationDataDirectory,
); );
} }
@ -110,27 +113,6 @@ extension AppFlowyTestBase on WidgetTester {
}); });
} }
Future<String> mockApplicationDataStorage({
// use to append after the application data directory
String? pathExtension,
}) async {
final dir = await getTemporaryDirectory();
// Use a random uuid to avoid conflict.
String path = p.join(dir.path, 'appflowy_integration_test', uuid());
if (pathExtension != null && pathExtension.isNotEmpty) {
path = '$path/$pathExtension';
}
final directory = Directory(path);
if (!directory.existsSync()) {
await directory.create(recursive: true);
}
MockApplicationDataStorage.initialPath = directory.path;
return directory.path;
}
Future<void> waitUntilSignInPageShow() async { Future<void> waitUntilSignInPageShow() async {
if (isAuthEnabled) { if (isAuthEnabled) {
final finder = find.byType(SignInAnonymousButton); final finder = find.byType(SignInAnonymousButton);
@ -143,16 +125,22 @@ extension AppFlowyTestBase on WidgetTester {
} }
} }
Future<void> waitForSeconds(int seconds) async {
await Future.delayed((Duration(seconds: seconds)), () {});
}
Future<void> pumpUntilFound( Future<void> pumpUntilFound(
Finder finder, { Finder finder, {
Duration timeout = const Duration(seconds: 10), Duration timeout = const Duration(seconds: 10),
Duration pumpInterval =
const Duration(milliseconds: 50), // Interval between pumps
}) async { }) async {
bool timerDone = false; bool timerDone = false;
final timer = Timer(timeout, () => timerDone = true); final timer = Timer(timeout, () => timerDone = true);
while (timerDone != true) { while (!timerDone) {
await pump(); await pump(pumpInterval); // Pump with an interval
if (any(finder)) { if (any(finder)) {
timerDone = true; break;
} }
} }
timer.cancel(); timer.cancel();
@ -273,3 +261,24 @@ Future<void> useAppFlowyCloud() async {
await setAuthenticatorType(AuthenticatorType.appflowyCloud); await setAuthenticatorType(AuthenticatorType.appflowyCloud);
await setAppFlowyCloudUrl(Some(TestEnv.afCloudUrl)); await setAppFlowyCloudUrl(Some(TestEnv.afCloudUrl));
} }
Future<String> mockApplicationDataStorage({
// use to append after the application data directory
String? pathExtension,
}) async {
final dir = await getTemporaryDirectory();
// Use a random uuid to avoid conflict.
String path = p.join(dir.path, 'appflowy_integration_test', uuid());
if (pathExtension != null && pathExtension.isNotEmpty) {
path = '$path/$pathExtension';
}
final directory = Directory(path);
if (!directory.existsSync()) {
await directory.create(recursive: true);
}
MockApplicationDataStorage.initialPath = directory.path;
return directory.path;
}

View File

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:archive/archive.dart';
Future<void> deleteDirectoriesWithSameBaseNameAsPrefix( Future<void> deleteDirectoriesWithSameBaseNameAsPrefix(
String path, String path,
@ -28,3 +29,25 @@ Future<void> deleteDirectoriesWithSameBaseNameAsPrefix(
} }
} }
} }
Future<void> unzipFile(File zipFile, Directory targetDirectory) async {
// Read the Zip file from disk.
final bytes = zipFile.readAsBytesSync();
// Decode the Zip file
final archive = ZipDecoder().decodeBytes(bytes);
// Extract the contents of the Zip archive to disk.
for (final file in archive) {
final filename = file.name;
if (file.isFile) {
final data = file.content as List<int>;
File(p.join(targetDirectory.path, filename))
..createSync(recursive: true)
..writeAsBytesSync(data);
} else {
Directory(p.join(targetDirectory.path, filename))
.createSync(recursive: true);
}
}
}

View File

@ -11,14 +11,20 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'util.dart';
// const String readme = 'Read me'; // const String readme = 'Read me';
const String gettingStarted = 'Getting started'; const String gettingStarted = 'Getting started';
extension Expectation on WidgetTester { extension Expectation on WidgetTester {
/// Expect to see the home page and with a default read me page. /// Expect to see the home page and with a default read me page.
void expectToSeeHomePage() { Future<void> expectToSeeHomePage() async {
expect(find.byType(HomeStack), findsOneWidget); final finder = find.byType(HomeStack);
expect(find.textContaining(gettingStarted), findsWidgets); await pumpUntilFound(finder);
expect(finder, findsOneWidget);
final docFinder = find.textContaining(gettingStarted);
await pumpUntilFound(docFinder);
} }
/// Expect to see the page name on the home page. /// Expect to see the page name on the home page.

View File

@ -1,6 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart'; import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_user.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart'; import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
@ -13,7 +14,7 @@ import 'base.dart';
extension AppFlowySettings on WidgetTester { extension AppFlowySettings on WidgetTester {
/// Open settings page /// Open settings page
Future<void> openSettings() async { Future<void> openSettings() async {
final settingsButton = find.byTooltip(LocaleKeys.settings_menu_open.tr()); final settingsButton = find.byType(UserSettingButton);
expect(settingsButton, findsOneWidget); expect(settingsButton, findsOneWidget);
await tapButton(settingsButton); await tapButton(settingsButton);
final settingsDialog = find.byType(SettingsDialog); final settingsDialog = find.byType(SettingsDialog);

View File

@ -131,7 +131,7 @@ void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
case AuthenticatorType.local: case AuthenticatorType.local:
getIt.registerFactory<AuthService>( getIt.registerFactory<AuthService>(
() => BackendAuthService( () => BackendAuthService(
AuthTypePB.Local, AuthenticatorPB.Local,
), ),
); );
break; break;

View File

@ -3,7 +3,7 @@ import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/presentation/screens/splash_screen.dart'; import 'package:appflowy/user/presentation/screens/splash_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class FlowyApp implements EntryPoint { class AppFlowyApplication implements EntryPoint {
@override @override
Widget create(LaunchConfiguration config) { Widget create(LaunchConfiguration config) {
return SplashScreen( return SplashScreen(

View File

@ -29,14 +29,30 @@ class FlowyRunnerContext {
FlowyRunnerContext({required this.applicationDataDirectory}); FlowyRunnerContext({required this.applicationDataDirectory});
} }
Future<void> runAppFlowy() async { Future<void> runAppFlowy({bool isAnon = false}) async {
if (kReleaseMode) {
await FlowyRunner.run( await FlowyRunner.run(
FlowyApp(), AppFlowyApplication(),
integrationMode(), integrationMode(),
isAnon: isAnon,
); );
} else {
// When running the app in integration test mode, we need to
// specify the mode to run the app again.
await FlowyRunner.run(
AppFlowyApplication(),
FlowyRunner.currentMode,
didInitGetItCallback: IntegrationTestHelper.didInitGetItCallback,
rustEnvsBuilder: IntegrationTestHelper.rustEnvsBuilder,
isAnon: isAnon,
);
}
} }
class FlowyRunner { class FlowyRunner {
// This variable specifies the initial mode of the app when it is launched for the first time.
// The same mode will be automatically applied in subsequent executions when the runAppFlowy()
// method is called.
static var currentMode = integrationMode(); static var currentMode = integrationMode();
static Future<FlowyRunnerContext> run( static Future<FlowyRunnerContext> run(
@ -55,6 +71,13 @@ class FlowyRunner {
bool isAnon = false, bool isAnon = false,
}) async { }) async {
currentMode = mode; currentMode = mode;
// Only set the mode when it's not release mode
if (!kReleaseMode) {
IntegrationTestHelper.didInitGetItCallback = didInitGetItCallback;
IntegrationTestHelper.rustEnvsBuilder = rustEnvsBuilder;
}
// Clear all the states in case of rebuilding. // Clear all the states in case of rebuilding.
await getIt.reset(); await getIt.reset();
@ -220,3 +243,9 @@ IntegrationMode integrationMode() {
return IntegrationMode.develop; return IntegrationMode.develop;
} }
/// Only used for integration test
class IntegrationTestHelper {
static Future Function()? didInitGetItCallback;
static Map<String, String> Function()? rustEnvsBuilder;
}

View File

@ -87,7 +87,7 @@ class AppFlowyCloudDeepLink {
(_) async { (_) async {
final deviceId = await getDeviceId(); final deviceId = await getDeviceId();
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthTypePB.AFCloud, authType: AuthenticatorPB.AppFlowyCloud,
map: { map: {
AuthServiceMapKeys.signInURL: uri.toString(), AuthServiceMapKeys.signInURL: uri.toString(),
AuthServiceMapKeys.deviceId: deviceId, AuthServiceMapKeys.deviceId: deviceId,

View File

@ -17,7 +17,7 @@ class AppFlowyCloudAuthService implements AuthService {
AppFlowyCloudAuthService(); AppFlowyCloudAuthService();
final BackendAuthService _backendAuthService = BackendAuthService( final BackendAuthService _backendAuthService = BackendAuthService(
AuthTypePB.AFCloud, AuthenticatorPB.AppFlowyCloud,
); );
@override @override

View File

@ -18,7 +18,7 @@ class AppFlowyCloudMockAuthService implements AuthService {
: userEmail = email ?? "${uuid()}@appflowy.io"; : userEmail = email ?? "${uuid()}@appflowy.io";
final BackendAuthService _appFlowyAuthService = final BackendAuthService _appFlowyAuthService =
BackendAuthService(AuthTypePB.Supabase); BackendAuthService(AuthenticatorPB.Supabase);
@override @override
Future<Either<FlowyError, UserProfilePB>> signUp({ Future<Either<FlowyError, UserProfilePB>> signUp({
@ -45,7 +45,7 @@ class AppFlowyCloudMockAuthService implements AuthService {
Map<String, String> params = const {}, Map<String, String> params = const {},
}) async { }) async {
final payload = SignInUrlPayloadPB.create() final payload = SignInUrlPayloadPB.create()
..authType = AuthTypePB.AFCloud ..authType = AuthenticatorPB.AppFlowyCloud
// don't use nanoid here, the gotrue server will transform the email // don't use nanoid here, the gotrue server will transform the email
..email = userEmail; ..email = userEmail;
@ -55,7 +55,7 @@ class AppFlowyCloudMockAuthService implements AuthService {
return getSignInURLResult.fold( return getSignInURLResult.fold(
(urlPB) async { (urlPB) async {
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthTypePB.AFCloud, authType: AuthenticatorPB.AppFlowyCloud,
map: { map: {
AuthServiceMapKeys.signInURL: urlPB.signInUrl, AuthServiceMapKeys.signInURL: urlPB.signInUrl,
AuthServiceMapKeys.deviceId: deviceId, AuthServiceMapKeys.deviceId: deviceId,

View File

@ -14,7 +14,7 @@ import '../../../generated/locale_keys.g.dart';
import 'device_id.dart'; import 'device_id.dart';
class BackendAuthService implements AuthService { class BackendAuthService implements AuthService {
final AuthTypePB authType; final AuthenticatorPB authType;
BackendAuthService(this.authType); BackendAuthService(this.authType);
@ -73,7 +73,7 @@ class BackendAuthService implements AuthService {
..email = userEmail ..email = userEmail
..password = password ..password = password
// When sign up as guest, the auth type is always local. // When sign up as guest, the auth type is always local.
..authType = AuthTypePB.Local ..authType = AuthenticatorPB.Local
..deviceId = await getDeviceId(); ..deviceId = await getDeviceId();
final response = await UserEventSignUp(request).send().then( final response = await UserEventSignUp(request).send().then(
(value) => value.swap(), (value) => value.swap(),
@ -84,7 +84,7 @@ class BackendAuthService implements AuthService {
@override @override
Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({ Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
required String platform, required String platform,
AuthTypePB authType = AuthTypePB.Local, AuthenticatorPB authType = AuthenticatorPB.Local,
Map<String, String> params = const {}, Map<String, String> params = const {},
}) async { }) async {
return left( return left(

View File

@ -22,7 +22,7 @@ class SupabaseAuthService implements AuthService {
GoTrueClient get _auth => _client.auth; GoTrueClient get _auth => _client.auth;
final BackendAuthService _backendAuthService = BackendAuthService( final BackendAuthService _backendAuthService = BackendAuthService(
AuthTypePB.Supabase, AuthenticatorPB.Supabase,
); );
@override @override
@ -171,7 +171,7 @@ class SupabaseAuthService implements AuthService {
required Map<String, String> map, required Map<String, String> map,
}) async { }) async {
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthTypePB.Supabase, authType: AuthenticatorPB.Supabase,
map: map, map: map,
); );

View File

@ -21,7 +21,7 @@ class SupabaseMockAuthService implements AuthService {
GoTrueClient get _auth => _client.auth; GoTrueClient get _auth => _client.auth;
final BackendAuthService _appFlowyAuthService = final BackendAuthService _appFlowyAuthService =
BackendAuthService(AuthTypePB.Supabase); BackendAuthService(AuthenticatorPB.Supabase);
@override @override
Future<Either<FlowyError, UserProfilePB>> signUp({ Future<Either<FlowyError, UserProfilePB>> signUp({
@ -67,7 +67,7 @@ class SupabaseMockAuthService implements AuthService {
// Create the OAuth sign-in payload. // Create the OAuth sign-in payload.
final payload = OauthSignInPB( final payload = OauthSignInPB(
authType: AuthTypePB.Supabase, authType: AuthenticatorPB.Supabase,
map: { map: {
AuthServiceMapKeys.uuid: uuid, AuthServiceMapKeys.uuid: uuid,
AuthServiceMapKeys.email: email, AuthServiceMapKeys.email: email,

View File

@ -72,8 +72,9 @@ class AnonUserItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final icon = isSelected ? const FlowySvg(FlowySvgs.check_s) : null; final icon = isSelected ? const FlowySvg(FlowySvgs.check_s) : null;
final isDisabled = isSelected || user.authType != AuthTypePB.Local; final isDisabled =
final desc = "${user.name}\t ${user.authType}\t"; isSelected || user.authenticator != AuthenticatorPB.Local;
final desc = "${user.name}\t ${user.authenticator}\t";
final child = SizedBox( final child = SizedBox(
height: 30, height: 30,
child: FlowyButton( child: FlowyButton(

View File

@ -1,7 +1,6 @@
import 'package:appflowy/core/frameless_window.dart'; import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/entry_point.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/anon_user_bloc.dart'; import 'package:appflowy/user/application/anon_user_bloc.dart';
@ -101,11 +100,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
} }
Future<void> _relaunchAppAndAutoRegister() async { Future<void> _relaunchAppAndAutoRegister() async {
await FlowyRunner.run( await runAppFlowy(isAnon: true);
FlowyApp(),
integrationMode(),
isAnon: true,
);
} }
} }

View File

@ -46,7 +46,7 @@ class SidebarUser extends StatelessWidget {
Expanded( Expanded(
child: _buildUserName(context, state), child: _buildUserName(context, state),
), ),
_buildSettingsButton(context, state), UserSettingButton(userProfile: state.userProfile),
const HSpace(4), const HSpace(4),
NotificationButton(views: views), NotificationButton(views: views),
], ],
@ -64,8 +64,22 @@ class SidebarUser extends StatelessWidget {
); );
} }
Widget _buildSettingsButton(BuildContext context, MenuUserState state) { /// Return the user name, if the user name is empty, return the default user name.
final userProfile = state.userProfile; String _userName(UserProfilePB userProfile) {
String name = userProfile.name;
if (name.isEmpty) {
name = LocaleKeys.defaultUsername.tr();
}
return name;
}
}
class UserSettingButton extends StatelessWidget {
final UserProfilePB userProfile;
const UserSettingButton({required this.userProfile, super.key});
@override
Widget build(BuildContext context) {
return FlowyTooltip( return FlowyTooltip(
message: LocaleKeys.settings_menu_open.tr(), message: LocaleKeys.settings_menu_open.tr(),
child: IconButton( child: IconButton(
@ -109,13 +123,4 @@ class SidebarUser extends StatelessWidget {
), ),
); );
} }
/// Return the user name, if the user name is empty, return the default user name.
String _userName(UserProfilePB userProfile) {
String name = userProfile.name;
if (name.isEmpty) {
name = LocaleKeys.defaultUsername.tr();
}
return name;
}
} }

View File

@ -1,7 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/startup/entry_point.dart';
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart'; import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart';
@ -207,11 +206,7 @@ class _ChangeStoragePathButtonState extends State<_ChangeStoragePathButton> {
return; return;
} }
await context.read<SettingsLocationCubit>().setCustomPath(path); await context.read<SettingsLocationCubit>().setCustomPath(path);
await FlowyRunner.run( await runAppFlowy(isAnon: true);
FlowyApp(),
FlowyRunner.currentMode,
isAnon: true,
);
if (mounted) { if (mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
@ -283,11 +278,7 @@ class _RecoverDefaultStorageButtonState
await context await context
.read<SettingsLocationCubit>() .read<SettingsLocationCubit>()
.resetDataStoragePathToApplicationDefault(); .resetDataStoragePathToApplicationDefault();
await FlowyRunner.run( await runAppFlowy(isAnon: true);
FlowyApp(),
FlowyRunner.currentMode,
isAnon: true,
);
if (mounted) { if (mounted) {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }

View File

@ -54,7 +54,8 @@ class SettingsUserView extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
_buildUserIconSetting(context), _buildUserIconSetting(context),
if (isAuthEnabled && user.authType != AuthTypePB.Local) ...[ if (isAuthEnabled &&
user.authenticator != AuthenticatorPB.Local) ...[
const VSpace(12), const VSpace(12),
UserEmailInput(user.email), UserEmailInput(user.email),
], ],
@ -146,7 +147,7 @@ class SettingsUserView extends StatelessWidget {
} }
// If the user is logged in locally, render a third-party login button. // If the user is logged in locally, render a third-party login button.
if (state.userProfile.authType == AuthTypePB.Local) { if (state.userProfile.authenticator == AuthenticatorPB.Local) {
return SettingThirdPartyLogin(didLogin: didLogin); return SettingThirdPartyLogin(didLogin: didLogin);
} }
@ -284,6 +285,7 @@ class UserNameInputState extends State<UserNameInput> {
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
_debounce?.cancel();
super.dispose(); super.dispose();
} }
} }
@ -348,6 +350,7 @@ class UserEmailInputState extends State<UserEmailInput> {
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
_debounce?.cancel();
super.dispose(); super.dispose();
} }
} }

View File

@ -35,7 +35,7 @@ class AppFlowyUnitTest {
_pathProviderInitialized(); _pathProviderInitialized();
await FlowyRunner.run( await FlowyRunner.run(
FlowyTestApp(), AppFlowyApplicationUniTest(),
IntegrationMode.unitTest, IntegrationMode.unitTest,
); );
@ -111,7 +111,7 @@ void _pathProviderInitialized() {
}); });
} }
class FlowyTestApp implements EntryPoint { class AppFlowyApplicationUniTest implements EntryPoint {
@override @override
Widget create(LaunchConfiguration config) { Widget create(LaunchConfiguration config) {
return Container(); return Container();

View File

@ -139,7 +139,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -786,7 +786,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -883,7 +883,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -902,7 +902,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -932,7 +932,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-derive" name = "collab-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -944,7 +944,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -963,7 +963,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -977,7 +977,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -1019,7 +1019,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-persistence" name = "collab-persistence"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1040,7 +1040,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1066,7 +1066,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1471,7 +1471,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2842,7 +2842,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2858,7 +2858,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -3280,7 +3280,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -5040,7 +5040,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-entity" name = "realtime-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5062,7 +5062,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-protocol" name = "realtime-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5809,7 +5809,7 @@ dependencies = [
[[package]] [[package]]
name = "shared_entity" name = "shared_entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -7699,7 +7699,7 @@ dependencies = [
[[package]] [[package]]
name = "workspace-template" name = "workspace-template"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"]
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5c1a16cec52e628e2fb7648455581d6fe5e84be1" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "80d4048c69a22c0af77b2f2e9b13b1004b2156e2" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -67,14 +67,14 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5c1
# To switch to the local path, run: # To switch to the local path, run:
# scripts/tool/update_collab_source.sh # scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }

View File

@ -125,7 +125,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]] [[package]]
name = "app-error" name = "app-error"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -667,7 +667,7 @@ dependencies = [
[[package]] [[package]]
name = "client-api" name = "client-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -733,7 +733,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -752,7 +752,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -782,7 +782,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-derive" name = "collab-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -794,7 +794,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -813,7 +813,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-entity" name = "collab-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -827,7 +827,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@ -869,7 +869,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-persistence" name = "collab-persistence"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -890,7 +890,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -916,7 +916,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-user" name = "collab-user"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0e117f568bd2465762582f6aeb8d9c11fe714e63#0e117f568bd2465762582f6aeb8d9c11fe714e63" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=d5324139e6450e32246520416af768202f544869#d5324139e6450e32246520416af768202f544869"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1277,7 +1277,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]] [[package]]
name = "database-entity" name = "database-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2483,7 +2483,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue" name = "gotrue"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"futures-util", "futures-util",
@ -2499,7 +2499,7 @@ dependencies = [
[[package]] [[package]]
name = "gotrue-entity" name = "gotrue-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -2860,7 +2860,7 @@ dependencies = [
[[package]] [[package]]
name = "infra" name = "infra"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"reqwest", "reqwest",
@ -4329,7 +4329,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-entity" name = "realtime-entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -4351,7 +4351,7 @@ dependencies = [
[[package]] [[package]]
name = "realtime-protocol" name = "realtime-protocol"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode", "bincode",
@ -5020,7 +5020,7 @@ dependencies = [
[[package]] [[package]]
name = "shared_entity" name = "shared_entity"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"app-error", "app-error",
@ -6401,7 +6401,7 @@ dependencies = [
[[package]] [[package]]
name = "workspace-template" name = "workspace-template"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5c1a16cec52e628e2fb7648455581d6fe5e84be1#5c1a16cec52e628e2fb7648455581d6fe5e84be1" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=80d4048c69a22c0af77b2f2e9b13b1004b2156e2#80d4048c69a22c0af77b2f2e9b13b1004b2156e2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@ -99,7 +99,7 @@ incremental = false
# Run the script: # Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id # scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5c1a16cec52e628e2fb7648455581d6fe5e84be1" } client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "80d4048c69a22c0af77b2f2e9b13b1004b2156e2" }
# Please use the following script to update collab. # Please use the following script to update collab.
# Working directory: frontend # Working directory: frontend
# #
@ -109,11 +109,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5c1
# To switch to the local path, run: # To switch to the local path, run:
# scripts/tool/update_collab_source.sh # scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️ # ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0e117f568bd2465762582f6aeb8d9c11fe714e63" } collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "d5324139e6450e32246520416af768202f544869" }

View File

@ -7,7 +7,7 @@ edition = "2018"
[lib] [lib]
name = "dart_ffi" name = "dart_ffi"
# this value will change depending on the target os # this value will change depending on the target os
# default staticlib # default static library
crate-type = ["staticlib"] crate-type = ["staticlib"]

View File

@ -8,7 +8,7 @@ use parking_lot::RwLock;
use flowy_core::config::AppFlowyCoreConfig; use flowy_core::config::AppFlowyCoreConfig;
use flowy_core::AppFlowyCore; use flowy_core::AppFlowyCore;
use flowy_notification::register_notification_sender; use flowy_notification::register_notification_sender;
use flowy_user::entities::AuthTypePB; use flowy_user::entities::AuthenticatorPB;
use crate::user_event::TestNotificationSender; use crate::user_event::TestNotificationSender;
@ -21,7 +21,7 @@ pub mod user_event;
#[derive(Clone)] #[derive(Clone)]
pub struct EventIntegrationTest { pub struct EventIntegrationTest {
pub auth_type: Arc<RwLock<AuthTypePB>>, pub auth_type: Arc<RwLock<AuthenticatorPB>>,
pub inner: AppFlowyCore, pub inner: AppFlowyCore,
#[allow(dead_code)] #[allow(dead_code)]
cleaner: Arc<Cleaner>, cleaner: Arc<Cleaner>,
@ -48,7 +48,7 @@ impl EventIntegrationTest {
let inner = init_core(config).await; let inner = init_core(config).await;
let notification_sender = TestNotificationSender::new(); let notification_sender = TestNotificationSender::new();
let auth_type = Arc::new(RwLock::new(AuthTypePB::Local)); let auth_type = Arc::new(RwLock::new(AuthenticatorPB::Local));
register_notification_sender(notification_sender.clone()); register_notification_sender(notification_sender.clone());
std::mem::forget(inner.dispatcher()); std::mem::forget(inner.dispatcher());
Self { Self {

View File

@ -15,7 +15,7 @@ use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_UR
use flowy_server_config::af_cloud_config::AFCloudConfiguration; use flowy_server_config::af_cloud_config::AFCloudConfiguration;
use flowy_server_config::AuthenticatorType; use flowy_server_config::AuthenticatorType;
use flowy_user::entities::{ use flowy_user::entities::{
AuthTypePB, CloudSettingPB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB, AuthenticatorPB, CloudSettingPB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB,
UpdateCloudConfigPB, UpdateUserProfilePayloadPB, UserProfilePB, UpdateCloudConfigPB, UpdateUserProfilePayloadPB, UserProfilePB,
}; };
use flowy_user::errors::{FlowyError, FlowyResult}; use flowy_user::errors::{FlowyError, FlowyResult};
@ -59,7 +59,7 @@ impl EventIntegrationTest {
email, email,
name: "appflowy".to_string(), name: "appflowy".to_string(),
password: password.clone(), password: password.clone(),
auth_type: AuthTypePB::Local, auth_type: AuthenticatorPB::Local,
device_id: uuid::Uuid::new_v4().to_string(), device_id: uuid::Uuid::new_v4().to_string(),
} }
.into_bytes() .into_bytes()
@ -88,7 +88,7 @@ impl EventIntegrationTest {
let map = third_party_sign_up_param(Uuid::new_v4().to_string()); let map = third_party_sign_up_param(Uuid::new_v4().to_string());
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthTypePB::Supabase, auth_type: AuthenticatorPB::Supabase,
}; };
EventBuilder::new(self.clone()) EventBuilder::new(self.clone())
@ -106,7 +106,7 @@ impl EventIntegrationTest {
.await; .await;
} }
pub fn set_auth_type(&self, auth_type: AuthTypePB) { pub fn set_auth_type(&self, auth_type: AuthenticatorPB) {
*self.auth_type.write() = auth_type; *self.auth_type.write() = auth_type;
} }
@ -133,7 +133,7 @@ impl EventIntegrationTest {
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> { pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
let payload = SignInUrlPayloadPB { let payload = SignInUrlPayloadPB {
email: email.to_string(), email: email.to_string(),
auth_type: AuthTypePB::AFCloud, auth_type: AuthenticatorPB::AppFlowyCloud,
}; };
let sign_in_url = EventBuilder::new(self.clone()) let sign_in_url = EventBuilder::new(self.clone())
.event(GenerateSignInURL) .event(GenerateSignInURL)
@ -148,7 +148,7 @@ impl EventIntegrationTest {
map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string()); map.insert(USER_DEVICE_ID.to_string(), Uuid::new_v4().to_string());
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthTypePB::AFCloud, auth_type: AuthenticatorPB::AppFlowyCloud,
}; };
let user_profile = EventBuilder::new(self.clone()) let user_profile = EventBuilder::new(self.clone())
@ -175,7 +175,7 @@ impl EventIntegrationTest {
); );
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthTypePB::Supabase, auth_type: AuthenticatorPB::Supabase,
}; };
let user_profile = EventBuilder::new(self.clone()) let user_profile = EventBuilder::new(self.clone())

View File

@ -1,9 +1,7 @@
use event_integration::user_event::user_localhost_af_cloud;
use event_integration::EventIntegrationTest; use event_integration::EventIntegrationTest;
use flowy_core::DEFAULT_NAME; use flowy_core::DEFAULT_NAME;
use flowy_user::entities::AuthTypePB;
use crate::util::{get_af_cloud_config, unzip_history_user_db}; use crate::util::unzip_history_user_db;
#[tokio::test] #[tokio::test]
async fn reading_039_anon_user_data_test() { async fn reading_039_anon_user_data_test() {
@ -36,63 +34,36 @@ async fn reading_039_anon_user_data_test() {
drop(cleaner); drop(cleaner);
} }
#[tokio::test] // #[tokio::test]
async fn anon_user_to_af_cloud_test() { // async fn migrate_anon_user_data_to_af_cloud_test() {
if get_af_cloud_config().is_none() { // let (cleaner, user_db_path) = unzip_history_user_db("./tests/asset", "039_local").unwrap();
return; // user_localhost_af_cloud().await;
} // let test =
let (cleaner, user_db_path) = unzip_history_user_db("./tests/asset", "039_local").unwrap(); // EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()).await;
user_localhost_af_cloud().await; // let anon_first_level_views = test.get_all_workspace_views().await;
let test = // let anon_second_level_views = test
EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()).await; // .get_views(&anon_first_level_views[0].id)
let anon_first_level_views = test.get_all_workspace_views().await; // .await
let _anon_second_level_views = test // .child_views;
.get_views(&anon_first_level_views[0].id)
.await
.child_views;
let user = test.af_cloud_sign_up().await;
assert_eq!(user.auth_type, AuthTypePB::AFCloud);
// let mut sync_state = test
// .folder_manager
// .get_mutex_folder()
// .lock()
// .as_ref()
// .unwrap()
// .subscribe_sync_state();
// //
// // TODO(nathan): will be removed when supporting merge FolderData // // The anon user data will be migrated to the AppFlowy cloud after sign up
// // wait until the state is SyncFinished with 10 secs timeout // let user = test.af_cloud_sign_up().await;
// loop { // assert_eq!(user.authenticator, AuthenticatorPB::AppFlowyCloud);
// select! {
// _ = tokio::time::sleep(Duration::from_secs(10)) => {
// panic!("Timeout waiting for sync finished");
// }
// state = sync_state.next() => {
// if let Some(state) = &state {
// if state == &SyncState::SyncFinished {
// break;
// }
// }
// }
// }
// }
// //
// let user_first_level_views = test.get_all_workspace_views().await; // let user_first_level_views = test.get_all_workspace_views().await;
// let user_second_level_views = test // let user_second_level_views = test
// .get_views(&user_first_level_views[1].id) // .get_views(&user_first_level_views[0].id)
// .await // .await
// .child_views; // .child_views;
// //
// // first // // first
// assert_eq!(anon_first_level_views.len(), 1); // assert_eq!(anon_first_level_views.len(), 1);
// assert_eq!(user_first_level_views.len(), 2); // assert_eq!(user_first_level_views.len(), 1);
// assert_eq!( // assert_eq!(
// anon_first_level_views[0].name, // anon_first_level_views[0].name,
// // The first one is the get started document // user_first_level_views[0].name
// user_first_level_views[1].name
// ); // );
// assert_ne!(anon_first_level_views[0].id, user_first_level_views[1].id); // assert_ne!(anon_first_level_views[0].id, user_first_level_views[0].id);
// //
// // second // // second
// assert_eq!(anon_second_level_views.len(), user_second_level_views.len()); // assert_eq!(anon_second_level_views.len(), user_second_level_views.len());
@ -101,5 +72,5 @@ async fn anon_user_to_af_cloud_test() {
// user_second_level_views[0].name // user_second_level_views[0].name
// ); // );
// assert_ne!(anon_second_level_views[0].id, user_second_level_views[0].id); // assert_ne!(anon_second_level_views[0].id, user_second_level_views[0].id);
drop(cleaner); // drop(cleaner);
} // }

View File

@ -1,6 +1,6 @@
use event_integration::user_event::{login_password, unique_email}; use event_integration::user_event::{login_password, unique_email};
use event_integration::{event_builder::EventBuilder, EventIntegrationTest}; use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
use flowy_user::entities::{AuthTypePB, SignInPayloadPB, SignUpPayloadPB}; use flowy_user::entities::{AuthenticatorPB, SignInPayloadPB, SignUpPayloadPB};
use flowy_user::errors::ErrorCode; use flowy_user::errors::ErrorCode;
use flowy_user::event_map::UserEvent::*; use flowy_user::event_map::UserEvent::*;
@ -14,7 +14,7 @@ async fn sign_up_with_invalid_email() {
email: email.to_string(), email: email.to_string(),
name: valid_name(), name: valid_name(),
password: login_password(), password: login_password(),
auth_type: AuthTypePB::Local, auth_type: AuthenticatorPB::Local,
device_id: "".to_string(), device_id: "".to_string(),
}; };
@ -38,7 +38,7 @@ async fn sign_up_with_long_password() {
email: unique_email(), email: unique_email(),
name: valid_name(), name: valid_name(),
password: "1234".repeat(100).as_str().to_string(), password: "1234".repeat(100).as_str().to_string(),
auth_type: AuthTypePB::Local, auth_type: AuthenticatorPB::Local,
device_id: "".to_string(), device_id: "".to_string(),
}; };
@ -63,7 +63,7 @@ async fn sign_in_with_invalid_email() {
email: email.to_string(), email: email.to_string(),
password: login_password(), password: login_password(),
name: "".to_string(), name: "".to_string(),
auth_type: AuthTypePB::Local, auth_type: AuthenticatorPB::Local,
device_id: "".to_string(), device_id: "".to_string(),
}; };
@ -90,7 +90,7 @@ async fn sign_in_with_invalid_password() {
email: unique_email(), email: unique_email(),
password, password,
name: "".to_string(), name: "".to_string(),
auth_type: AuthTypePB::Local, auth_type: AuthenticatorPB::Local,
device_id: "".to_string(), device_id: "".to_string(),
}; };

View File

@ -1,7 +1,7 @@
use nanoid::nanoid; use nanoid::nanoid;
use event_integration::{event_builder::EventBuilder, EventIntegrationTest}; use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
use flowy_user::entities::{AuthTypePB, UpdateUserProfilePayloadPB, UserProfilePB}; use flowy_user::entities::{AuthenticatorPB, UpdateUserProfilePayloadPB, UserProfilePB};
use flowy_user::{errors::ErrorCode, event_map::UserEvent::*}; use flowy_user::{errors::ErrorCode, event_map::UserEvent::*};
use crate::user::local_test::helper::*; use crate::user::local_test::helper::*;
@ -32,7 +32,7 @@ async fn anon_user_profile_get() {
assert_eq!(user_profile.openai_key, user.openai_key); assert_eq!(user_profile.openai_key, user.openai_key);
assert_eq!(user_profile.stability_ai_key, user.stability_ai_key); assert_eq!(user_profile.stability_ai_key, user.stability_ai_key);
assert_eq!(user_profile.workspace_id, user.workspace_id); assert_eq!(user_profile.workspace_id, user.workspace_id);
assert_eq!(user_profile.auth_type, AuthTypePB::Local); assert_eq!(user_profile.authenticator, AuthenticatorPB::Local);
} }
#[tokio::test] #[tokio::test]

View File

@ -14,7 +14,9 @@ use event_integration::EventIntegrationTest;
use flowy_core::DEFAULT_NAME; use flowy_core::DEFAULT_NAME;
use flowy_encrypt::decrypt_text; use flowy_encrypt::decrypt_text;
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_UUID}; use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_UUID};
use flowy_user::entities::{AuthTypePB, OauthSignInPB, UpdateUserProfilePayloadPB, UserProfilePB}; use flowy_user::entities::{
AuthenticatorPB, OauthSignInPB, UpdateUserProfilePayloadPB, UserProfilePB,
};
use flowy_user::errors::ErrorCode; use flowy_user::errors::ErrorCode;
use flowy_user::event_map::UserEvent::*; use flowy_user::event_map::UserEvent::*;
@ -33,7 +35,7 @@ async fn third_party_sign_up_test() {
map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string()); map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string());
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthTypePB::Supabase, auth_type: AuthenticatorPB::Supabase,
}; };
let response = EventBuilder::new(test.clone()) let response = EventBuilder::new(test.clone())
@ -77,7 +79,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
.event(OauthSignIn) .event(OauthSignIn)
.payload(OauthSignInPB { .payload(OauthSignInPB {
map: map.clone(), map: map.clone(),
auth_type: AuthTypePB::Supabase, auth_type: AuthenticatorPB::Supabase,
}) })
.async_send() .async_send()
.await .await
@ -88,7 +90,7 @@ async fn third_party_sign_up_with_duplicated_uuid() {
.event(OauthSignIn) .event(OauthSignIn)
.payload(OauthSignInPB { .payload(OauthSignInPB {
map: map.clone(), map: map.clone(),
auth_type: AuthTypePB::Supabase, auth_type: AuthenticatorPB::Supabase,
}) })
.async_send() .async_send()
.await .await

View File

@ -4,7 +4,7 @@ use event_integration::{event_builder::EventBuilder, EventIntegrationTest};
use flowy_folder2::entities::WorkspaceSettingPB; use flowy_folder2::entities::WorkspaceSettingPB;
use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspaceSetting; use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspaceSetting;
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID}; use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
use flowy_user::entities::{AuthTypePB, OauthSignInPB, UserProfilePB}; use flowy_user::entities::{AuthenticatorPB, OauthSignInPB, UserProfilePB};
use flowy_user::event_map::UserEvent::*; use flowy_user::event_map::UserEvent::*;
use crate::util::*; use crate::util::*;
@ -21,7 +21,7 @@ async fn initial_workspace_test() {
); );
let payload = OauthSignInPB { let payload = OauthSignInPB {
map, map,
auth_type: AuthTypePB::Supabase, auth_type: AuthenticatorPB::Supabase,
}; };
let _ = EventBuilder::new(test.clone()) let _ = EventBuilder::new(test.clone())

View File

@ -23,7 +23,7 @@ use flowy_server::supabase::api::*;
use flowy_server::{AppFlowyEncryption, EncryptionImpl}; use flowy_server::{AppFlowyEncryption, EncryptionImpl};
use flowy_server_config::af_cloud_config::AFCloudConfiguration; use flowy_server_config::af_cloud_config::AFCloudConfiguration;
use flowy_server_config::supabase_config::SupabaseConfiguration; use flowy_server_config::supabase_config::SupabaseConfiguration;
use flowy_user::entities::{AuthTypePB, UpdateUserProfilePayloadPB}; use flowy_user::entities::{AuthenticatorPB, UpdateUserProfilePayloadPB};
use flowy_user::errors::FlowyError; use flowy_user::errors::FlowyError;
use flowy_user::event_map::UserCloudServiceProvider; use flowy_user::event_map::UserCloudServiceProvider;
use flowy_user::event_map::UserEvent::*; use flowy_user::event_map::UserEvent::*;
@ -43,7 +43,7 @@ impl FlowySupabaseTest {
pub async fn new() -> Option<Self> { pub async fn new() -> Option<Self> {
let _ = get_supabase_config()?; let _ = get_supabase_config()?;
let test = EventIntegrationTest::new().await; let test = EventIntegrationTest::new().await;
test.set_auth_type(AuthTypePB::Supabase); test.set_auth_type(AuthenticatorPB::Supabase);
test test
.server_provider .server_provider
.set_authenticator(Authenticator::Supabase); .set_authenticator(Authenticator::Supabase);
@ -210,7 +210,7 @@ impl AFCloudTest {
pub async fn new() -> Option<Self> { pub async fn new() -> Option<Self> {
let _ = get_af_cloud_config()?; let _ = get_af_cloud_config()?;
let test = EventIntegrationTest::new().await; let test = EventIntegrationTest::new().await;
test.set_auth_type(AuthTypePB::AFCloud); test.set_auth_type(AuthenticatorPB::AppFlowyCloud);
test test
.server_provider .server_provider
.set_authenticator(Authenticator::AppFlowyCloud); .set_authenticator(Authenticator::AppFlowyCloud);

View File

@ -22,25 +22,25 @@ pub(crate) const SERVER_PROVIDER_TYPE_KEY: &str = "server_provider_type";
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)] #[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize_repr, Deserialize_repr)]
#[repr(u8)] #[repr(u8)]
pub enum ServerType { pub enum Server {
/// Local server provider. /// Local server provider.
/// Offline mode, no user authentication and the data is stored locally. /// Offline mode, no user authentication and the data is stored locally.
Local = 0, Local = 0,
/// AppFlowy Cloud server provider. /// AppFlowy Cloud server provider.
/// The [AppFlowy-Server](https://github.com/AppFlowy-IO/AppFlowy-Cloud) is still a work in /// The [AppFlowy-Server](https://github.com/AppFlowy-IO/AppFlowy-Cloud) is still a work in
/// progress. /// progress.
AFCloud = 1, AppFlowyCloud = 1,
/// Supabase server provider. /// Supabase server provider.
/// It uses supabase postgresql database to store data and user authentication. /// It uses supabase postgresql database to store data and user authentication.
Supabase = 2, Supabase = 2,
} }
impl Display for ServerType { impl Display for Server {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
ServerType::Local => write!(f, "Local"), Server::Local => write!(f, "Local"),
ServerType::AFCloud => write!(f, "AppFlowyCloud"), Server::AppFlowyCloud => write!(f, "AppFlowyCloud"),
ServerType::Supabase => write!(f, "Supabase"), Server::Supabase => write!(f, "Supabase"),
} }
} }
} }
@ -51,8 +51,8 @@ impl Display for ServerType {
/// Each server implements the [AppFlowyServer] trait, which provides the [UserCloudService], etc. /// Each server implements the [AppFlowyServer] trait, which provides the [UserCloudService], etc.
pub struct ServerProvider { pub struct ServerProvider {
config: AppFlowyCoreConfig, config: AppFlowyCoreConfig,
server_type: RwLock<ServerType>, server: RwLock<Server>,
providers: RwLock<HashMap<ServerType, Arc<dyn AppFlowyServer>>>, providers: RwLock<HashMap<Server, Arc<dyn AppFlowyServer>>>,
pub(crate) encryption: RwLock<Arc<dyn AppFlowyEncryption>>, pub(crate) encryption: RwLock<Arc<dyn AppFlowyEncryption>>,
pub(crate) store_preferences: Weak<StorePreferences>, pub(crate) store_preferences: Weak<StorePreferences>,
pub(crate) enable_sync: RwLock<bool>, pub(crate) enable_sync: RwLock<bool>,
@ -62,13 +62,13 @@ pub struct ServerProvider {
impl ServerProvider { impl ServerProvider {
pub fn new( pub fn new(
config: AppFlowyCoreConfig, config: AppFlowyCoreConfig,
server_type: ServerType, server: Server,
store_preferences: Weak<StorePreferences>, store_preferences: Weak<StorePreferences>,
) -> Self { ) -> Self {
let encryption = EncryptionImpl::new(None); let encryption = EncryptionImpl::new(None);
Self { Self {
config, config,
server_type: RwLock::new(server_type), server: RwLock::new(server),
providers: RwLock::new(HashMap::new()), providers: RwLock::new(HashMap::new()),
enable_sync: RwLock::new(true), enable_sync: RwLock::new(true),
encryption: RwLock::new(Arc::new(encryption)), encryption: RwLock::new(Arc::new(encryption)),
@ -77,37 +77,34 @@ impl ServerProvider {
} }
} }
pub fn get_server_type(&self) -> ServerType { pub fn get_server_type(&self) -> Server {
self.server_type.read().clone() self.server.read().clone()
} }
pub fn set_server_type(&self, server_type: ServerType) { pub fn set_server_type(&self, server_type: Server) {
let old_server_type = self.server_type.read().clone(); let old_server_type = self.server.read().clone();
if server_type != old_server_type { if server_type != old_server_type {
self.providers.write().remove(&old_server_type); self.providers.write().remove(&old_server_type);
} }
*self.server_type.write() = server_type; *self.server.write() = server_type;
} }
/// Returns a [AppFlowyServer] trait implementation base on the provider_type. /// Returns a [AppFlowyServer] trait implementation base on the provider_type.
pub(crate) fn get_server( pub(crate) fn get_server(&self, server_type: &Server) -> FlowyResult<Arc<dyn AppFlowyServer>> {
&self,
server_type: &ServerType,
) -> FlowyResult<Arc<dyn AppFlowyServer>> {
if let Some(provider) = self.providers.read().get(server_type) { if let Some(provider) = self.providers.read().get(server_type) {
return Ok(provider.clone()); return Ok(provider.clone());
} }
let server = match server_type { let server = match server_type {
ServerType::Local => { Server::Local => {
let local_db = Arc::new(LocalServerDBImpl { let local_db = Arc::new(LocalServerDBImpl {
storage_path: self.config.storage_path.clone(), storage_path: self.config.storage_path.clone(),
}); });
let server = Arc::new(LocalServer::new(local_db)); let server = Arc::new(LocalServer::new(local_db));
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server) Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)
}, },
ServerType::AFCloud => { Server::AppFlowyCloud => {
let config = AFCloudConfiguration::from_env()?; let config = AFCloudConfiguration::from_env()?;
let server = Arc::new(AppFlowyCloudServer::new( let server = Arc::new(AppFlowyCloudServer::new(
config, config,
@ -117,7 +114,7 @@ impl ServerProvider {
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server) Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)
}, },
ServerType::Supabase => { Server::Supabase => {
let config = SupabaseConfiguration::from_env()?; let config = SupabaseConfiguration::from_env()?;
let uid = self.uid.clone(); let uid = self.uid.clone();
tracing::trace!("🔑Supabase config: {:?}", config); tracing::trace!("🔑Supabase config: {:?}", config);
@ -140,36 +137,35 @@ impl ServerProvider {
} }
} }
impl From<Authenticator> for ServerType { impl From<Authenticator> for Server {
fn from(auth_provider: Authenticator) -> Self { fn from(auth_provider: Authenticator) -> Self {
match auth_provider { match auth_provider {
Authenticator::Local => ServerType::Local, Authenticator::Local => Server::Local,
Authenticator::AppFlowyCloud => ServerType::AFCloud, Authenticator::AppFlowyCloud => Server::AppFlowyCloud,
Authenticator::Supabase => ServerType::Supabase, Authenticator::Supabase => Server::Supabase,
} }
} }
} }
impl From<ServerType> for Authenticator { impl From<Server> for Authenticator {
fn from(ty: ServerType) -> Self { fn from(ty: Server) -> Self {
match ty { match ty {
ServerType::Local => Authenticator::Local, Server::Local => Authenticator::Local,
ServerType::AFCloud => Authenticator::AppFlowyCloud, Server::AppFlowyCloud => Authenticator::AppFlowyCloud,
ServerType::Supabase => Authenticator::Supabase, Server::Supabase => Authenticator::Supabase,
} }
} }
} }
impl From<&Authenticator> for ServerType { impl From<&Authenticator> for Server {
fn from(auth_provider: &Authenticator) -> Self { fn from(auth_provider: &Authenticator) -> Self {
Self::from(auth_provider.clone()) Self::from(auth_provider.clone())
} }
} }
pub fn current_server_type(store_preferences: &Arc<StorePreferences>) -> ServerType { pub fn current_server_type(store_preferences: &Arc<StorePreferences>) -> Server {
match store_preferences.get_object::<ServerType>(SERVER_PROVIDER_TYPE_KEY) { store_preferences
None => ServerType::Local, .get_object::<Server>(SERVER_PROVIDER_TYPE_KEY)
Some(provider_type) => provider_type, .unwrap_or(Server::Local)
}
} }
struct LocalServerDBImpl { struct LocalServerDBImpl {

View File

@ -30,7 +30,7 @@ use flowy_user_deps::cloud::UserCloudService;
use flowy_user_deps::entities::{Authenticator, UserTokenState}; use flowy_user_deps::entities::{Authenticator, UserTokenState};
use lib_infra::future::{to_fut, Fut, FutureResult}; use lib_infra::future::{to_fut, Fut, FutureResult};
use crate::integrate::server::{ServerProvider, ServerType, SERVER_PROVIDER_TYPE_KEY}; use crate::integrate::server::{Server, ServerProvider, SERVER_PROVIDER_TYPE_KEY};
impl FileStorageService for ServerProvider { impl FileStorageService for ServerProvider {
fn create_object(&self, object: StorageObject) -> FutureResult<String, FlowyError> { fn create_object(&self, object: StorageObject) -> FutureResult<String, FlowyError> {
@ -91,12 +91,12 @@ impl UserCloudServiceProvider for ServerProvider {
/// When user login, the provider type is set by the [Authenticator] and save to disk for next use. /// When user login, the provider type is set by the [Authenticator] and save to disk for next use.
/// ///
/// Each [Authenticator] has a corresponding [ServerType]. The [ServerType] is used /// Each [Authenticator] has a corresponding [Server]. The [Server] is used
/// to create a new [AppFlowyServer] if it doesn't exist. Once the [ServerType] is set, /// to create a new [AppFlowyServer] if it doesn't exist. Once the [Server] is set,
/// it will be used when user open the app again. /// it will be used when user open the app again.
/// ///
fn set_authenticator(&self, authenticator: Authenticator) { fn set_authenticator(&self, authenticator: Authenticator) {
let server_type: ServerType = authenticator.into(); let server_type: Server = authenticator.into();
self.set_server_type(server_type.clone()); self.set_server_type(server_type.clone());
match self.store_preferences.upgrade() { match self.store_preferences.upgrade() {
@ -117,7 +117,7 @@ impl UserCloudServiceProvider for ServerProvider {
Authenticator::from(server_type) Authenticator::from(server_type)
} }
/// Returns the [UserCloudService] base on the current [ServerType]. /// Returns the [UserCloudService] base on the current [Server].
/// Creates a new [AppFlowyServer] if it doesn't exist. /// Creates a new [AppFlowyServer] if it doesn't exist.
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> { fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
let server_type = self.get_server_type(); let server_type = self.get_server_type();
@ -127,11 +127,11 @@ impl UserCloudServiceProvider for ServerProvider {
fn service_url(&self) -> String { fn service_url(&self) -> String {
match self.get_server_type() { match self.get_server_type() {
ServerType::Local => "".to_string(), Server::Local => "".to_string(),
ServerType::AFCloud => AFCloudConfiguration::from_env() Server::AppFlowyCloud => AFCloudConfiguration::from_env()
.map(|config| config.base_url) .map(|config| config.base_url)
.unwrap_or_default(), .unwrap_or_default(),
ServerType::Supabase => SupabaseConfiguration::from_env() Server::Supabase => SupabaseConfiguration::from_env()
.map(|config| config.url) .map(|config| config.url)
.unwrap_or_default(), .unwrap_or_default(),
} }
@ -325,7 +325,7 @@ impl CollabStorageProvider for ServerProvider {
collab_object, collab_object,
local_collab, local_collab,
} => { } => {
if let Ok(server) = self.get_server(&ServerType::AFCloud) { if let Ok(server) = self.get_server(&Server::AppFlowyCloud) {
to_fut(async move { to_fut(async move {
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![]; let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
match server.collab_ws_channel(&collab_object.object_id).await { match server.collab_ws_channel(&collab_object.object_id).await {
@ -373,7 +373,7 @@ impl CollabStorageProvider for ServerProvider {
} => { } => {
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![]; let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
if let Some(remote_collab_storage) = self if let Some(remote_collab_storage) = self
.get_server(&ServerType::Supabase) .get_server(&Server::Supabase)
.ok() .ok()
.and_then(|provider| provider.collab_storage(&collab_object)) .and_then(|provider| provider.collab_storage(&collab_object))
{ {

View File

@ -26,7 +26,7 @@ use crate::deps_resolve::collab_backup::RocksdbBackupImpl;
use crate::deps_resolve::*; use crate::deps_resolve::*;
use crate::integrate::collab_interact::CollabInteractImpl; use crate::integrate::collab_interact::CollabInteractImpl;
use crate::integrate::log::init_log; use crate::integrate::log::init_log;
use crate::integrate::server::{current_server_type, ServerProvider, ServerType}; use crate::integrate::server::{current_server_type, Server, ServerProvider};
use crate::integrate::user::UserStatusCallbackImpl; use crate::integrate::user::UserStatusCallbackImpl;
pub mod config; pub mod config;
@ -232,12 +232,12 @@ fn init_user_manager(
) )
} }
impl From<ServerType> for CollabDataSource { impl From<Server> for CollabDataSource {
fn from(server_type: ServerType) -> Self { fn from(server_type: Server) -> Self {
match server_type { match server_type {
ServerType::Local => CollabDataSource::Local, Server::Local => CollabDataSource::Local,
ServerType::AFCloud => CollabDataSource::AppFlowyCloud, Server::AppFlowyCloud => CollabDataSource::AppFlowyCloud,
ServerType::Supabase => CollabDataSource::Supabase, Server::Supabase => CollabDataSource::Supabase,
} }
} }
} }

View File

@ -5,7 +5,6 @@ use collab_document::blocks::DocumentData;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tracing::trace;
use validator::ValidationError; use validator::ValidationError;
pub fn get_delta_for_block(block_id: &str, data: &DocumentData) -> Option<Vec<InsertDelta>> { pub fn get_delta_for_block(block_id: &str, data: &DocumentData) -> Option<Vec<InsertDelta>> {

View File

@ -47,9 +47,6 @@ pub struct FolderManager {
pub cloud_service: Arc<dyn FolderCloudService>, pub cloud_service: Arc<dyn FolderCloudService>,
} }
unsafe impl Send for FolderManager {}
unsafe impl Sync for FolderManager {}
impl FolderManager { impl FolderManager {
pub async fn new( pub async fn new(
user: Arc<dyn FolderUser>, user: Arc<dyn FolderUser>,
@ -1012,8 +1009,8 @@ impl FolderManager {
/// Return the views that belong to the workspace. The views are filtered by the trash. /// Return the views that belong to the workspace. The views are filtered by the trash.
pub(crate) fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> { pub(crate) fn get_workspace_view_pbs(_workspace_id: &str, folder: &Folder) -> Vec<ViewPB> {
let trash_ids = folder let items = folder.get_all_trash();
.get_all_trash() let trash_ids = items
.into_iter() .into_iter()
.map(|trash| trash.id) .map(|trash| trash.id)
.collect::<Vec<String>>(); .collect::<Vec<String>>();

View File

@ -37,10 +37,10 @@ impl FolderManager {
let collab_db = self.user.collab_db(uid)?; let collab_db = self.user.collab_db(uid)?;
let (view_tx, view_rx) = tokio::sync::broadcast::channel(100); let (view_tx, view_rx) = tokio::sync::broadcast::channel(100);
let (trash_tx, trash_rx) = tokio::sync::broadcast::channel(100); let (section_change_tx, section_change_rx) = tokio::sync::broadcast::channel(100);
let folder_notifier = FolderNotify { let folder_notifier = FolderNotify {
view_change_tx: view_tx, view_change_tx: view_tx,
trash_change_tx: trash_tx, section_change_tx,
}; };
let folder = match initial_data { let folder = match initial_data {
@ -99,7 +99,7 @@ impl FolderManager {
let weak_mutex_folder = Arc::downgrade(&self.mutex_folder); let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
subscribe_folder_sync_state_changed(workspace_id.clone(), folder_state_rx, &weak_mutex_folder); subscribe_folder_sync_state_changed(workspace_id.clone(), folder_state_rx, &weak_mutex_folder);
subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder); subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder);
subscribe_folder_trash_changed(trash_rx, &weak_mutex_folder); subscribe_folder_trash_changed(section_change_rx, &weak_mutex_folder);
subscribe_folder_view_changed(view_rx, &weak_mutex_folder); subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
Ok(()) Ok(())
} }

View File

@ -3,7 +3,8 @@ use std::sync::{Arc, Weak};
use collab::core::collab_state::SyncState; use collab::core::collab_state::SyncState;
use collab_folder::{ use collab_folder::{
Folder, TrashChange, TrashChangeReceiver, View, ViewChange, ViewChangeReceiver, Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange,
ViewChangeReceiver,
}; };
use tokio_stream::wrappers::WatchStream; use tokio_stream::wrappers::WatchStream;
use tokio_stream::StreamExt; use tokio_stream::StreamExt;
@ -102,7 +103,7 @@ pub(crate) fn subscribe_folder_sync_state_changed(
/// Listen on the [TrashChange]s and notify the frontend some views were changed. /// Listen on the [TrashChange]s and notify the frontend some views were changed.
pub(crate) fn subscribe_folder_trash_changed( pub(crate) fn subscribe_folder_trash_changed(
mut rx: TrashChangeReceiver, mut rx: SectionChangeReceiver,
weak_mutex_folder: &Weak<MutexFolder>, weak_mutex_folder: &Weak<MutexFolder>,
) { ) {
let weak_mutex_folder = weak_mutex_folder.clone(); let weak_mutex_folder = weak_mutex_folder.clone();
@ -111,11 +112,13 @@ pub(crate) fn subscribe_folder_trash_changed(
if let Some(folder) = weak_mutex_folder.upgrade() { if let Some(folder) = weak_mutex_folder.upgrade() {
let mut unique_ids = HashSet::new(); let mut unique_ids = HashSet::new();
tracing::trace!("Did receive trash change: {:?}", value); tracing::trace!("Did receive trash change: {:?}", value);
let ids = match value {
TrashChange::DidCreateTrash { ids } => ids,
TrashChange::DidDeleteTrash { ids } => ids,
};
match value {
SectionChange::Trash(change) => {
let ids = match change {
TrashSectionChange::TrashItemAdded { ids } => ids,
TrashSectionChange::TrashItemRemoved { ids } => ids,
};
if let Some(folder) = folder.lock().as_ref() { if let Some(folder) = folder.lock().as_ref() {
let views = folder.views.get_views(&ids); let views = folder.views.get_views(&ids);
for view in views { for view in views {
@ -130,6 +133,8 @@ pub(crate) fn subscribe_folder_trash_changed(
let parent_view_ids = unique_ids.into_iter().collect(); let parent_view_ids = unique_ids.into_iter().collect();
notify_parent_view_did_change(folder.clone(), parent_view_ids); notify_parent_view_did_change(folder.clone(), parent_view_ids);
},
}
} }
} }
}); });

View File

@ -54,6 +54,7 @@ impl DefaultFolderBuilder {
views: FlattedViews::flatten_views(views), views: FlattedViews::flatten_views(views),
favorites: Default::default(), favorites: Default::default(),
recent: Default::default(), recent: Default::default(),
trash: Default::default(),
} }
} }
} }

View File

@ -1,6 +1,6 @@
use anyhow::Error; use anyhow::Error;
use client_api::entity::QueryCollabResult::{Failed, Success}; use client_api::entity::QueryCollabResult::{Failed, Success};
use client_api::entity::{BatchQueryCollab, BatchQueryCollabParams, QueryCollabParams}; use client_api::entity::{QueryCollab, QueryCollabParams};
use client_api::error::ErrorCode::RecordNotFound; use client_api::error::ErrorCode::RecordNotFound;
use collab::core::collab_plugin::EncodedCollabV1; use collab::core::collab_plugin::EncodedCollabV1;
use collab_entity::CollabType; use collab_entity::CollabType;
@ -31,8 +31,10 @@ where
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id,
inner: QueryCollab {
object_id, object_id,
collab_type, collab_type,
},
}; };
match try_get_client?.get_collab(params).await { match try_get_client?.get_collab(params).await {
Ok(data) => Ok(vec![data.doc_state.to_vec()]), Ok(data) => Ok(vec![data.doc_state.to_vec()]),
@ -57,15 +59,13 @@ where
let try_get_client = self.0.try_get_client(); let try_get_client = self.0.try_get_client();
FutureResult::new(async move { FutureResult::new(async move {
let client = try_get_client?; let client = try_get_client?;
let params = BatchQueryCollabParams( let params = object_ids
object_ids
.into_iter() .into_iter()
.map(|object_id| BatchQueryCollab { .map(|object_id| QueryCollab {
object_id, object_id,
collab_type: object_ty.clone(), collab_type: object_ty.clone(),
}) })
.collect(), .collect();
);
let results = client.batch_get_collab(&workspace_id, params).await?; let results = client.batch_get_collab(&workspace_id, params).await?;
Ok( Ok(
results results

View File

@ -1,5 +1,5 @@
use anyhow::Error; use anyhow::Error;
use client_api::entity::QueryCollabParams; use client_api::entity::{QueryCollab, QueryCollabParams};
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab_document::document::Document; use collab_document::document::Document;
use collab_entity::CollabType; use collab_entity::CollabType;
@ -27,8 +27,10 @@ where
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id,
inner: QueryCollab {
object_id: document_id.to_string(), object_id: document_id.to_string(),
collab_type: CollabType::Document, collab_type: CollabType::Document,
},
}; };
let data = try_get_client? let data = try_get_client?
.get_collab(params) .get_collab(params)
@ -60,8 +62,10 @@ where
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
workspace_id, workspace_id,
inner: QueryCollab {
object_id: document_id.clone(), object_id: document_id.clone(),
collab_type: CollabType::Document, collab_type: CollabType::Document,
},
}; };
let doc_state = try_get_client? let doc_state = try_get_client?
.get_collab(params) .get_collab(params)

View File

@ -1,5 +1,5 @@
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use client_api::entity::QueryCollabParams; use client_api::entity::{QueryCollab, QueryCollabParams};
use collab::core::origin::CollabOrigin; use collab::core::origin::CollabOrigin;
use collab_entity::CollabType; use collab_entity::CollabType;
@ -60,9 +60,11 @@ where
let try_get_client = self.0.try_get_client(); let try_get_client = self.0.try_get_client();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
object_id: workspace_id.clone(),
workspace_id: workspace_id.clone(), workspace_id: workspace_id.clone(),
inner: QueryCollab {
object_id: workspace_id.clone(),
collab_type: CollabType::Folder, collab_type: CollabType::Folder,
},
}; };
let doc_state = try_get_client? let doc_state = try_get_client?
.get_collab(params) .get_collab(params)
@ -98,9 +100,11 @@ where
let try_get_client = self.0.try_get_client(); let try_get_client = self.0.try_get_client();
FutureResult::new(async move { FutureResult::new(async move {
let params = QueryCollabParams { let params = QueryCollabParams {
object_id: workspace_id.clone(), workspace_id: workspace_id.clone(),
workspace_id, inner: QueryCollab {
object_id: workspace_id,
collab_type: CollabType::Folder, collab_type: CollabType::Folder,
},
}; };
let doc_state = try_get_client? let doc_state = try_get_client?
.get_collab(params) .get_collab(params)

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use client_api::entity::workspace_dto::{CreateWorkspaceMember, WorkspaceMemberChangeset}; use client_api::entity::workspace_dto::{CreateWorkspaceMember, WorkspaceMemberChangeset};
use client_api::entity::{AFRole, AFWorkspace, AuthProvider, InsertCollabParams}; use client_api::entity::{AFRole, AFWorkspace, AuthProvider, CollabParams, CreateCollabParams};
use collab_entity::CollabObject; use collab_entity::CollabObject;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -231,16 +231,20 @@ where
&self, &self,
collab_object: &CollabObject, collab_object: &CollabObject,
data: Vec<u8>, data: Vec<u8>,
override_if_exist: bool,
) -> FutureResult<(), Error> { ) -> FutureResult<(), Error> {
let try_get_client = self.server.try_get_client(); let try_get_client = self.server.try_get_client();
let collab_object = collab_object.clone(); let collab_object = collab_object.clone();
FutureResult::new(async move { FutureResult::new(async move {
let client = try_get_client?; let client = try_get_client?;
let params = InsertCollabParams::new( let params = CreateCollabParams::new(
collab_object.object_id.clone(),
collab_object.collab_type.clone(),
data,
collab_object.workspace_id.clone(), collab_object.workspace_id.clone(),
CollabParams {
object_id: collab_object.object_id.clone(),
encoded_collab_v1: data,
collab_type: collab_object.collab_type.clone(),
override_if_exist,
},
); );
client.create_collab(params).await?; client.create_collab(params).await?;
Ok(()) Ok(())

View File

@ -141,6 +141,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
&self, &self,
_collab_object: &CollabObject, _collab_object: &CollabObject,
_data: Vec<u8>, _data: Vec<u8>,
_override_if_exist: bool,
) -> FutureResult<(), Error> { ) -> FutureResult<(), Error> {
FutureResult::new(async { Ok(()) }) FutureResult::new(async { Ok(()) })
} }

View File

@ -98,10 +98,8 @@ where
// Query the user profile and workspaces // Query the user profile and workspaces
tracing::debug!("user uuid: {}", params.uuid); tracing::debug!("user uuid: {}", params.uuid);
let user_profile = get_user_profile( let user_profile =
postgrest.clone(), get_user_profile(postgrest.clone(), GetUserProfileParams::Uuid(params.uuid))
GetUserProfileParams::Uuid(params.uuid.clone()),
)
.await? .await?
.unwrap(); .unwrap();
let user_workspaces = get_user_workspaces(postgrest.clone(), user_profile.uid).await?; let user_workspaces = get_user_workspaces(postgrest.clone(), user_profile.uid).await?;
@ -137,7 +135,7 @@ where
FutureResult::new(async move { FutureResult::new(async move {
let postgrest = try_get_postgrest?; let postgrest = try_get_postgrest?;
let params = oauth_params_from_box_any(params)?; let params = oauth_params_from_box_any(params)?;
let uuid = params.uuid.clone(); let uuid = params.uuid;
let response = get_user_profile(postgrest.clone(), GetUserProfileParams::Uuid(uuid)) let response = get_user_profile(postgrest.clone(), GetUserProfileParams::Uuid(uuid))
.await? .await?
.unwrap(); .unwrap();
@ -311,7 +309,8 @@ where
fn create_collab_object( fn create_collab_object(
&self, &self,
collab_object: &CollabObject, collab_object: &CollabObject,
update: Vec<u8>, data: Vec<u8>,
_override_if_exist: bool,
) -> FutureResult<(), Error> { ) -> FutureResult<(), Error> {
let try_get_postgrest = self.server.try_get_weak_postgrest(); let try_get_postgrest = self.server.try_get_weak_postgrest();
let cloned_collab_object = collab_object.clone(); let cloned_collab_object = collab_object.clone();
@ -319,7 +318,7 @@ where
af_spawn(async move { af_spawn(async move {
tx.send( tx.send(
async move { async move {
CreateCollabAction::new(cloned_collab_object, try_get_postgrest?, update) CreateCollabAction::new(cloned_collab_object, try_get_postgrest?, data)
.run() .run()
.await?; .await?;
Ok(()) Ok(())

View File

@ -141,6 +141,7 @@ pub trait UserCloudService: Send + Sync + 'static {
&self, &self,
collab_object: &CollabObject, collab_object: &CollabObject,
data: Vec<u8>, data: Vec<u8>,
override_if_exist: bool,
) -> FutureResult<(), Error>; ) -> FutureResult<(), Error>;
} }

View File

@ -17,6 +17,7 @@ use parking_lot::{Mutex, RwLock};
use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction}; use collab_integrate::{PersistenceError, RocksCollabDB, YrsDocAction};
use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_folder_deps::cloud::gen_view_id; use flowy_folder_deps::cloud::gen_view_id;
use flowy_user_deps::entities::Authenticator;
use crate::migrations::MigrationUser; use crate::migrations::MigrationUser;
@ -27,6 +28,7 @@ pub fn migration_anon_user_on_sign_up(
old_collab_db: &Arc<RocksCollabDB>, old_collab_db: &Arc<RocksCollabDB>,
new_user: &MigrationUser, new_user: &MigrationUser,
new_collab_db: &Arc<RocksCollabDB>, new_collab_db: &Arc<RocksCollabDB>,
authenticator: &Authenticator,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
new_collab_db new_collab_db
.with_write_txn(|new_collab_w_txn| { .with_write_txn(|new_collab_w_txn| {
@ -72,6 +74,7 @@ pub fn migration_anon_user_on_sign_up(
&old_collab_r_txn, &old_collab_r_txn,
new_user, new_user,
new_collab_w_txn, new_collab_w_txn,
authenticator,
)?; )?;
// Migrate other collab objects // Migrate other collab objects
@ -191,6 +194,7 @@ fn migrate_workspace_folder<'a, 'b, W>(
old_collab_r_txn: &'b W, old_collab_r_txn: &'b W,
new_user: &MigrationUser, new_user: &MigrationUser,
new_collab_w_txn: &'a W, new_collab_w_txn: &'a W,
_authenticator: &Authenticator,
) -> Result<(), PersistenceError> ) -> Result<(), PersistenceError>
where where
'a: 'b, 'a: 'b,
@ -206,9 +210,9 @@ where
old_folder_collab.with_origin_transact_mut(|txn| { old_folder_collab.with_origin_transact_mut(|txn| {
old_collab_r_txn.load_doc_with_txn(old_uid, old_workspace_id, txn) old_collab_r_txn.load_doc_with_txn(old_uid, old_workspace_id, txn)
})?; })?;
let oid_user_id = UserId::from(old_uid); let old_user_id = UserId::from(old_uid);
let old_folder = Folder::open( let old_folder = Folder::open(
oid_user_id, old_user_id.clone(),
Arc::new(MutexCollab::from_collab(old_folder_collab)), Arc::new(MutexCollab::from_collab(old_folder_collab)),
None, None,
) )
@ -219,6 +223,41 @@ where
"Can't migrate the folder data" "Can't migrate the folder data"
)))?; )))?;
if let Some(old_fav_map) = folder_data.favorites.remove(&old_user_id) {
let fav_map = old_fav_map
.into_iter()
.map(|mut item| {
let new_view_id = old_to_new_id_map.get_new_id(&item.id);
item.id = new_view_id;
item
})
.collect();
folder_data.favorites.insert(UserId::from(new_uid), fav_map);
}
if let Some(old_trash_map) = folder_data.trash.remove(&old_user_id) {
let trash_map = old_trash_map
.into_iter()
.map(|mut item| {
let new_view_id = old_to_new_id_map.get_new_id(&item.id);
item.id = new_view_id;
item
})
.collect();
folder_data.trash.insert(UserId::from(new_uid), trash_map);
}
if let Some(old_recent_map) = folder_data.recent.remove(&old_user_id) {
let recent_map = old_recent_map
.into_iter()
.map(|mut item| {
let new_view_id = old_to_new_id_map.get_new_id(&item.id);
item.id = new_view_id;
item
})
.collect();
folder_data.recent.insert(UserId::from(new_uid), recent_map);
}
old_to_new_id_map old_to_new_id_map
.0 .0
.insert(old_workspace_id.to_string(), new_workspace_id.to_string()); .insert(old_workspace_id.to_string(), new_workspace_id.to_string());

View File

@ -109,7 +109,7 @@ fn sync_view(
encode_v1.len() encode_v1.len()
); );
user_service user_service
.create_collab_object(&collab_object, encode_v1) .create_collab_object(&collab_object, encode_v1, false)
.await?; .await?;
}, },
ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => { ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => {
@ -121,7 +121,7 @@ fn sync_view(
database_encode_v1.len() database_encode_v1.len()
); );
user_service user_service
.create_collab_object(&collab_object, database_encode_v1) .create_collab_object(&collab_object, database_encode_v1, false)
.await?; .await?;
// sync database's row // sync database's row
@ -145,7 +145,7 @@ fn sync_view(
); );
let _ = user_service let _ = user_service
.create_collab_object(&database_row_collab_object, database_row_encode_v1) .create_collab_object(&database_row_collab_object, database_row_encode_v1, false)
.await; .await;
let database_row_document = CollabObject::new( let database_row_document = CollabObject::new(
@ -165,7 +165,7 @@ fn sync_view(
document_encode_v1.len() document_encode_v1.len()
); );
let _ = user_service let _ = user_service
.create_collab_object(&database_row_document, document_encode_v1) .create_collab_object(&database_row_document, document_encode_v1, false)
.await; .await;
} }
} }
@ -271,7 +271,7 @@ async fn sync_folder(
encode_v1.len() encode_v1.len()
); );
if let Err(err) = user_service if let Err(err) = user_service
.create_collab_object(&collab_object, encode_v1) .create_collab_object(&collab_object, encode_v1, true)
.await .await
{ {
tracing::error!("🔴sync folder failed: {:?}", err); tracing::error!("🔴sync folder failed: {:?}", err);
@ -316,7 +316,7 @@ async fn sync_database_views(
if let Ok((records, encode_v1)) = result { if let Ok((records, encode_v1)) = result {
if let Ok(encode_v1) = encode_v1 { if let Ok(encode_v1) = encode_v1 {
let _ = user_service let _ = user_service
.create_collab_object(&collab_object, encode_v1) .create_collab_object(&collab_object, encode_v1, false)
.await; .await;
} }

View File

@ -109,7 +109,7 @@ fn sync_view(
doc_state.len() doc_state.len()
); );
user_service user_service
.create_collab_object(&collab_object, doc_state) .create_collab_object(&collab_object, doc_state, false)
.await?; .await?;
}, },
ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => { ViewLayout::Grid | ViewLayout::Board | ViewLayout::Calendar => {
@ -121,7 +121,7 @@ fn sync_view(
database_doc_state.len() database_doc_state.len()
); );
user_service user_service
.create_collab_object(&collab_object, database_doc_state) .create_collab_object(&collab_object, database_doc_state, false)
.await?; .await?;
// sync database's row // sync database's row
@ -145,7 +145,7 @@ fn sync_view(
); );
let _ = user_service let _ = user_service
.create_collab_object(&database_row_collab_object, database_row_doc_state) .create_collab_object(&database_row_collab_object, database_row_doc_state, false)
.await; .await;
let database_row_document = CollabObject::new( let database_row_document = CollabObject::new(
@ -165,7 +165,7 @@ fn sync_view(
document_doc_state.len() document_doc_state.len()
); );
let _ = user_service let _ = user_service
.create_collab_object(&database_row_document, document_doc_state) .create_collab_object(&database_row_document, document_doc_state, false)
.await; .await;
} }
} }
@ -281,7 +281,7 @@ async fn sync_folder(
update.len() update.len()
); );
if let Err(err) = user_service if let Err(err) = user_service
.create_collab_object(&collab_object, update.to_vec()) .create_collab_object(&collab_object, update.to_vec(), false)
.await .await
{ {
tracing::error!("🔴sync folder failed: {:?}", err); tracing::error!("🔴sync folder failed: {:?}", err);
@ -325,7 +325,7 @@ async fn sync_database_views(
if let Ok((records, doc_state)) = result { if let Ok((records, doc_state)) = result {
let _ = user_service let _ = user_service
.create_collab_object(&collab_object, doc_state.to_vec()) .create_collab_object(&collab_object, doc_state.to_vec(), false)
.await; .await;
records.into_iter().map(Arc::new).collect() records.into_iter().map(Arc::new).collect()
} else { } else {

View File

@ -19,7 +19,7 @@ pub struct SignInPayloadPB {
pub name: String, pub name: String,
#[pb(index = 4)] #[pb(index = 4)]
pub auth_type: AuthTypePB, pub auth_type: AuthenticatorPB,
#[pb(index = 5)] #[pb(index = 5)]
pub device_id: String, pub device_id: String,
@ -53,7 +53,7 @@ pub struct SignUpPayloadPB {
pub password: String, pub password: String,
#[pb(index = 4)] #[pb(index = 4)]
pub auth_type: AuthTypePB, pub auth_type: AuthenticatorPB,
#[pb(index = 5)] #[pb(index = 5)]
pub device_id: String, pub device_id: String,
@ -88,7 +88,7 @@ pub struct OauthSignInPB {
pub map: HashMap<String, String>, pub map: HashMap<String, String>,
#[pb(index = 2)] #[pb(index = 2)]
pub auth_type: AuthTypePB, pub auth_type: AuthenticatorPB,
} }
#[derive(ProtoBuf, Default)] #[derive(ProtoBuf, Default)]
@ -97,7 +97,7 @@ pub struct SignInUrlPayloadPB {
pub email: String, pub email: String,
#[pb(index = 2)] #[pb(index = 2)]
pub auth_type: AuthTypePB, pub auth_type: AuthenticatorPB,
} }
#[derive(ProtoBuf, Default)] #[derive(ProtoBuf, Default)]
@ -173,13 +173,13 @@ pub struct OauthProviderDataPB {
} }
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)] #[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
pub enum AuthTypePB { pub enum AuthenticatorPB {
Local = 0, Local = 0,
Supabase = 1, Supabase = 1,
AFCloud = 2, AppFlowyCloud = 2,
} }
impl Default for AuthTypePB { impl Default for AuthenticatorPB {
fn default() -> Self { fn default() -> Self {
Self::Local Self::Local
} }
@ -232,7 +232,7 @@ impl From<UserCredentialsPB> for UserCredentials {
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
pub struct UserStatePB { pub struct UserStatePB {
#[pb(index = 1)] #[pb(index = 1)]
pub auth_type: AuthTypePB, pub auth_type: AuthenticatorPB,
} }
#[derive(ProtoBuf, Debug, Default, Clone)] #[derive(ProtoBuf, Debug, Default, Clone)]

View File

@ -7,7 +7,7 @@ use flowy_user_deps::entities::*;
use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword}; use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword};
use crate::entities::required_not_empty_str; use crate::entities::required_not_empty_str;
use crate::entities::AuthTypePB; use crate::entities::AuthenticatorPB;
use crate::errors::ErrorCode; use crate::errors::ErrorCode;
use super::parser::UserStabilityAIKey; use super::parser::UserStabilityAIKey;
@ -45,7 +45,7 @@ pub struct UserProfilePB {
pub openai_key: String, pub openai_key: String,
#[pb(index = 7)] #[pb(index = 7)]
pub auth_type: AuthTypePB, pub authenticator: AuthenticatorPB,
#[pb(index = 8)] #[pb(index = 8)]
pub encryption_sign: String, pub encryption_sign: String,
@ -85,7 +85,7 @@ impl std::convert::From<UserProfile> for UserProfilePB {
token: user_profile.token, token: user_profile.token,
icon_url: user_profile.icon_url, icon_url: user_profile.icon_url,
openai_key: user_profile.openai_key, openai_key: user_profile.openai_key,
auth_type: user_profile.authenticator.into(), authenticator: user_profile.authenticator.into(),
encryption_sign, encryption_sign,
encryption_type: encryption_ty, encryption_type: encryption_ty,
workspace_id: user_profile.workspace_id, workspace_id: user_profile.workspace_id,

View File

@ -114,7 +114,7 @@ impl std::default::Default for LocaleSettingsPB {
} }
} }
#[derive(ProtoBuf, Serialize, Deserialize, Debug, Clone)] #[derive(ProtoBuf, Serialize, Deserialize, Debug, Clone, Default)]
pub struct DocumentSettingsPB { pub struct DocumentSettingsPB {
#[pb(index = 1, one_of)] #[pb(index = 1, one_of)]
pub cursor_color: Option<String>, pub cursor_color: Option<String>,
@ -123,15 +123,6 @@ pub struct DocumentSettingsPB {
pub selection_color: Option<String>, pub selection_color: Option<String>,
} }
impl std::default::Default for DocumentSettingsPB {
fn default() -> Self {
Self {
cursor_color: None,
selection_color: None,
}
}
}
pub const APPEARANCE_DEFAULT_THEME: &str = "Default"; pub const APPEARANCE_DEFAULT_THEME: &str = "Default";
pub const APPEARANCE_DEFAULT_FONT: &str = "Poppins"; pub const APPEARANCE_DEFAULT_FONT: &str = "Poppins";
pub const APPEARANCE_DEFAULT_MONOSPACE_FONT: &str = "SF Mono"; pub const APPEARANCE_DEFAULT_MONOSPACE_FONT: &str = "SF Mono";

View File

@ -35,6 +35,7 @@ use crate::migrations::document_empty_content::HistoricalEmptyDocumentMigration;
use crate::migrations::migration::{UserDataMigration, UserLocalDataMigration}; use crate::migrations::migration::{UserDataMigration, UserLocalDataMigration};
use crate::migrations::session_migration::migrate_session_with_user_uuid; use crate::migrations::session_migration::migrate_session_with_user_uuid;
use crate::migrations::workspace_and_favorite_v1::FavoriteV1AndWorkspaceArrayMigration; use crate::migrations::workspace_and_favorite_v1::FavoriteV1AndWorkspaceArrayMigration;
use crate::migrations::workspace_trash_v1::WorkspaceTrashMapToSectionMigration;
use crate::migrations::MigrationUser; use crate::migrations::MigrationUser;
use crate::services::cloud_config::get_cloud_config; use crate::services::cloud_config::get_cloud_config;
use crate::services::collab_interact::{CollabInteract, DefaultCollabInteract}; use crate::services::collab_interact::{CollabInteract, DefaultCollabInteract};
@ -198,16 +199,18 @@ impl UserManager {
let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?); let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?);
if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() { if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() {
event!(tracing::Level::DEBUG, "Listen token state change"); event!(tracing::Level::DEBUG, "Listen token state change");
let user_uid = user.uid;
let user_token = user.token.clone();
af_spawn(async move { af_spawn(async move {
while let Some(token_state) = token_state_rx.next().await { while let Some(token_state) = token_state_rx.next().await {
debug!("Token state changed: {:?}", token_state); debug!("Token state changed: {:?}", token_state);
match token_state { match token_state {
UserTokenState::Refresh { token } => { UserTokenState::Refresh { token } => {
// Only save the token if the token is different from the current token // Only save the token if the token is different from the current token
if token != user.token { if token != user_token {
if let Some(pool) = weak_pool.upgrade() { if let Some(pool) = weak_pool.upgrade() {
// Save the new token // Save the new token
if let Err(err) = save_user_token(user.uid, pool, token) { if let Err(err) = save_user_token(user_uid, pool, token) {
error!("Save user token failed: {}", err); error!("Save user token failed: {}", err);
} }
} }
@ -232,9 +235,10 @@ impl UserManager {
let migrations: Vec<Box<dyn UserDataMigration>> = vec![ let migrations: Vec<Box<dyn UserDataMigration>> = vec![
Box::new(HistoricalEmptyDocumentMigration), Box::new(HistoricalEmptyDocumentMigration),
Box::new(FavoriteV1AndWorkspaceArrayMigration), Box::new(FavoriteV1AndWorkspaceArrayMigration),
Box::new(WorkspaceTrashMapToSectionMigration),
]; ];
match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool) match UserLocalDataMigration::new(session.clone(), collab_db, sqlite_pool)
.run(migrations, &current_authenticator) .run(migrations, &user.authenticator)
{ {
Ok(applied_migrations) => { Ok(applied_migrations) => {
if !applied_migrations.is_empty() { if !applied_migrations.is_empty() {
@ -786,7 +790,13 @@ impl UserManager {
) -> Result<(), FlowyError> { ) -> Result<(), FlowyError> {
let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?; let old_collab_db = self.database.get_collab_db(old_user.session.user_id)?;
let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?; let new_collab_db = self.database.get_collab_db(new_user.session.user_id)?;
migration_anon_user_on_sign_up(old_user, &old_collab_db, new_user, &new_collab_db)?; migration_anon_user_on_sign_up(
old_user,
&old_collab_db,
new_user,
&new_collab_db,
authenticator,
)?;
match authenticator { match authenticator {
Authenticator::Supabase => { Authenticator::Supabase => {

View File

@ -1,11 +1,13 @@
use crate::services::entities::Session;
use flowy_user_deps::entities::UserProfile; use flowy_user_deps::entities::UserProfile;
use crate::services::entities::Session;
pub mod document_empty_content; pub mod document_empty_content;
pub mod migration; pub mod migration;
pub mod session_migration; pub mod session_migration;
mod util; mod util;
pub mod workspace_and_favorite_v1; pub mod workspace_and_favorite_v1;
pub mod workspace_trash_v1;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MigrationUser { pub struct MigrationUser {

View File

@ -15,17 +15,16 @@ pub fn migrate_session_with_user_uuid(
session: &Arc<parking_lot::RwLock<Option<Session>>>, session: &Arc<parking_lot::RwLock<Option<Session>>>,
store_preferences: &Arc<StorePreferences>, store_preferences: &Arc<StorePreferences>,
) { ) {
if !store_preferences.get_bool(MIGRATION_USER_NO_USER_UUID) { if !store_preferences.get_bool(MIGRATION_USER_NO_USER_UUID)
if store_preferences && store_preferences
.set_bool(MIGRATION_USER_NO_USER_UUID, true) .set_bool(MIGRATION_USER_NO_USER_UUID, true)
.is_ok() .is_ok()
{ {
if let Some(mut value) = store_preferences.get_object::<Value>(&user_config.session_cache_key) if let Some(mut value) = store_preferences.get_object::<Value>(&user_config.session_cache_key) {
{
if value.get("user_uuid").is_none() { if value.get("user_uuid").is_none() {
value.as_object_mut().map(|map| { if let Some(map) = value.as_object_mut() {
map.insert("user_uuid".to_string(), json!(Uuid::new_v4())); map.insert("user_uuid".to_string(), json!(Uuid::new_v4()));
}); }
} }
if let Ok(new_session) = serde_json::from_value::<Session>(value) { if let Ok(new_session) = serde_json::from_value::<Session>(value) {
@ -35,4 +34,3 @@ pub fn migrate_session_with_user_uuid(
} }
} }
} }
}

View File

@ -0,0 +1,56 @@
use std::sync::Arc;
use collab_folder::Folder;
use tracing::instrument;
use collab_integrate::{RocksCollabDB, YrsDocAction};
use flowy_error::{internal_error, FlowyResult};
use flowy_user_deps::entities::Authenticator;
use crate::migrations::migration::UserDataMigration;
use crate::migrations::util::load_collab;
use crate::services::entities::Session;
/// 1. Migrate the workspace: { trash: [view_id] } to { trash: { uid: [view_id] } }
pub struct WorkspaceTrashMapToSectionMigration;
impl UserDataMigration for WorkspaceTrashMapToSectionMigration {
fn name(&self) -> &str {
"workspace_trash_map_to_section_migration"
}
#[instrument(name = "WorkspaceTrashMapToSectionMigration", skip_all, err)]
fn run(
&self,
session: &Session,
collab_db: &Arc<RocksCollabDB>,
_authenticator: &Authenticator,
) -> FlowyResult<()> {
let write_txn = collab_db.write_txn();
if let Ok(collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id) {
let folder = Folder::open(session.user_id, collab, None)?;
let trash_ids = folder
.get_trash_v1()
.into_iter()
.map(|fav| fav.id)
.collect::<Vec<String>>();
if !trash_ids.is_empty() {
folder.add_trash(trash_ids);
}
let encode = folder.encode_collab_v1();
write_txn
.flush_doc_with(
session.user_id,
&session.user_workspace.id,
&encode.doc_state,
&encode.state_vector,
)
.map_err(internal_error)?;
}
write_txn.commit_transaction().map_err(internal_error)?;
Ok(())
}
}

View File

@ -11,7 +11,7 @@ use uuid::Uuid;
use flowy_user_deps::entities::{AuthResponse, UserProfile, UserWorkspace}; use flowy_user_deps::entities::{AuthResponse, UserProfile, UserWorkspace};
use flowy_user_deps::entities::{Authenticator, UserAuthResponse}; use flowy_user_deps::entities::{Authenticator, UserAuthResponse};
use crate::entities::AuthTypePB; use crate::entities::AuthenticatorPB;
use crate::migrations::MigrationUser; use crate::migrations::MigrationUser;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
@ -99,7 +99,7 @@ where
fn from(value: &T) -> Self { fn from(value: &T) -> Self {
Self { Self {
user_id: value.user_id(), user_id: value.user_id(),
user_uuid: value.user_uuid().clone(), user_uuid: *value.user_uuid(),
user_workspace: value.latest_workspace().clone(), user_workspace: value.latest_workspace().clone(),
} }
} }
@ -114,22 +114,22 @@ impl std::convert::From<Session> for String {
} }
} }
impl From<AuthTypePB> for Authenticator { impl From<AuthenticatorPB> for Authenticator {
fn from(pb: AuthTypePB) -> Self { fn from(pb: AuthenticatorPB) -> Self {
match pb { match pb {
AuthTypePB::Supabase => Authenticator::Supabase, AuthenticatorPB::Supabase => Authenticator::Supabase,
AuthTypePB::Local => Authenticator::Local, AuthenticatorPB::Local => Authenticator::Local,
AuthTypePB::AFCloud => Authenticator::AppFlowyCloud, AuthenticatorPB::AppFlowyCloud => Authenticator::AppFlowyCloud,
} }
} }
} }
impl From<Authenticator> for AuthTypePB { impl From<Authenticator> for AuthenticatorPB {
fn from(auth_type: Authenticator) -> Self { fn from(auth_type: Authenticator) -> Self {
match auth_type { match auth_type {
Authenticator::Supabase => AuthTypePB::Supabase, Authenticator::Supabase => AuthenticatorPB::Supabase,
Authenticator::Local => AuthTypePB::Local, Authenticator::Local => AuthenticatorPB::Local,
Authenticator::AppFlowyCloud => AuthTypePB::AFCloud, Authenticator::AppFlowyCloud => AuthenticatorPB::AppFlowyCloud,
} }
} }
} }

View File

@ -6,7 +6,7 @@ use tracing::subscriber::set_global_default;
use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender}; use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender};
use tracing_bunyan_formatter::JsonStorageLayer; use tracing_bunyan_formatter::JsonStorageLayer;
use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter};
use crate::layer::FlowyFormattingLayer; use crate::layer::FlowyFormattingLayer;
@ -37,22 +37,25 @@ impl Builder {
self self
} }
pub fn build(self) -> std::result::Result<(), String> { pub fn build(self) -> Result<(), String> {
let env_filter = EnvFilter::new(self.env_filter); let env_filter = EnvFilter::new(self.env_filter);
let std_out_layer = fmt::layer().with_writer(std::io::stdout).pretty();
let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender); let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
let file_layer = FlowyFormattingLayer::new(non_blocking);
let subscriber = tracing_subscriber::fmt() let subscriber = tracing_subscriber::fmt()
.with_timer(CustomTime) .with_timer(CustomTime)
.with_ansi(true) .with_ansi(true)
.with_target(false) .with_target(false)
.with_max_level(tracing::Level::TRACE) .with_max_level(tracing::Level::TRACE)
.with_thread_ids(false) .with_thread_ids(false)
.with_writer(std::io::stderr)
.pretty() .pretty()
.with_env_filter(env_filter) .with_env_filter(env_filter)
.finish() .finish()
.with(JsonStorageLayer) .with(JsonStorageLayer)
.with(FlowyFormattingLayer::new(non_blocking)); .with(file_layer)
.with(std_out_layer);
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?; set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;

View File

@ -1,11 +1,11 @@
# If you want to test a single file with single case, you can try this command: # If you want to test a single file with single case, you can try this command:
# RUST_LOG="debug" flutter test -j, --concurrency=1 'path to the file' --name 'test case name' # RUST_LOG="debug" flutter test -d macOS -j, 'path to the file' --name 'test case name'
[tasks.flutter_test] [tasks.flutter_test]
description = "Run flutter test with single case in single file. Input: cargo make flutter_test 'path to the file' --name 'test case name'" description = "Run flutter test with single case in single file. Input: cargo make flutter_test 'path to the file' --name 'test case name'"
script = ''' script = '''
cd appflowy_flutter cd appflowy_flutter
RUST_LOG="debug" flutter test -j, --concurrency=1 "${@}" flutter test -j, --concurrency=1 "${@}"
''' '''
script_runner = "@shell" script_runner = "@shell"