mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
tests: AppFlowy Cloud integration test (#4015)
* chore: save cloud ofnig * chore: fix .a link warnings * chore: add cloud test runner * refactor: test folder * ci: add test * ci: add test * ci: fix * ci: fix
This commit is contained in:
parent
9d61ca0278
commit
3e17613f54
121
.github/workflows/flutter_ci.yaml
vendored
121
.github/workflows/flutter_ci.yaml
vendored
@ -119,7 +119,8 @@ jobs:
|
||||
run: flutter analyze .
|
||||
|
||||
- name: Compress appflowy_flutter
|
||||
run: tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
run: |
|
||||
tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@ -297,17 +298,9 @@ jobs:
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
# - name: Create .env.cloud.test file in flowy-server
|
||||
# working-directory: frontend/appflowy_flutter
|
||||
# run: |
|
||||
# touch .env.cloud.test
|
||||
# echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.cloud.test
|
||||
# echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.cloud.test
|
||||
# echo APPFLOWY_CLOUD_URL=${{ secrets.APPFLOWY_CLOUD_URL }} >> .env.cloud.test
|
||||
|
||||
- name: Run flutter pub get
|
||||
working-directory: frontend
|
||||
run: cargo make pub_get
|
||||
@ -327,19 +320,101 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
cloud_integration_test:
|
||||
needs: [prepare]
|
||||
# if: github.event_name != 'pull_request'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_integrateion_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
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: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: |
|
||||
tar -xf appflowy_flutter.tar.gz
|
||||
ls -al
|
||||
|
||||
- name: Enable localhost env
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
echo 'APPFLOWY_CLOUD_URL=http://localhost' >> .env
|
||||
dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
- name: Run flutter pub get
|
||||
working-directory: frontend
|
||||
run: cargo make pub_get
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
export DISPLAY=:99
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
flutter test integration_test/cloud_runner.dart -d Linux --coverage
|
||||
shell: bash
|
||||
|
||||
build:
|
||||
needs: [prepare]
|
||||
@ -411,7 +486,7 @@ jobs:
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Build flutter product
|
||||
|
@ -28,6 +28,7 @@ LIB_NAME = "dart_ffi"
|
||||
CURRENT_APP_VERSION = "0.3.8"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||
# If you update the macOS's CRATE_TYPE, don't forget to update the
|
||||
# appflowy_backend.podspec
|
||||
|
1
frontend/appflowy_flutter/dev.env
Normal file
1
frontend/appflowy_flutter/dev.env
Normal file
@ -0,0 +1 @@
|
||||
APPFLOWY_CLOUD_URL=
|
@ -4,6 +4,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/settings_appear
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
|
@ -0,0 +1,123 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.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:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/mock/mock_file_picker.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('appflowy cloud auth', () {
|
||||
testWidgets('sign in', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloud,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
tester.expectToSeeHomePage();
|
||||
});
|
||||
|
||||
// testWidgets('sign out', (tester) async {
|
||||
// await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloud,
|
||||
// );
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // Open the setting page and sign out
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.user);
|
||||
// await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
// tester.expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
// await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
|
||||
// // Go to the sign in page again
|
||||
// await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
// tester.expectToSeeGoogleLoginButton();
|
||||
// });
|
||||
|
||||
// testWidgets('sign in as annoymous', (tester) async {
|
||||
// await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloud,
|
||||
// );
|
||||
// await tester.tapSignInAsGuest();
|
||||
|
||||
// // should not see the sync setting page when sign in as annoymous
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.user);
|
||||
// tester.expectToSeeGoogleLoginButton();
|
||||
// });
|
||||
|
||||
// testWidgets('enable sync', (tester) async {
|
||||
// await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloud,
|
||||
// );
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // Open the setting page and sign out
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// // the switch should be on by default
|
||||
// tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
// await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
|
||||
// // the switch should be off
|
||||
// tester.assertAppFlowyCloudEnableSyncSwitchValue(false);
|
||||
|
||||
// // the switch should be on after toggling
|
||||
// await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
// tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
// });
|
||||
|
||||
// testWidgets('custom folder sign in', (tester) async {
|
||||
// const userA = 'UserA';
|
||||
// final initialPath = p.join(userA, appFlowyDataFolder);
|
||||
// final context = await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloud,
|
||||
// pathExtension: initialPath,
|
||||
// );
|
||||
// // remove the last extension
|
||||
// final rootPath = context.applicationDataDirectory.replaceFirst(
|
||||
// initialPath,
|
||||
// '',
|
||||
// );
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // Open the setting page and sign out
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.user);
|
||||
// await tester.enterUserName(userA);
|
||||
|
||||
// await tester.openSettingsPage(SettingsPage.files);
|
||||
// await tester.pumpAndSettle();
|
||||
|
||||
// // mock the file_picker result
|
||||
// await mockGetDirectoryPath(
|
||||
// p.join(rootPath, "random_folder"),
|
||||
// );
|
||||
|
||||
// // after selecting the folder, an annoymous user should be signed in
|
||||
// await tester.tapCustomLocationButton();
|
||||
// tester.expectToSeeHomePage();
|
||||
// await tester.pumpAndSettle();
|
||||
|
||||
// // Login as userA in custom folder
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.user);
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
// tester.expectToSeeHomePage();
|
||||
// // UserA should be displayed
|
||||
// tester.expectToSeeUserName(userA);
|
||||
// });
|
||||
});
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.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_test/flutter_test.dart';
|
||||
@ -10,15 +11,15 @@ import '../util/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('auth', () {
|
||||
group('supabase auth', () {
|
||||
testWidgets('sign in with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: CloudType.supabase);
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
tester.expectToSeeHomePage();
|
||||
});
|
||||
|
||||
testWidgets('sign out with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: CloudType.supabase);
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
@ -35,7 +36,7 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('sign in as annoymous', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: CloudType.supabase);
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapSignInAsGuest();
|
||||
|
||||
// should not see the sync setting page when sign in as annoymous
|
||||
@ -65,7 +66,7 @@ void main() {
|
||||
// });
|
||||
|
||||
testWidgets('enable sync', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: CloudType.supabase);
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
@ -73,15 +74,15 @@ void main() {
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync();
|
||||
tester.assertSupabaseEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync(SupabaseEnableSync);
|
||||
|
||||
// the switch should be off
|
||||
tester.assertEnableSyncSwitchValue(false);
|
||||
tester.assertSupabaseEnableSyncSwitchValue(false);
|
||||
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync();
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync(SupabaseEnableSync);
|
||||
tester.assertSupabaseEnableSyncSwitchValue(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
13
frontend/appflowy_flutter/integration_test/cloud_runner.dart
Normal file
13
frontend/appflowy_flutter/integration_test/cloud_runner.dart
Normal file
@ -0,0 +1,13 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'auth/appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
|
||||
import 'empty_test.dart' as first_test;
|
||||
|
||||
Future<void> main() async {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// This test must be run first, otherwise the CI will fail.
|
||||
first_test.main();
|
||||
|
||||
appflowy_cloud_auth_test.main();
|
||||
}
|
@ -3,8 +3,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -4,8 +4,8 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -4,8 +4,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -6,8 +6,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -4,7 +4,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -7,9 +7,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/emoji.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/emoji.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -2,8 +2,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -3,8 +3,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -2,7 +2,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -2,7 +2,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -3,8 +3,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
@ -2,17 +2,18 @@ import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'appearance_settings_test.dart' as appearance_test_runner;
|
||||
import 'board/board_test_runner.dart' as board_test_runner;
|
||||
import 'database_calendar_test.dart' as database_calendar_test;
|
||||
import 'database_cell_test.dart' as database_cell_test;
|
||||
import 'database_field_settings_test.dart' as database_field_settings_test;
|
||||
import 'database_field_test.dart' as database_field_test;
|
||||
import 'database_filter_test.dart' as database_filter_test;
|
||||
import 'database_row_page_test.dart' as database_row_page_test;
|
||||
import 'database_row_test.dart' as database_row_test;
|
||||
import 'database_setting_test.dart' as database_setting_test;
|
||||
import 'database_share_test.dart' as database_share_test;
|
||||
import 'database_sort_test.dart' as database_sort_test;
|
||||
import 'database_view_test.dart' as database_view_test;
|
||||
import 'database/database_calendar_test.dart' as database_calendar_test;
|
||||
import 'database/database_cell_test.dart' as database_cell_test;
|
||||
import 'database/database_field_settings_test.dart'
|
||||
as database_field_settings_test;
|
||||
import 'database/database_field_test.dart' as database_field_test;
|
||||
import 'database/database_filter_test.dart' as database_filter_test;
|
||||
import 'database/database_row_page_test.dart' as database_row_page_test;
|
||||
import 'database/database_row_test.dart' as database_row_test;
|
||||
import 'database/database_setting_test.dart' as database_setting_test;
|
||||
import 'database/database_share_test.dart' as database_share_test;
|
||||
import 'database/database_sort_test.dart' as database_sort_test;
|
||||
import 'database/database_view_test.dart' as database_view_test;
|
||||
import 'document/document_test_runner.dart' as document_test_runner;
|
||||
import 'empty_test.dart' as first_test;
|
||||
import 'hotkeys_test.dart' as hotkeys_test;
|
||||
@ -75,8 +76,6 @@ Future<void> main() async {
|
||||
// User settings
|
||||
settings_test_runner.main();
|
||||
|
||||
// supabase_auth_test_runner.main();
|
||||
|
||||
// board_test.main();
|
||||
// empty_document_test.main();
|
||||
// smart_menu_test.main();
|
||||
|
@ -1,4 +1,5 @@
|
||||
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_supabase_cloud.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@ -34,7 +35,7 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
);
|
||||
}
|
||||
|
||||
void assertEnableSyncSwitchValue(bool value) {
|
||||
void assertSupabaseEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
of: find.byType(SupabaseEnableSync),
|
||||
@ -44,6 +45,16 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
);
|
||||
}
|
||||
|
||||
void assertAppFlowyCloudEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyCloudEnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> toggleEnableEncrypt() async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(EnableEncrypt),
|
||||
@ -53,9 +64,9 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<void> toggleEnableSync() async {
|
||||
Future<void> toggleEnableSync(Type syncButton) async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(SupabaseEnableSync),
|
||||
of: find.byType(syncButton),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
);
|
||||
|
||||
|
@ -31,7 +31,7 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
// use to append after the application data directory
|
||||
String? pathExtension,
|
||||
Size windowsSize = const Size(1600, 1200),
|
||||
CloudType? cloudType,
|
||||
AuthenticatorType? cloudType,
|
||||
}) async {
|
||||
binding.setSurfaceSize(windowsSize);
|
||||
|
||||
@ -45,16 +45,32 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
IntegrationMode.integrationTest,
|
||||
didInitGetIt: Future(
|
||||
rustEnvsBuilder: () {
|
||||
final rustEnvs = <String, String>{};
|
||||
if (cloudType != null) {
|
||||
switch (cloudType) {
|
||||
case AuthenticatorType.local:
|
||||
break;
|
||||
case AuthenticatorType.supabase:
|
||||
break;
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
rustEnvs["GOTRUE_ADMIN_EMAIL"] = "admin@example.com";
|
||||
rustEnvs["GOTRUE_ADMIN_PASSWORD"] = "password";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rustEnvs;
|
||||
},
|
||||
didInitGetItCallback: Future(
|
||||
() async {
|
||||
if (cloudType != null) {
|
||||
switch (cloudType) {
|
||||
case CloudType.local:
|
||||
case AuthenticatorType.local:
|
||||
break;
|
||||
case CloudType.supabase:
|
||||
case AuthenticatorType.supabase:
|
||||
await useSupabaseCloud();
|
||||
break;
|
||||
case CloudType.appflowyCloud:
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
await useAppFlowyCloud();
|
||||
break;
|
||||
}
|
||||
@ -227,7 +243,7 @@ extension AppFlowyFinderTestBase on CommonFinders {
|
||||
}
|
||||
|
||||
Future<void> useSupabaseCloud() async {
|
||||
await setCloudType(CloudType.supabase);
|
||||
await setAuthenticatorType(AuthenticatorType.supabase);
|
||||
await setSupbaseServer(
|
||||
Some(TestEnv.supabaseUrl),
|
||||
Some(TestEnv.supabaseAnonKey),
|
||||
@ -235,6 +251,6 @@ Future<void> useSupabaseCloud() async {
|
||||
}
|
||||
|
||||
Future<void> useAppFlowyCloud() async {
|
||||
await setCloudType(CloudType.appflowyCloud);
|
||||
await setAuthenticatorType(AuthenticatorType.appflowyCloud);
|
||||
await setAppFlowyCloudUrl(Some(TestEnv.afCloudUrl));
|
||||
}
|
||||
|
@ -5,20 +5,24 @@ part 'backend_env.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class AppFlowyConfiguration {
|
||||
final String root;
|
||||
final String custom_app_path;
|
||||
final String origin_app_path;
|
||||
final String device_id;
|
||||
final int cloud_type;
|
||||
final int authenticator_type;
|
||||
final SupabaseConfiguration supabase_config;
|
||||
final AppFlowyCloudConfiguration appflowy_cloud_config;
|
||||
final Map<String, String> envs;
|
||||
|
||||
AppFlowyConfiguration({
|
||||
required this.root,
|
||||
required this.custom_app_path,
|
||||
required this.origin_app_path,
|
||||
required this.device_id,
|
||||
required this.cloud_type,
|
||||
required this.authenticator_type,
|
||||
required this.supabase_config,
|
||||
required this.appflowy_cloud_config,
|
||||
required this.envs,
|
||||
});
|
||||
|
||||
factory AppFlowyConfiguration.fromJson(Map<String, dynamic> json) =>
|
||||
|
127
frontend/appflowy_flutter/lib/env/cloud_env.dart
vendored
127
frontend/appflowy_flutter/lib/env/cloud_env.dart
vendored
@ -1,6 +1,7 @@
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/env/backend_env.dart';
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@ -9,22 +10,22 @@ import 'package:dartz/dartz.dart';
|
||||
///
|
||||
/// This method updates the cloud type setting in the key-value storage
|
||||
/// using the [KeyValueStorage] service. The cloud type is identified
|
||||
/// by the [CloudType] enum.
|
||||
/// by the [AuthenticatorType] enum.
|
||||
///
|
||||
/// [ty] - The type of cloud to be set. It must be one of the values from
|
||||
/// [CloudType] enum. The corresponding integer value of the enum is stored:
|
||||
/// [AuthenticatorType] enum. The corresponding integer value of the enum is stored:
|
||||
/// - `CloudType.local` is stored as "0".
|
||||
/// - `CloudType.supabase` is stored as "1".
|
||||
/// - `CloudType.appflowyCloud` is stored as "2".
|
||||
Future<void> setCloudType(CloudType ty) async {
|
||||
Future<void> setAuthenticatorType(AuthenticatorType ty) async {
|
||||
switch (ty) {
|
||||
case CloudType.local:
|
||||
case AuthenticatorType.local:
|
||||
getIt<KeyValueStorage>().set(KVKeys.kCloudType, 0.toString());
|
||||
break;
|
||||
case CloudType.supabase:
|
||||
case AuthenticatorType.supabase:
|
||||
getIt<KeyValueStorage>().set(KVKeys.kCloudType, 1.toString());
|
||||
break;
|
||||
case CloudType.appflowyCloud:
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
getIt<KeyValueStorage>().set(KVKeys.kCloudType, 2.toString());
|
||||
break;
|
||||
}
|
||||
@ -34,25 +35,25 @@ Future<void> setCloudType(CloudType ty) async {
|
||||
///
|
||||
/// This method fetches the cloud type setting from the key-value storage
|
||||
/// using the [KeyValueStorage] service and returns the corresponding
|
||||
/// [CloudType] enum value.
|
||||
/// [AuthenticatorType] enum value.
|
||||
///
|
||||
/// Returns:
|
||||
/// A Future that resolves to a [CloudType] enum value representing the
|
||||
/// A Future that resolves to a [AuthenticatorType] enum value representing the
|
||||
/// currently set cloud type. The default return value is `CloudType.local`
|
||||
/// if no valid setting is found.
|
||||
///
|
||||
Future<CloudType> getCloudType() async {
|
||||
Future<AuthenticatorType> getAuthenticatorType() async {
|
||||
final value = await getIt<KeyValueStorage>().get(KVKeys.kCloudType);
|
||||
return value.fold(() => CloudType.local, (s) {
|
||||
return value.fold(() => AuthenticatorType.local, (s) {
|
||||
switch (s) {
|
||||
case "0":
|
||||
return CloudType.local;
|
||||
return AuthenticatorType.local;
|
||||
case "1":
|
||||
return CloudType.supabase;
|
||||
return AuthenticatorType.supabase;
|
||||
case "2":
|
||||
return CloudType.appflowyCloud;
|
||||
return AuthenticatorType.appflowyCloud;
|
||||
default:
|
||||
return CloudType.local;
|
||||
return AuthenticatorType.local;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -70,17 +71,15 @@ Future<CloudType> getCloudType() async {
|
||||
/// Returns `false` otherwise.
|
||||
bool get isAuthEnabled {
|
||||
// Only enable supabase in release and develop mode.
|
||||
if (integrationMode().isRelease || integrationMode().isDevelop) {
|
||||
if (integrationMode().isRelease ||
|
||||
integrationMode().isDevelop ||
|
||||
integrationMode().isIntegrationTest) {
|
||||
final env = getIt<AppFlowyCloudSharedEnv>();
|
||||
if (env.cloudType == CloudType.local) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (env.cloudType == CloudType.supabase) {
|
||||
if (env.authenticatorType == AuthenticatorType.supabase) {
|
||||
return env.supabaseConfig.isValid;
|
||||
}
|
||||
|
||||
if (env.cloudType == CloudType.appflowyCloud) {
|
||||
if (env.authenticatorType == AuthenticatorType.appflowyCloud) {
|
||||
return env.appflowyCloudConfig.isValid;
|
||||
}
|
||||
|
||||
@ -101,8 +100,10 @@ bool get isAuthEnabled {
|
||||
/// is `CloudType.supabase`. Otherwise, it returns `false`.
|
||||
bool get isSupabaseEnabled {
|
||||
// Only enable supabase in release and develop mode.
|
||||
if (integrationMode().isRelease || integrationMode().isDevelop) {
|
||||
return currentCloudType() == CloudType.supabase;
|
||||
if (integrationMode().isRelease ||
|
||||
integrationMode().isDevelop ||
|
||||
integrationMode().isIntegrationTest) {
|
||||
return currentCloudType() == AuthenticatorType.supabase;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -119,26 +120,28 @@ bool get isSupabaseEnabled {
|
||||
/// cloud type is `CloudType.appflowyCloud`. Otherwise, it returns `false`.
|
||||
bool get isAppFlowyCloudEnabled {
|
||||
// Only enable appflowy cloud in release and develop mode.
|
||||
if (integrationMode().isRelease || integrationMode().isDevelop) {
|
||||
return currentCloudType() == CloudType.appflowyCloud;
|
||||
if (integrationMode().isRelease ||
|
||||
integrationMode().isDevelop ||
|
||||
integrationMode().isIntegrationTest) {
|
||||
return currentCloudType() == AuthenticatorType.appflowyCloud;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum CloudType {
|
||||
enum AuthenticatorType {
|
||||
local,
|
||||
supabase,
|
||||
appflowyCloud;
|
||||
|
||||
bool get isEnabled => this != CloudType.local;
|
||||
bool get isEnabled => this != AuthenticatorType.local;
|
||||
int get value {
|
||||
switch (this) {
|
||||
case CloudType.local:
|
||||
case AuthenticatorType.local:
|
||||
return 0;
|
||||
case CloudType.supabase:
|
||||
case AuthenticatorType.supabase:
|
||||
return 1;
|
||||
case CloudType.appflowyCloud:
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@ -146,19 +149,19 @@ enum CloudType {
|
||||
static fromValue(int value) {
|
||||
switch (value) {
|
||||
case 0:
|
||||
return CloudType.local;
|
||||
return AuthenticatorType.local;
|
||||
case 1:
|
||||
return CloudType.supabase;
|
||||
return AuthenticatorType.supabase;
|
||||
case 2:
|
||||
return CloudType.appflowyCloud;
|
||||
return AuthenticatorType.appflowyCloud;
|
||||
default:
|
||||
return CloudType.local;
|
||||
return AuthenticatorType.local;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloudType currentCloudType() {
|
||||
return getIt<AppFlowyCloudSharedEnv>().cloudType;
|
||||
AuthenticatorType currentCloudType() {
|
||||
return getIt<AppFlowyCloudSharedEnv>().authenticatorType;
|
||||
}
|
||||
|
||||
Future<void> setAppFlowyCloudUrl(Option<String> url) async {
|
||||
@ -170,22 +173,52 @@ Future<void> setAppFlowyCloudUrl(Option<String> url) async {
|
||||
|
||||
/// Use getIt<AppFlowyCloudSharedEnv>() to get the shared environment.
|
||||
class AppFlowyCloudSharedEnv {
|
||||
final CloudType cloudType;
|
||||
final AuthenticatorType _authenticatorType;
|
||||
final AppFlowyCloudConfiguration appflowyCloudConfig;
|
||||
final SupabaseConfiguration supabaseConfig;
|
||||
|
||||
AppFlowyCloudSharedEnv({
|
||||
required this.cloudType,
|
||||
required AuthenticatorType authenticatorType,
|
||||
required this.appflowyCloudConfig,
|
||||
required this.supabaseConfig,
|
||||
});
|
||||
}) : _authenticatorType = authenticatorType;
|
||||
|
||||
AuthenticatorType get authenticatorType => _authenticatorType;
|
||||
|
||||
static Future<AppFlowyCloudSharedEnv> fromEnv() async {
|
||||
if (Env.enableCustomCloud) {
|
||||
// Use the custom cloud configuration.
|
||||
final cloudType = await getAuthenticatorType();
|
||||
final appflowyCloudConfig = await getAppFlowyCloudConfig();
|
||||
final supabaseCloudConfig = await getSupabaseCloudConfig();
|
||||
|
||||
return AppFlowyCloudSharedEnv(
|
||||
authenticatorType: cloudType,
|
||||
appflowyCloudConfig: appflowyCloudConfig,
|
||||
supabaseConfig: supabaseCloudConfig,
|
||||
);
|
||||
} else {
|
||||
final appflowyCloudConfig = AppFlowyCloudConfiguration(
|
||||
base_url: Env.afCloudUrl,
|
||||
ws_base_url: await _getAppFlowyCloudWSUrl(Env.afCloudUrl),
|
||||
gotrue_url: await _getAppFlowyCloudGotrueUrl(Env.afCloudUrl),
|
||||
);
|
||||
|
||||
return AppFlowyCloudSharedEnv(
|
||||
authenticatorType: AuthenticatorType.fromValue(Env.authenticatorType),
|
||||
appflowyCloudConfig: appflowyCloudConfig,
|
||||
supabaseConfig: SupabaseConfiguration.defaultConfig(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<AppFlowyCloudConfiguration> getAppFlowyCloudConfig() async {
|
||||
final baseURL = await getAppFlowyCloudUrl();
|
||||
return AppFlowyCloudConfiguration(
|
||||
base_url: await getAppFlowyCloudUrl(),
|
||||
ws_base_url: await _getAppFlowyCloudWSUrl(),
|
||||
gotrue_url: await _getAppFlowyCloudGotrueUrl(),
|
||||
base_url: baseURL,
|
||||
ws_base_url: await _getAppFlowyCloudWSUrl(baseURL),
|
||||
gotrue_url: await _getAppFlowyCloudGotrueUrl(baseURL),
|
||||
);
|
||||
}
|
||||
|
||||
@ -198,10 +231,9 @@ Future<String> getAppFlowyCloudUrl() async {
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> _getAppFlowyCloudWSUrl() async {
|
||||
Future<String> _getAppFlowyCloudWSUrl(String baseURL) async {
|
||||
try {
|
||||
final serverUrl = await getAppFlowyCloudUrl();
|
||||
final uri = Uri.parse(serverUrl);
|
||||
final uri = Uri.parse(baseURL);
|
||||
|
||||
// Construct the WebSocket URL directly from the parsed URI.
|
||||
final wsScheme = uri.isScheme('HTTPS') ? 'wss' : 'ws';
|
||||
@ -214,9 +246,8 @@ Future<String> _getAppFlowyCloudWSUrl() async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _getAppFlowyCloudGotrueUrl() async {
|
||||
final serverUrl = await getAppFlowyCloudUrl();
|
||||
return "$serverUrl/gotrue";
|
||||
Future<String> _getAppFlowyCloudGotrueUrl(String baseURL) async {
|
||||
return "$baseURL/gotrue";
|
||||
}
|
||||
|
||||
Future<void> setSupbaseServer(
|
||||
|
28
frontend/appflowy_flutter/lib/env/env.dart
vendored
Normal file
28
frontend/appflowy_flutter/lib/env/env.dart
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// lib/env/env.dart
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:envied/envied.dart';
|
||||
|
||||
part 'env.g.dart';
|
||||
|
||||
@Envied(path: '.env')
|
||||
abstract class Env {
|
||||
static bool get enableCustomCloud {
|
||||
return Env.authenticatorType == AuthenticatorType.appflowyCloud.value &&
|
||||
_Env.afCloudUrl.isEmpty;
|
||||
}
|
||||
|
||||
@EnviedField(
|
||||
obfuscate: false,
|
||||
varName: 'AUTHENTICATOR_TYPE',
|
||||
defaultValue: 2,
|
||||
)
|
||||
static const int authenticatorType = _Env.authenticatorType;
|
||||
|
||||
/// AppFlowy Cloud Configuration
|
||||
@EnviedField(
|
||||
obfuscate: false,
|
||||
varName: 'APPFLOWY_CLOUD_URL',
|
||||
defaultValue: '',
|
||||
)
|
||||
static const String afCloudUrl = _Env.afCloudUrl;
|
||||
}
|
@ -9,6 +9,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/stability_
|
||||
import 'package:appflowy/plugins/trash/application/prelude.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/af_cloud_auth_service.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/application/auth/supabase_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/supabase_mock_auth_service.dart';
|
||||
@ -57,16 +58,8 @@ class DependencyResolver {
|
||||
}
|
||||
|
||||
Future<void> _resolveCloudDeps(GetIt getIt) async {
|
||||
final cloudType = await getCloudType();
|
||||
final appflowyCloudConfig = await getAppFlowyCloudConfig();
|
||||
final supabaseCloudConfig = await getSupabaseCloudConfig();
|
||||
getIt.registerFactory<AppFlowyCloudSharedEnv>(() {
|
||||
return AppFlowyCloudSharedEnv(
|
||||
cloudType: cloudType,
|
||||
appflowyCloudConfig: appflowyCloudConfig,
|
||||
supabaseConfig: supabaseCloudConfig,
|
||||
);
|
||||
});
|
||||
final env = await AppFlowyCloudSharedEnv.fromEnv();
|
||||
getIt.registerFactory<AppFlowyCloudSharedEnv>(() => env);
|
||||
}
|
||||
|
||||
void _resolveCommonService(
|
||||
@ -130,22 +123,27 @@ void _resolveCommonService(
|
||||
|
||||
void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
|
||||
switch (currentCloudType()) {
|
||||
case CloudType.local:
|
||||
case AuthenticatorType.local:
|
||||
getIt.registerFactory<AuthService>(
|
||||
() => BackendAuthService(
|
||||
AuthTypePB.Local,
|
||||
),
|
||||
);
|
||||
break;
|
||||
case CloudType.supabase:
|
||||
case AuthenticatorType.supabase:
|
||||
if (mode.isIntegrationTest) {
|
||||
getIt.registerFactory<AuthService>(() => MockAuthService());
|
||||
getIt.registerFactory<AuthService>(() => SupabaseMockAuthService());
|
||||
} else {
|
||||
getIt.registerFactory<AuthService>(() => SupabaseAuthService());
|
||||
}
|
||||
break;
|
||||
case CloudType.appflowyCloud:
|
||||
getIt.registerFactory<AuthService>(() => AFCloudAuthService());
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
if (mode.isIntegrationTest) {
|
||||
getIt
|
||||
.registerFactory<AuthService>(() => AppFlowyCloudMockAuthService());
|
||||
} else {
|
||||
getIt.registerFactory<AuthService>(() => AppFlowyCloudAuthService());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ class FlowyApp implements EntryPoint {
|
||||
@override
|
||||
Widget create(LaunchConfiguration config) {
|
||||
return SplashScreen(
|
||||
autoRegister: config.autoRegistrationSupported,
|
||||
isAnon: config.isAnon,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
class LaunchConfiguration {
|
||||
const LaunchConfiguration({
|
||||
this.autoRegistrationSupported = false,
|
||||
this.isAnon = false,
|
||||
required this.rustEnvs,
|
||||
});
|
||||
|
||||
// APP will automatically register after launching.
|
||||
final bool autoRegistrationSupported;
|
||||
final bool isAnon;
|
||||
//
|
||||
final Map<String, String> rustEnvs;
|
||||
}
|
||||
|
@ -36,21 +36,35 @@ Future<void> runAppFlowy() async {
|
||||
}
|
||||
|
||||
class FlowyRunner {
|
||||
static var currentMode = integrationMode();
|
||||
|
||||
static Future<FlowyRunnerContext> run(
|
||||
EntryPoint f,
|
||||
IntegrationMode mode, {
|
||||
Future? didInitGetIt,
|
||||
LaunchConfiguration config = const LaunchConfiguration(
|
||||
autoRegistrationSupported: false,
|
||||
),
|
||||
// This callback is triggered after the initialization of 'getIt',
|
||||
// which is used for dependency injection throughout the app.
|
||||
// If your functionality depends on 'getIt', ensure to register
|
||||
// your callback here to execute any necessary actions post-initialization.
|
||||
Future? didInitGetItCallback,
|
||||
// Passing the envs to the backend
|
||||
Map<String, String> Function()? rustEnvsBuilder,
|
||||
// Indicate whether the app is running in anonymous mode.
|
||||
// Note: when the app is running in anonymous mode, the user no need to
|
||||
// sign in, and the app will only save the data in the local storage.
|
||||
bool isAnon = false,
|
||||
}) async {
|
||||
currentMode = mode;
|
||||
// Clear all the states in case of rebuilding.
|
||||
await getIt.reset();
|
||||
|
||||
final config = LaunchConfiguration(
|
||||
isAnon: isAnon,
|
||||
rustEnvs: rustEnvsBuilder?.call() ?? {},
|
||||
);
|
||||
|
||||
// Specify the env
|
||||
await initGetIt(getIt, mode, f, config);
|
||||
|
||||
await didInitGetIt;
|
||||
await didInitGetItCallback;
|
||||
|
||||
final applicationDataDirectory =
|
||||
await getIt<ApplicationDataStorage>().getPath().then(
|
||||
|
@ -23,15 +23,18 @@ class InitRustSDKTask extends LaunchTask {
|
||||
|
||||
@override
|
||||
Future<void> initialize(LaunchContext context) async {
|
||||
final root = await getApplicationSupportDirectory();
|
||||
final applicationPath = await appFlowyApplicationDataDirectory();
|
||||
final dir = customApplicationPath ?? applicationPath;
|
||||
final deviceId = await getDeviceId();
|
||||
|
||||
// Pass the environment variables to the Rust SDK
|
||||
final env = _getAppFlowyConfiguration(
|
||||
final env = _makeAppFlowyConfiguration(
|
||||
root.path,
|
||||
dir.path,
|
||||
applicationPath.path,
|
||||
deviceId,
|
||||
rustEnvs: context.config.rustEnvs,
|
||||
);
|
||||
await context.getIt<FlowySDK>().init(jsonEncode(env.toJson()));
|
||||
}
|
||||
@ -40,19 +43,23 @@ class InitRustSDKTask extends LaunchTask {
|
||||
Future<void> dispose() async {}
|
||||
}
|
||||
|
||||
AppFlowyConfiguration _getAppFlowyConfiguration(
|
||||
AppFlowyConfiguration _makeAppFlowyConfiguration(
|
||||
String root,
|
||||
String customAppPath,
|
||||
String originAppPath,
|
||||
String deviceId,
|
||||
) {
|
||||
String deviceId, {
|
||||
required Map<String, String> rustEnvs,
|
||||
}) {
|
||||
final env = getIt<AppFlowyCloudSharedEnv>();
|
||||
return AppFlowyConfiguration(
|
||||
root: root,
|
||||
custom_app_path: customAppPath,
|
||||
origin_app_path: originAppPath,
|
||||
device_id: deviceId,
|
||||
cloud_type: env.cloudType.value,
|
||||
authenticator_type: env.authenticatorType.value,
|
||||
supabase_config: env.supabaseConfig,
|
||||
appflowy_cloud_config: env.appflowyCloudConfig,
|
||||
envs: rustEnvs,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -14,11 +14,11 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
import 'auth_error.dart';
|
||||
import 'device_id.dart';
|
||||
|
||||
class AFCloudAuthService implements AuthService {
|
||||
class AppFlowyCloudAuthService implements AuthService {
|
||||
final _appLinks = AppLinks();
|
||||
StreamSubscription<Uri?>? _deeplinkSubscription;
|
||||
|
||||
AFCloudAuthService();
|
||||
AppFlowyCloudAuthService();
|
||||
|
||||
final BackendAuthService _backendAuthService = BackendAuthService(
|
||||
AuthTypePB.AFCloud,
|
||||
@ -35,7 +35,7 @@ class AFCloudAuthService implements AuthService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signIn({
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params = const {},
|
||||
|
@ -0,0 +1,100 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/user/application/auth/backend_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/device_id.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
|
||||
/// Only used for testing.
|
||||
class AppFlowyCloudMockAuthService implements AuthService {
|
||||
// Use same email for all tests.
|
||||
static String currentUserEmail = "";
|
||||
|
||||
AppFlowyCloudMockAuthService() {
|
||||
if (currentUserEmail.isEmpty) {
|
||||
currentUserEmail = "${uuid()}@appflowy.io";
|
||||
}
|
||||
}
|
||||
|
||||
final BackendAuthService _appFlowyAuthService =
|
||||
BackendAuthService(AuthTypePB.Supabase);
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUp({
|
||||
required String name,
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpWithOAuth({
|
||||
required String platform,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
final payload = SignInUrlPayloadPB.create()
|
||||
..authType = AuthTypePB.AFCloud
|
||||
// don't use nanoid here, the gotrue server will transform the email
|
||||
..email = currentUserEmail;
|
||||
|
||||
final deviceId = await getDeviceId();
|
||||
final getSignInURLResult = await UserEventGenerateSignInURL(payload).send();
|
||||
|
||||
return getSignInURLResult.fold(
|
||||
(urlPB) async {
|
||||
final payload = OauthSignInPB(
|
||||
authType: AuthTypePB.AFCloud,
|
||||
map: {
|
||||
AuthServiceMapKeys.signInURL: urlPB.signInUrl,
|
||||
AuthServiceMapKeys.deviceId: deviceId,
|
||||
},
|
||||
);
|
||||
return await UserEventOauthSignIn(payload)
|
||||
.send()
|
||||
.then((value) => value.swap());
|
||||
},
|
||||
(r) => left(r),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> signOut() async {
|
||||
await _appFlowyAuthService.signOut();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signUpAsGuest({
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
return _appFlowyAuthService.signUpAsGuest();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithMagicLink({
|
||||
required String email,
|
||||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> getUser() async {
|
||||
return UserBackendService.getCurrentUserProfile();
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ abstract class AuthService {
|
||||
///
|
||||
/// Returns [UserProfilePB] if the user is authenticated, otherwise returns [FlowyError].
|
||||
|
||||
Future<Either<FlowyError, UserProfilePB>> signIn({
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params,
|
||||
|
@ -19,7 +19,7 @@ class BackendAuthService implements AuthService {
|
||||
BackendAuthService(this.authType);
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signIn({
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params = const {},
|
||||
@ -29,7 +29,7 @@ class BackendAuthService implements AuthService {
|
||||
..password = password
|
||||
..authType = authType
|
||||
..deviceId = await getDeviceId();
|
||||
final response = UserEventSignIn(request).send();
|
||||
final response = UserEventSignInWithEmailPassword(request).send();
|
||||
return response.then((value) => value.swap());
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ class SupabaseAuthService implements AuthService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signIn({
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params = const {},
|
||||
@ -68,7 +68,7 @@ class SupabaseAuthService implements AuthService {
|
||||
if (uuid == null) {
|
||||
return Left(AuthError.supabaseSignInError);
|
||||
}
|
||||
return _backendAuthService.signIn(
|
||||
return _backendAuthService.signInWithEmailPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
params: {
|
||||
|
@ -13,8 +13,8 @@ import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'auth_error.dart';
|
||||
|
||||
/// Only used for testing.
|
||||
class MockAuthService implements AuthService {
|
||||
MockAuthService();
|
||||
class SupabaseMockAuthService implements AuthService {
|
||||
SupabaseMockAuthService();
|
||||
static OauthSignInPB? signInPayload;
|
||||
|
||||
SupabaseClient get _client => Supabase.instance.client;
|
||||
@ -34,7 +34,7 @@ class MockAuthService implements AuthService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, UserProfilePB>> signIn({
|
||||
Future<Either<FlowyError, UserProfilePB>> signInWithEmailPassword({
|
||||
required String email,
|
||||
required String password,
|
||||
Map<String, String> params = const {},
|
||||
|
@ -59,7 +59,7 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
||||
SignInState state,
|
||||
Emitter<SignInState> emit,
|
||||
) async {
|
||||
final result = await authService.signIn(
|
||||
final result = await authService.signInWithEmailPassword(
|
||||
email: state.email ?? '',
|
||||
password: state.password ?? '',
|
||||
);
|
||||
|
@ -20,10 +20,15 @@ void handleOpenWorkspaceError(BuildContext context, FlowyError error) {
|
||||
error.msg,
|
||||
);
|
||||
break;
|
||||
case ErrorCode.HttpError:
|
||||
showSnapBar(
|
||||
context,
|
||||
error.toString(),
|
||||
);
|
||||
default:
|
||||
showSnapBar(
|
||||
context,
|
||||
error.msg,
|
||||
error.toString(),
|
||||
onClosed: () {
|
||||
getIt<AuthService>().signOut();
|
||||
runAppFlowy();
|
||||
|
@ -2,7 +2,6 @@ import 'package:appflowy/core/frameless_window.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/entry_point.dart';
|
||||
import 'package:appflowy/startup/launch_configuration.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/application/historical_user_bloc.dart';
|
||||
@ -66,6 +65,13 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
||||
}
|
||||
},
|
||||
),
|
||||
// if (Env.enableCustomCloud) ...[
|
||||
// const VSpace(10),
|
||||
// const SizedBox(
|
||||
// width: 340,
|
||||
// child: _SetupYourServer(),
|
||||
// ),
|
||||
// ],
|
||||
const VSpace(32),
|
||||
SizedBox(
|
||||
width: size.width * 0.7,
|
||||
@ -98,9 +104,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationMode(),
|
||||
config: const LaunchConfiguration(
|
||||
autoRegistrationSupported: true,
|
||||
),
|
||||
isAnon: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -279,10 +283,33 @@ class GoButton extends StatelessWidget {
|
||||
? LocaleKeys.letsGoButtonText.tr()
|
||||
: LocaleKeys.signIn_continueAnonymousUser.tr();
|
||||
|
||||
final textWidget = FlowyText.medium(
|
||||
final textWidget = Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: FlowyText.medium(
|
||||
text,
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
// Tooltip(
|
||||
// message: LocaleKeys.settings_menu_configServerGuide.tr(),
|
||||
// child: Container(
|
||||
// width: 30.0,
|
||||
// decoration: const BoxDecoration(
|
||||
// shape: BoxShape.circle,
|
||||
// ),
|
||||
// child: Center(
|
||||
// child: Icon(
|
||||
// Icons.help,
|
||||
// color: Colors.white,
|
||||
// weight: 2,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
|
@ -18,16 +18,14 @@ class SplashScreen extends StatelessWidget {
|
||||
/// Root Page of the app.
|
||||
const SplashScreen({
|
||||
super.key,
|
||||
required this.autoRegister,
|
||||
required this.isAnon,
|
||||
});
|
||||
|
||||
final bool autoRegister;
|
||||
final bool isAnon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!autoRegister) {
|
||||
return _buildChild(context);
|
||||
} else {
|
||||
if (isAnon) {
|
||||
return FutureBuilder<void>(
|
||||
future: _registerIfNeeded(),
|
||||
builder: (context, snapshot) {
|
||||
@ -37,6 +35,8 @@ class SplashScreen extends StatelessWidget {
|
||||
return _buildChild(context);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return _buildChild(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,13 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
part 'cloud_setting_bloc.freezed.dart';
|
||||
|
||||
class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
|
||||
CloudSettingBloc(CloudType cloudType)
|
||||
CloudSettingBloc(AuthenticatorType cloudType)
|
||||
: super(CloudSettingState.initial(cloudType)) {
|
||||
on<CloudSettingEvent>((event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {},
|
||||
updateCloudType: (CloudType newCloudType) async {
|
||||
await setCloudType(newCloudType);
|
||||
updateCloudType: (AuthenticatorType newCloudType) async {
|
||||
await setAuthenticatorType(newCloudType);
|
||||
emit(state.copyWith(cloudType: newCloudType));
|
||||
},
|
||||
);
|
||||
@ -22,17 +22,19 @@ class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
|
||||
@freezed
|
||||
class CloudSettingEvent with _$CloudSettingEvent {
|
||||
const factory CloudSettingEvent.initial() = _Initial;
|
||||
const factory CloudSettingEvent.updateCloudType(CloudType newCloudType) =
|
||||
_UpdateCloudType;
|
||||
const factory CloudSettingEvent.updateCloudType(
|
||||
AuthenticatorType newCloudType,
|
||||
) = _UpdateCloudType;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class CloudSettingState with _$CloudSettingState {
|
||||
const factory CloudSettingState({
|
||||
required CloudType cloudType,
|
||||
required AuthenticatorType cloudType,
|
||||
}) = _CloudSettingState;
|
||||
|
||||
factory CloudSettingState.initial(CloudType cloudType) => CloudSettingState(
|
||||
factory CloudSettingState.initial(AuthenticatorType cloudType) =>
|
||||
CloudSettingState(
|
||||
cloudType: cloudType,
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appflowy_cloud_setting_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appflowy_cloud_urls_bloc.dart';
|
||||
@ -50,7 +51,16 @@ class SettingAppFlowyCloudView extends StatelessWidget {
|
||||
children: [
|
||||
const AppFlowyCloudEnableSync(),
|
||||
const VSpace(40),
|
||||
if (Env.enableCustomCloud)
|
||||
AppFlowyCloudURLs(didUpdateUrls: () => didResetServerUrl()),
|
||||
if (!Env.enableCustomCloud)
|
||||
Row(
|
||||
children: [
|
||||
FlowyText(LocaleKeys.settings_menu_cloudServerType.tr()),
|
||||
const Spacer(),
|
||||
const FlowyText(Env.afCloudUrl),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/cloud_setting_bloc.dart';
|
||||
@ -19,8 +20,9 @@ class SettingCloud extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: getCloudType(),
|
||||
builder: (BuildContext context, AsyncSnapshot<CloudType> snapshot) {
|
||||
future: getAuthenticatorType(),
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<AuthenticatorType> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final cloudType = snapshot.data!;
|
||||
return BlocProvider(
|
||||
@ -29,6 +31,7 @@ class SettingCloud extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return Column(
|
||||
children: [
|
||||
if (Env.enableCustomCloud)
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -37,8 +40,8 @@ class SettingCloud extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message:
|
||||
LocaleKeys.settings_menu_cloudServerTypeTip.tr(),
|
||||
message: LocaleKeys.settings_menu_cloudServerTypeTip
|
||||
.tr(),
|
||||
child: CloudTypeSwitcher(
|
||||
cloudType: state.cloudType,
|
||||
onSelected: (newCloudType) {
|
||||
@ -67,15 +70,15 @@ class SettingCloud extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _viewFromCloudType(CloudType cloudType) {
|
||||
Widget _viewFromCloudType(AuthenticatorType cloudType) {
|
||||
switch (cloudType) {
|
||||
case CloudType.local:
|
||||
case AuthenticatorType.local:
|
||||
return SettingLocalCloud(didResetServerUrl: didResetServerUrl);
|
||||
case CloudType.supabase:
|
||||
case AuthenticatorType.supabase:
|
||||
return SettingSupabaseCloudView(
|
||||
didResetServerUrl: didResetServerUrl,
|
||||
);
|
||||
case CloudType.appflowyCloud:
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
return SettingAppFlowyCloudView(
|
||||
didResetServerUrl: didResetServerUrl,
|
||||
);
|
||||
@ -84,8 +87,8 @@ class SettingCloud extends StatelessWidget {
|
||||
}
|
||||
|
||||
class CloudTypeSwitcher extends StatelessWidget {
|
||||
final CloudType cloudType;
|
||||
final Function(CloudType) onSelected;
|
||||
final AuthenticatorType cloudType;
|
||||
final Function(AuthenticatorType) onSelected;
|
||||
const CloudTypeSwitcher({
|
||||
required this.cloudType,
|
||||
required this.onSelected,
|
||||
@ -108,12 +111,12 @@ class CloudTypeSwitcher extends StatelessWidget {
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, index) {
|
||||
return CloudTypeItem(
|
||||
cloudType: CloudType.values[index],
|
||||
cloudType: AuthenticatorType.values[index],
|
||||
currentCloudtype: cloudType,
|
||||
onSelected: onSelected,
|
||||
);
|
||||
},
|
||||
itemCount: CloudType.values.length,
|
||||
itemCount: AuthenticatorType.values.length,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -121,9 +124,9 @@ class CloudTypeSwitcher extends StatelessWidget {
|
||||
}
|
||||
|
||||
class CloudTypeItem extends StatelessWidget {
|
||||
final CloudType cloudType;
|
||||
final CloudType currentCloudtype;
|
||||
final Function(CloudType) onSelected;
|
||||
final AuthenticatorType cloudType;
|
||||
final AuthenticatorType currentCloudtype;
|
||||
final Function(AuthenticatorType) onSelected;
|
||||
|
||||
const CloudTypeItem({
|
||||
required this.cloudType,
|
||||
@ -154,13 +157,13 @@ class CloudTypeItem extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
String titleFromCloudType(CloudType cloudType) {
|
||||
String titleFromCloudType(AuthenticatorType cloudType) {
|
||||
switch (cloudType) {
|
||||
case CloudType.local:
|
||||
case AuthenticatorType.local:
|
||||
return LocaleKeys.settings_menu_cloudLocal.tr();
|
||||
case CloudType.supabase:
|
||||
case AuthenticatorType.supabase:
|
||||
return LocaleKeys.settings_menu_cloudSupabase.tr();
|
||||
case CloudType.appflowyCloud:
|
||||
case AuthenticatorType.appflowyCloud:
|
||||
return LocaleKeys.settings_menu_cloudAppFlowy.tr();
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../generated/locale_keys.g.dart';
|
||||
import '../../../../startup/launch_configuration.dart';
|
||||
import '../../../../startup/startup.dart';
|
||||
import '../../../../startup/tasks/prelude.dart';
|
||||
|
||||
@ -210,10 +209,8 @@ class _ChangeStoragePathButtonState extends State<_ChangeStoragePathButton> {
|
||||
await context.read<SettingsLocationCubit>().setCustomPath(path);
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationMode(),
|
||||
config: const LaunchConfiguration(
|
||||
autoRegistrationSupported: true,
|
||||
),
|
||||
FlowyRunner.currentMode,
|
||||
isAnon: true,
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
@ -288,10 +285,8 @@ class _RecoverDefaultStorageButtonState
|
||||
.resetDataStoragePathToApplicationDefault();
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
integrationMode(),
|
||||
config: const LaunchConfiguration(
|
||||
autoRegistrationSupported: true,
|
||||
),
|
||||
FlowyRunner.currentMode,
|
||||
isAnon: true,
|
||||
);
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
@ -1,4 +1,4 @@
|
||||
platform :osx, '10.11'
|
||||
platform :osx, '10.13'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
@ -17,7 +17,7 @@ A new flutter plugin project.
|
||||
s.public_header_files = 'Classes/**/*.h'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.platform = :osx, '10.13'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
s.static_framework = true
|
||||
|
@ -1,4 +1,4 @@
|
||||
platform :osx, '10.11'
|
||||
platform :osx, '10.13'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
@ -15,7 +15,8 @@ void showSnapBar(BuildContext context, String title, {VoidCallback? onClosed}) {
|
||||
},
|
||||
child: FlowyText.medium(
|
||||
title,
|
||||
fontSize: 16,
|
||||
fontSize: 12,
|
||||
maxLines: 3,
|
||||
),
|
||||
),
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
|
@ -16,7 +16,7 @@ A new flutter plugin project.
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.platform = :osx, '10.13'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
||||
|
@ -271,12 +271,12 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f
|
||||
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.1"
|
||||
version: "5.0.2"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: connectivity_plus_platform_interface
|
||||
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a
|
||||
@ -1463,10 +1463,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022"
|
||||
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.2.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1495,10 +1495,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d
|
||||
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.3.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -74,8 +74,7 @@ dependencies:
|
||||
package_info_plus: ^4.0.1
|
||||
url_launcher: ^6.1.11
|
||||
clipboard: ^0.1.3
|
||||
connectivity_plus: ^5.0.1
|
||||
connectivity_plus_platform_interface: ^1.2.4
|
||||
connectivity_plus: ^5.0.2
|
||||
easy_localization: ^3.0.2
|
||||
textfield_tags: ^2.0.2
|
||||
device_info_plus: ^9.0.1
|
||||
@ -91,7 +90,7 @@ dependencies:
|
||||
charcode: ^1.3.1
|
||||
collection: ^1.17.1
|
||||
bloc: ^8.1.2
|
||||
shared_preferences: ^2.1.1
|
||||
shared_preferences: ^2.2.2
|
||||
google_fonts: ^4.0.5
|
||||
percent_indicator: ^4.2.3
|
||||
calendar_view:
|
||||
|
36
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
36
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -138,7 +138,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -785,7 +785,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1326,7 +1326,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa 1.0.6",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1472,7 +1472,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2830,7 +2830,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2846,7 +2846,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3267,7 +3267,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -4363,7 +4363,6 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -4455,19 +4454,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -4709,7 +4695,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.4.1",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
@ -4730,7 +4716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.29",
|
||||
@ -5025,7 +5011,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -5778,7 +5764,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
@ -57,7 +57,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b578c83cc912255e48dea9e33a203a069ce7d0c5" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5090711272dbc503912544375307365c174bb804" }
|
||||
# Please use the following script to update collab.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
UserEventGetUserProfile,
|
||||
UserEventGetUserSetting,
|
||||
UserEventSetAppearanceSetting,
|
||||
UserEventSignIn,
|
||||
UserEventSignInWithEmailPassword,
|
||||
UserEventSignOut,
|
||||
UserEventSignUp,
|
||||
UserEventUpdateUserProfile,
|
||||
@ -99,7 +99,7 @@ export class AuthBackendService {
|
||||
signIn = (params: { email: string; password: string }) => {
|
||||
const payload = SignInPayloadPB.fromObject({ email: params.email, password: params.password });
|
||||
|
||||
return UserEventSignIn(payload);
|
||||
return UserEventSignInWithEmailPassword(payload);
|
||||
};
|
||||
|
||||
signUp = (params: { name: string; email: string; password: string }) => {
|
||||
|
@ -290,6 +290,8 @@
|
||||
"enableEncryptPrompt": "Activate encryption to secure your data with this secret. Store it safely; once enabled, it can't be turned off. If lost, your data becomes irretrievable. Click to copy",
|
||||
"inputEncryptPrompt": "Please enter your encryption secret for",
|
||||
"clickToCopySecret": "Click to copy secret",
|
||||
"configServerSetting": "Configurate your server settings",
|
||||
"configServerGuide": "After selecting `Quick Start`, navigate to `Settings` and then \"Cloud Setting\" to configure your self-hosted server.",
|
||||
"inputTextFieldHint": "Your secret",
|
||||
"historicalUserList": "User login history",
|
||||
"historicalUserListTooltip": "This list displays your anonymous accounts. You can click on an account to view its details. Anonymous accounts are created by clicking the 'Get Started' button",
|
||||
|
@ -1,6 +1,3 @@
|
||||
[build]
|
||||
rustflags = ["--cfg", "tokio_unstable"]
|
||||
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-mmacosx-version-min=11.0"]
|
||||
|
66
frontend/rust-lib/Cargo.lock
generated
66
frontend/rust-lib/Cargo.lock
generated
@ -124,7 +124,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -666,7 +666,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1150,7 +1150,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1251,6 +1251,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_yaml",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@ -1277,7 +1278,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2470,7 +2471,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2486,7 +2487,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2847,7 +2848,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -3663,7 +3664,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros 0.8.0",
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
@ -3683,7 +3684,6 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -3751,19 +3751,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -3967,7 +3954,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.4.1",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
@ -3988,7 +3975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
@ -4327,7 +4314,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -4868,9 +4855,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -4888,9 +4875,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.188"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -4931,6 +4918,19 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
|
||||
dependencies = [
|
||||
"indexmap 2.0.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo_arc"
|
||||
version = "0.3.0"
|
||||
@ -4980,7 +4980,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5090711272dbc503912544375307365c174bb804#5090711272dbc503912544375307365c174bb804"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -5950,6 +5950,12 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
|
@ -99,7 +99,7 @@ incremental = false
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b578c83cc912255e48dea9e33a203a069ce7d0c5" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5090711272dbc503912544375307365c174bb804" }
|
||||
# Please use the following script to update collab.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
@ -34,6 +34,7 @@ flowy-server = { workspace = true }
|
||||
flowy-server-config = { workspace = true}
|
||||
collab-integrate = { workspace = true }
|
||||
flowy-derive = { path = "../../../shared-lib/flowy-derive" }
|
||||
serde_yaml = "0.9.27"
|
||||
|
||||
[features]
|
||||
default = ["dart", "rev-sqlite"]
|
||||
|
54
frontend/rust-lib/dart-ffi/src/appflowy_yaml.rs
Normal file
54
frontend/rust-lib/dart-ffi/src/appflowy_yaml.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct AppFlowyYamlConfiguration {
|
||||
cloud_config: Vec<AFCloudConfiguration>,
|
||||
}
|
||||
|
||||
pub fn save_appflowy_cloud_config(
|
||||
root: impl AsRef<Path>,
|
||||
new_config: &AFCloudConfiguration,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file_path = root.as_ref().join("appflowy.yaml");
|
||||
let mut config = read_yaml_file(&file_path).unwrap_or_default();
|
||||
|
||||
if !config
|
||||
.cloud_config
|
||||
.iter()
|
||||
.any(|c| c.base_url == new_config.base_url)
|
||||
{
|
||||
config.cloud_config.push(new_config.clone());
|
||||
write_yaml_file(&file_path, &config)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_yaml_file(
|
||||
file_path: impl AsRef<Path>,
|
||||
) -> Result<AppFlowyYamlConfiguration, Box<dyn std::error::Error>> {
|
||||
let mut file = File::open(file_path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
let config: AppFlowyYamlConfiguration = serde_yaml::from_str(&contents)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn write_yaml_file(
|
||||
file_path: impl AsRef<Path>,
|
||||
config: &AppFlowyYamlConfiguration,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let yaml_string = serde_yaml::to_string(config)?;
|
||||
let mut file = OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(file_path)?;
|
||||
file.write_all(yaml_string.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
@ -6,13 +8,17 @@ use flowy_server_config::AuthenticatorType;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct AppFlowyDartConfiguration {
|
||||
/// The root path of the application
|
||||
pub root: String,
|
||||
/// This path will be used to store the user data
|
||||
pub custom_app_path: String,
|
||||
pub origin_app_path: String,
|
||||
pub device_id: String,
|
||||
pub cloud_type: AuthenticatorType,
|
||||
pub authenticator_type: AuthenticatorType,
|
||||
pub(crate) supabase_config: SupabaseConfiguration,
|
||||
pub(crate) appflowy_cloud_config: AFCloudConfiguration,
|
||||
#[serde(default)]
|
||||
pub(crate) envs: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl AppFlowyDartConfiguration {
|
||||
@ -20,12 +26,13 @@ impl AppFlowyDartConfiguration {
|
||||
serde_json::from_str::<AppFlowyDartConfiguration>(s).unwrap()
|
||||
}
|
||||
|
||||
/// Parse the environment variable from the frontend application. The frontend will
|
||||
/// pass the environment variable as a json string after launching.
|
||||
pub fn write_env_from(env_str: &str) {
|
||||
let configuration = Self::from_str(env_str);
|
||||
configuration.cloud_type.write_env();
|
||||
configuration.appflowy_cloud_config.write_env();
|
||||
configuration.supabase_config.write_env();
|
||||
pub fn write_env(&self) {
|
||||
self.authenticator_type.write_env();
|
||||
self.appflowy_cloud_config.write_env();
|
||||
self.supabase_config.write_env();
|
||||
|
||||
for (k, v) in self.envs.iter() {
|
||||
std::env::set_var(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,11 @@ use tracing::{error, trace};
|
||||
use flowy_core::config::AppFlowyCoreConfig;
|
||||
use flowy_core::*;
|
||||
use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
|
||||
use flowy_server_config::AuthenticatorType;
|
||||
use lib_dispatch::prelude::ToBytes;
|
||||
use lib_dispatch::prelude::*;
|
||||
|
||||
use crate::appflowy_yaml::save_appflowy_cloud_config;
|
||||
use crate::env_serde::AppFlowyDartConfiguration;
|
||||
use crate::notification::DartNotificationSender;
|
||||
use crate::{
|
||||
@ -20,6 +22,7 @@ use crate::{
|
||||
model::{FFIRequest, FFIResponse},
|
||||
};
|
||||
|
||||
mod appflowy_yaml;
|
||||
mod c;
|
||||
mod env_serde;
|
||||
mod model;
|
||||
@ -53,10 +56,11 @@ pub extern "C" fn init_sdk(data: *mut c_char) -> i64 {
|
||||
let c_str = unsafe { CStr::from_ptr(data) };
|
||||
let serde_str = c_str.to_str().unwrap();
|
||||
let configuration = AppFlowyDartConfiguration::from_str(serde_str);
|
||||
configuration.write_env();
|
||||
|
||||
configuration.cloud_type.write_env();
|
||||
configuration.appflowy_cloud_config.write_env();
|
||||
configuration.supabase_config.write_env();
|
||||
if configuration.authenticator_type == AuthenticatorType::AppFlowyCloud {
|
||||
let _ = save_appflowy_cloud_config(&configuration.root, &configuration.appflowy_cloud_config);
|
||||
}
|
||||
|
||||
let log_crates = vec!["flowy-ffi".to_string()];
|
||||
let config = AppFlowyCoreConfig::new(
|
||||
@ -170,8 +174,6 @@ pub extern "C" fn backend_log(level: i64, data: *const c_char) {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_env(data: *const c_char) {
|
||||
let c_str = unsafe { CStr::from_ptr(data) };
|
||||
let serde_str = c_str.to_str().unwrap();
|
||||
AppFlowyDartConfiguration::write_env_from(serde_str);
|
||||
pub extern "C" fn set_env(_data: *const c_char) {
|
||||
// Deprecated
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ impl EventIntegrationTest {
|
||||
auth_type: AuthTypePB::AFCloud,
|
||||
};
|
||||
let sign_in_url = EventBuilder::new(self.clone())
|
||||
.event(GetSignInURL)
|
||||
.event(GenerateSignInURL)
|
||||
.payload(payload)
|
||||
.async_send()
|
||||
.await
|
||||
|
@ -69,7 +69,7 @@ async fn sign_in_with_invalid_email() {
|
||||
|
||||
assert_eq!(
|
||||
EventBuilder::new(sdk)
|
||||
.event(SignIn)
|
||||
.event(SignInWithEmailPassword)
|
||||
.payload(request)
|
||||
.async_send()
|
||||
.await
|
||||
@ -95,7 +95,7 @@ async fn sign_in_with_invalid_password() {
|
||||
};
|
||||
|
||||
assert!(EventBuilder::new(sdk)
|
||||
.event(SignIn)
|
||||
.event(SignInWithEmailPassword)
|
||||
.payload(request)
|
||||
.async_send()
|
||||
.await
|
||||
|
@ -77,6 +77,12 @@ impl UserCloudServiceProvider for ServerProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_network_reachable(&self, reachable: bool) {
|
||||
if let Ok(server) = self.get_server(&self.get_server_type()) {
|
||||
server.set_network_reachable(reachable);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_encrypt_secret(&self, secret: String) {
|
||||
tracing::info!("🔑Set encrypt secret");
|
||||
self.encryption.write().set_secret(secret);
|
||||
|
@ -19,6 +19,7 @@ impl From<AppResponseError> for FlowyError {
|
||||
AppErrorCode::InvalidOAuthProvider => ErrorCode::InvalidAuthConfig,
|
||||
AppErrorCode::NotLoggedIn => ErrorCode::UserUnauthorized,
|
||||
AppErrorCode::NotEnoughPermissions => ErrorCode::NotEnoughPermissions,
|
||||
AppErrorCode::NetworkError => ErrorCode::HttpError,
|
||||
_ => ErrorCode::Internal,
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,7 @@ pub mod supabase_config;
|
||||
|
||||
pub const CLOUT_TYPE_STR: &str = "APPFLOWY_CLOUD_ENV_CLOUD_TYPE";
|
||||
|
||||
#[derive(Deserialize_repr, Debug, Clone)]
|
||||
#[derive(Deserialize_repr, Debug, Clone, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum AuthenticatorType {
|
||||
Local = 0,
|
||||
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use client_api::entity::workspace_dto::{CreateWorkspaceMember, WorkspaceMemberChangeset};
|
||||
use client_api::entity::{AFRole, AFWorkspace, InsertCollabParams, OAuthProvider};
|
||||
use client_api::entity::{AFRole, AFWorkspace, AuthProvider, InsertCollabParams};
|
||||
use collab_entity::CollabObject;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
@ -38,7 +38,7 @@ impl<T> UserCloudService for AFCloudUserAuthServiceImpl<T>
|
||||
where
|
||||
T: AFServer,
|
||||
{
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let params = oauth_params_from_box_any(params)?;
|
||||
@ -48,7 +48,7 @@ where
|
||||
}
|
||||
|
||||
// Zack: Not sure if this is needed anymore since sign_up handles both cases
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
@ -58,12 +58,12 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), Error> {
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move { Ok(try_get_client?.sign_out().await?) })
|
||||
}
|
||||
|
||||
fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, Error> {
|
||||
fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, FlowyError> {
|
||||
let email = email.to_string();
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
@ -82,8 +82,7 @@ where
|
||||
client_api::Client::new(client.base_url(), client.ws_addr(), client.gotrue_url());
|
||||
admin_client
|
||||
.sign_in_password(&admin_email, &admin_password)
|
||||
.await
|
||||
.unwrap();
|
||||
.await?;
|
||||
|
||||
let action_link = admin_client.generate_sign_in_action_link(&email).await?;
|
||||
let sign_in_url = client.extract_sign_in_url(&action_link).await?;
|
||||
@ -91,8 +90,8 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, Error> {
|
||||
let provider = OAuthProvider::from(provider);
|
||||
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, FlowyError> {
|
||||
let provider = AuthProvider::from(provider);
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let provider = provider.ok_or(anyhow!("invalid provider"))?;
|
||||
@ -107,7 +106,7 @@ where
|
||||
&self,
|
||||
_credential: UserCredentials,
|
||||
params: UpdateUserProfileParams,
|
||||
) -> FutureResult<(), Error> {
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let client = try_get_client?;
|
||||
@ -142,11 +141,11 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn get_all_workspace(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
fn get_all_workspace(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, FlowyError> {
|
||||
let try_get_client = self.server.try_get_client();
|
||||
FutureResult::new(async move {
|
||||
let workspaces = try_get_client?.get_workspaces().await?;
|
||||
Ok(to_user_workspaces(workspaces.0)?)
|
||||
to_user_workspaces(workspaces.0)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ pub struct AppFlowyCloudServer {
|
||||
pub(crate) config: AFCloudConfiguration,
|
||||
pub(crate) client: Arc<AFCloudClient>,
|
||||
enable_sync: Arc<AtomicBool>,
|
||||
network_reachable: Arc<AtomicBool>,
|
||||
#[allow(dead_code)]
|
||||
device_id: String,
|
||||
ws_client: Arc<WSClient>,
|
||||
@ -47,6 +48,7 @@ impl AppFlowyCloudServer {
|
||||
let api_client = AFCloudClient::new(&config.base_url, &config.ws_base_url, &config.gotrue_url);
|
||||
let token_state_rx = api_client.subscribe_token_state();
|
||||
let enable_sync = Arc::new(AtomicBool::new(enable_sync));
|
||||
let network_reachable = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let ws_client = WSClient::new(WSClientConfig::default(), api_client.clone());
|
||||
let ws_client = Arc::new(ws_client);
|
||||
@ -63,6 +65,7 @@ impl AppFlowyCloudServer {
|
||||
config,
|
||||
client: api_client,
|
||||
enable_sync,
|
||||
network_reachable,
|
||||
device_id,
|
||||
ws_client,
|
||||
}
|
||||
@ -117,8 +120,14 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
self.enable_sync.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn set_network_reachable(&self, reachable: bool) {
|
||||
self.network_reachable.store(reachable, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn user_service(&self) -> Arc<dyn UserCloudService> {
|
||||
let server = AFServerImpl(self.get_client());
|
||||
let server = AFServerImpl {
|
||||
client: self.get_client(),
|
||||
};
|
||||
let mut user_change = self.ws_client.subscribe_user_changed();
|
||||
let (tx, rx) = tokio::sync::mpsc::channel(1);
|
||||
tokio::spawn(async move {
|
||||
@ -139,17 +148,23 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
}
|
||||
|
||||
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
|
||||
let server = AFServerImpl(self.get_client());
|
||||
let server = AFServerImpl {
|
||||
client: self.get_client(),
|
||||
};
|
||||
Arc::new(AFCloudFolderCloudServiceImpl(server))
|
||||
}
|
||||
|
||||
fn database_service(&self) -> Arc<dyn DatabaseCloudService> {
|
||||
let server = AFServerImpl(self.get_client());
|
||||
let server = AFServerImpl {
|
||||
client: self.get_client(),
|
||||
};
|
||||
Arc::new(AFCloudDatabaseCloudServiceImpl(server))
|
||||
}
|
||||
|
||||
fn document_service(&self) -> Arc<dyn DocumentCloudService> {
|
||||
let server = AFServerImpl(self.get_client());
|
||||
let server = AFServerImpl {
|
||||
client: self.get_client(),
|
||||
};
|
||||
Arc::new(AFCloudDocumentCloudServiceImpl(server))
|
||||
}
|
||||
|
||||
@ -184,7 +199,9 @@ impl AppFlowyServer for AppFlowyCloudServer {
|
||||
}
|
||||
|
||||
fn file_storage(&self) -> Option<Arc<dyn FileStorageService>> {
|
||||
let client = AFServerImpl(self.get_client());
|
||||
let client = AFServerImpl {
|
||||
client: self.get_client(),
|
||||
};
|
||||
Some(Arc::new(AFCloudFileStorageServiceImpl::new(client)))
|
||||
}
|
||||
}
|
||||
@ -274,15 +291,17 @@ pub trait AFServer: Send + Sync + 'static {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AFServerImpl(pub Option<Arc<AFCloudClient>>);
|
||||
pub struct AFServerImpl {
|
||||
client: Option<Arc<AFCloudClient>>,
|
||||
}
|
||||
|
||||
impl AFServer for AFServerImpl {
|
||||
fn get_client(&self) -> Option<Arc<AFCloudClient>> {
|
||||
self.0.clone()
|
||||
self.client.clone()
|
||||
}
|
||||
|
||||
fn try_get_client(&self) -> Result<Arc<AFCloudClient>, Error> {
|
||||
match self.0.clone() {
|
||||
match self.client.clone() {
|
||||
None => Err(
|
||||
FlowyError::new(
|
||||
ErrorCode::DataSyncRequired,
|
||||
|
@ -26,7 +26,7 @@ pub(crate) struct LocalServerUserAuthServiceImpl {
|
||||
}
|
||||
|
||||
impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError> {
|
||||
FutureResult::new(async move {
|
||||
let params = params.unbox_or_error::<SignUpParams>()?;
|
||||
let uid = ID_GEN.lock().next_id();
|
||||
@ -52,7 +52,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError> {
|
||||
let db = self.db.clone();
|
||||
FutureResult::new(async move {
|
||||
let params: SignInParams = params.unbox_or_error::<SignInParams>()?;
|
||||
@ -76,27 +76,29 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), Error> {
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async { Ok(()) })
|
||||
}
|
||||
|
||||
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, Error> {
|
||||
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(anyhow::anyhow!(
|
||||
"Can't generate callback url when using offline mode"
|
||||
))
|
||||
Err(
|
||||
FlowyError::internal().with_context("Can't generate callback url when using offline mode"),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, Error> {
|
||||
FutureResult::new(async { Err(anyhow::anyhow!("Can't oauth url when using offline mode")) })
|
||||
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(FlowyError::internal().with_context("Can't oauth url when using offline mode"))
|
||||
})
|
||||
}
|
||||
|
||||
fn update_user(
|
||||
&self,
|
||||
_credential: UserCredentials,
|
||||
_params: UpdateUserProfileParams,
|
||||
) -> FutureResult<(), Error> {
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async { Ok(()) })
|
||||
}
|
||||
|
||||
@ -120,7 +122,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
||||
})
|
||||
}
|
||||
|
||||
fn get_all_workspace(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
fn get_all_workspace(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, FlowyError> {
|
||||
FutureResult::new(async { Ok(vec![]) })
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,12 @@ pub trait AppFlowyServer: Send + Sync + 'static {
|
||||
/// * `_enable` - A boolean to toggle the server synchronization.
|
||||
fn set_enable_sync(&self, _uid: i64, _enable: bool) {}
|
||||
|
||||
/// Sets the network reachability status.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reachable`: A boolean indicating whether the network is reachable.
|
||||
fn set_network_reachable(&self, _reachable: bool) {}
|
||||
|
||||
/// Provides access to cloud-based user management functionalities. This includes operations
|
||||
/// such as user registration, authentication, profile management, and handling of user workspaces.
|
||||
/// The interface also offers methods for managing collaborative objects, subscribing to user updates,
|
||||
|
@ -64,7 +64,7 @@ impl<T> UserCloudService for SupabaseUserServiceImpl<T>
|
||||
where
|
||||
T: SupabaseServerService,
|
||||
{
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError> {
|
||||
let try_get_postgrest = self.server.try_get_postgrest();
|
||||
FutureResult::new(async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -129,7 +129,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, Error> {
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError> {
|
||||
let try_get_postgrest = self.server.try_get_postgrest();
|
||||
FutureResult::new(async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -159,23 +159,19 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), Error> {
|
||||
fn sign_out(&self, _token: Option<String>) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async { Ok(()) })
|
||||
}
|
||||
|
||||
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, Error> {
|
||||
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(anyhow::anyhow!(
|
||||
"Can't generate callback url when using supabase"
|
||||
))
|
||||
Err(FlowyError::internal().with_context("Can't generate callback url when using supabase"))
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, Error> {
|
||||
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, FlowyError> {
|
||||
FutureResult::new(async {
|
||||
Err(anyhow::anyhow!(
|
||||
"Can't generate oauth url when using supabase"
|
||||
))
|
||||
Err(FlowyError::internal().with_context("Can't generate oauth url when using supabase"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -183,7 +179,7 @@ where
|
||||
&self,
|
||||
_credential: UserCredentials,
|
||||
params: UpdateUserProfileParams,
|
||||
) -> FutureResult<(), Error> {
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
let try_get_postgrest = self.server.try_get_postgrest();
|
||||
FutureResult::new(async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
@ -226,7 +222,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
fn get_all_workspace(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||
fn get_all_workspace(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, FlowyError> {
|
||||
let try_get_postgrest = self.server.try_get_postgrest();
|
||||
FutureResult::new(async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
|
@ -59,32 +59,32 @@ pub trait UserCloudService: Send + Sync + 'static {
|
||||
/// Sign up a new account.
|
||||
/// The type of the params is defined the this trait's implementation.
|
||||
/// Use the `unbox_or_error` of the [BoxAny] to get the params.
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, Error>;
|
||||
fn sign_up(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError>;
|
||||
|
||||
/// Sign in an account
|
||||
/// The type of the params is defined the this trait's implementation.
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, Error>;
|
||||
fn sign_in(&self, params: BoxAny) -> FutureResult<AuthResponse, FlowyError>;
|
||||
|
||||
/// Sign out an account
|
||||
fn sign_out(&self, token: Option<String>) -> FutureResult<(), Error>;
|
||||
fn sign_out(&self, token: Option<String>) -> FutureResult<(), FlowyError>;
|
||||
|
||||
/// Generate a sign in url for the user with the given email
|
||||
/// Currently, only use the admin client for testing
|
||||
fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, Error>;
|
||||
fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, FlowyError>;
|
||||
|
||||
/// When the user opens the OAuth URL, it redirects to the corresponding provider's OAuth web page.
|
||||
/// After the user is authenticated, the browser will open a deep link to the AppFlowy app (iOS, macOS, etc.),
|
||||
/// which will call [Client::sign_in_with_url] to sign in.
|
||||
///
|
||||
/// For example, the OAuth URL on Google looks like `https://appflowy.io/authorize?provider=google`.
|
||||
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, Error>;
|
||||
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, FlowyError>;
|
||||
|
||||
/// Using the user's token to update the user information
|
||||
fn update_user(
|
||||
&self,
|
||||
credential: UserCredentials,
|
||||
params: UpdateUserProfileParams,
|
||||
) -> FutureResult<(), Error>;
|
||||
) -> FutureResult<(), FlowyError>;
|
||||
|
||||
/// Get the user information using the user's token or uid
|
||||
/// return None if the user is not found
|
||||
@ -93,7 +93,7 @@ pub trait UserCloudService: Send + Sync + 'static {
|
||||
fn open_workspace(&self, workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError>;
|
||||
|
||||
/// Return the all the workspaces of the user
|
||||
fn get_all_workspace(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error>;
|
||||
fn get_all_workspace(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, FlowyError>;
|
||||
|
||||
fn add_workspace_member(
|
||||
&self,
|
||||
|
@ -180,7 +180,7 @@ impl EncryptionType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_need_encrypt_secret(&self) -> bool {
|
||||
pub fn require_encrypt_secret(&self) -> bool {
|
||||
match self {
|
||||
EncryptionType::NoEncryption => false,
|
||||
EncryptionType::SelfEncryption(sign) => !sign.is_empty(),
|
||||
|
@ -35,7 +35,7 @@ fn upgrade_store_preferences(
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", name = "sign_in", skip(data, manager), fields(email = %data.email), err)]
|
||||
pub async fn sign_in(
|
||||
pub async fn sign_in_with_email_password_handler(
|
||||
data: AFPluginData<SignInPayloadPB>,
|
||||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<UserProfilePB, FlowyError> {
|
||||
@ -43,10 +43,7 @@ pub async fn sign_in(
|
||||
let params: SignInParams = data.into_inner().try_into()?;
|
||||
let auth_type = params.auth_type.clone();
|
||||
|
||||
let user_profile: UserProfilePB = manager
|
||||
.sign_in(BoxAny::new(params), auth_type)
|
||||
.await?
|
||||
.into();
|
||||
let user_profile: UserProfilePB = manager.sign_in(params, auth_type).await?.into();
|
||||
data_result_ok(user_profile)
|
||||
}
|
||||
|
||||
@ -264,7 +261,7 @@ pub async fn oauth_handler(
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||
pub async fn get_sign_in_url_handler(
|
||||
pub async fn gen_sign_in_url_handler(
|
||||
data: AFPluginData<SignInUrlPayloadPB>,
|
||||
manager: AFPluginState<Weak<UserManager>>,
|
||||
) -> DataResult<SignInUrlPB, FlowyError> {
|
||||
@ -274,8 +271,7 @@ pub async fn get_sign_in_url_handler(
|
||||
let sign_in_url = manager
|
||||
.generate_sign_in_url_with_email(&auth_type, ¶ms.email)
|
||||
.await?;
|
||||
let resp = SignInUrlPB { sign_in_url };
|
||||
data_result_ok(resp)
|
||||
data_result_ok(SignInUrlPB { sign_in_url })
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
@ -459,6 +455,7 @@ pub async fn update_network_state_handler(
|
||||
) -> Result<(), FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let reachable = data.into_inner().ty.is_reachable();
|
||||
manager.cloud_services.set_network_reachable(reachable);
|
||||
manager
|
||||
.user_status_callback
|
||||
.read()
|
||||
|
@ -25,7 +25,7 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
|
||||
.name("Flowy-User")
|
||||
.state(user_session)
|
||||
.state(store_preferences)
|
||||
.event(UserEvent::SignIn, sign_in)
|
||||
.event(UserEvent::SignInWithEmailPassword, sign_in_with_email_password_handler)
|
||||
.event(UserEvent::SignUp, sign_up)
|
||||
.event(UserEvent::InitUser, init_user_handler)
|
||||
.event(UserEvent::GetUserProfile, get_user_profile_handler)
|
||||
@ -39,7 +39,7 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
|
||||
.event(UserEvent::SetEncryptionSecret, set_encrypt_secret_handler)
|
||||
.event(UserEvent::CheckEncryptionSign, check_encrypt_secret_handler)
|
||||
.event(UserEvent::OauthSignIn, oauth_handler)
|
||||
.event(UserEvent::GetSignInURL, get_sign_in_url_handler)
|
||||
.event(UserEvent::GenerateSignInURL, gen_sign_in_url_handler)
|
||||
.event(UserEvent::GetOauthURLWithProvider, sign_in_with_provider_handler)
|
||||
.event(UserEvent::GetAllWorkspace, get_all_workspace_handler)
|
||||
.event(UserEvent::OpenWorkspace, open_workspace_handler)
|
||||
@ -69,7 +69,7 @@ pub enum UserEvent {
|
||||
/// Only use when the [Authenticator] is Local or SelfHosted
|
||||
/// Logging into an account using a register email and password
|
||||
#[event(input = "SignInPayloadPB", output = "UserProfilePB")]
|
||||
SignIn = 0,
|
||||
SignInWithEmailPassword = 0,
|
||||
|
||||
/// Only use when the [Authenticator] is Local or SelfHosted
|
||||
/// Creating a new account
|
||||
@ -111,7 +111,7 @@ pub enum UserEvent {
|
||||
/// Get the OAuth callback url
|
||||
/// Only use when the [Authenticator] is AFCloud
|
||||
#[event(input = "SignInUrlPayloadPB", output = "SignInUrlPB")]
|
||||
GetSignInURL = 11,
|
||||
GenerateSignInURL = 11,
|
||||
|
||||
#[event(input = "OauthProviderPB", output = "OauthProviderDataPB")]
|
||||
GetOauthURLWithProvider = 12,
|
||||
@ -234,55 +234,73 @@ pub trait UserStatusCallback: Send + Sync + 'static {
|
||||
fn did_update_network(&self, _reachable: bool) {}
|
||||
}
|
||||
|
||||
/// The user cloud service provider.
|
||||
/// The provider can be supabase, firebase, aws, or any other cloud service.
|
||||
/// `UserCloudServiceProvider` defines a set of methods for managing user cloud services,
|
||||
/// including token management, synchronization settings, network reachability, and authentication.
|
||||
///
|
||||
/// This trait is intended for implementation by providers that offer cloud-based services for users.
|
||||
/// It includes methods for handling authentication tokens, enabling/disabling synchronization,
|
||||
/// setting network reachability, managing encryption secrets, and accessing user-specific cloud services.
|
||||
pub trait UserCloudServiceProvider: Send + Sync + 'static {
|
||||
/// Sets the authentication token for the cloud service.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `token`: A string slice representing the authentication token.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `Result` which is `Ok` if the token is successfully set, or a `FlowyError` otherwise.
|
||||
fn set_token(&self, token: &str) -> Result<(), FlowyError>;
|
||||
fn subscribe_token_state(&self) -> Option<WatchStream<UserTokenState>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Subscribes to the state of the authentication token.
|
||||
///
|
||||
/// # Returns
|
||||
/// An `Option` containing a `WatchStream<UserTokenState>` if available, or `None` otherwise.
|
||||
/// The stream allows the caller to watch for changes in the token state.
|
||||
fn subscribe_token_state(&self) -> Option<WatchStream<UserTokenState>>;
|
||||
|
||||
/// Sets the synchronization state for a user.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `uid`: An i64 representing the user ID.
|
||||
/// * `enable_sync`: A boolean indicating whether synchronization should be enabled or disabled.
|
||||
fn set_enable_sync(&self, uid: i64, enable_sync: bool);
|
||||
|
||||
/// Sets the network reachability status.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `reachable`: A boolean indicating whether the network is reachable.
|
||||
fn set_network_reachable(&self, reachable: bool);
|
||||
|
||||
/// Sets the encryption secret for secure communication.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `secret`: A `String` representing the encryption secret.
|
||||
fn set_encrypt_secret(&self, secret: String);
|
||||
|
||||
/// Sets the authenticator used for authentication processes.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `authenticator`: An `Authenticator` object.
|
||||
fn set_authenticator(&self, authenticator: Authenticator);
|
||||
|
||||
/// Retrieves the current authenticator.
|
||||
///
|
||||
/// # Returns
|
||||
/// The current `Authenticator` object.
|
||||
fn get_authenticator(&self) -> Authenticator;
|
||||
|
||||
/// Retrieves the user-specific cloud service.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `Result` containing an `Arc<dyn UserCloudService>` if successful, or a `FlowyError` otherwise.
|
||||
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError>;
|
||||
|
||||
/// Retrieves the service URL.
|
||||
///
|
||||
/// # Returns
|
||||
/// A `String` representing the service URL.
|
||||
fn service_url(&self) -> String;
|
||||
}
|
||||
|
||||
impl<T> UserCloudServiceProvider for Arc<T>
|
||||
where
|
||||
T: UserCloudServiceProvider,
|
||||
{
|
||||
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
|
||||
(**self).set_token(token)
|
||||
}
|
||||
|
||||
fn set_enable_sync(&self, uid: i64, enable_sync: bool) {
|
||||
(**self).set_enable_sync(uid, enable_sync)
|
||||
}
|
||||
|
||||
fn set_encrypt_secret(&self, secret: String) {
|
||||
(**self).set_encrypt_secret(secret)
|
||||
}
|
||||
|
||||
fn set_authenticator(&self, authenticator: Authenticator) {
|
||||
(**self).set_authenticator(authenticator)
|
||||
}
|
||||
|
||||
fn get_authenticator(&self) -> Authenticator {
|
||||
(**self).get_authenticator()
|
||||
}
|
||||
|
||||
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
|
||||
(**self).get_user_service()
|
||||
}
|
||||
|
||||
fn service_url(&self) -> String {
|
||||
(**self).service_url()
|
||||
}
|
||||
}
|
||||
|
||||
/// Acts as a placeholder [UserStatusCallback] for the user session, but does not perform any function
|
||||
pub(crate) struct DefaultUserStatusCallback;
|
||||
impl UserStatusCallback for DefaultUserStatusCallback {
|
||||
|
@ -291,14 +291,14 @@ impl UserManager {
|
||||
#[tracing::instrument(level = "debug", skip(self, params))]
|
||||
pub async fn sign_in(
|
||||
&self,
|
||||
params: BoxAny,
|
||||
params: SignInParams,
|
||||
authenticator: Authenticator,
|
||||
) -> Result<UserProfile, FlowyError> {
|
||||
self.update_authenticator(&authenticator).await;
|
||||
let response: AuthResponse = self
|
||||
.cloud_services
|
||||
.get_user_service()?
|
||||
.sign_in(params)
|
||||
.sign_in(BoxAny::new(params))
|
||||
.await?;
|
||||
let session = Session::from(&response);
|
||||
self.prepare_user(&session).await;
|
||||
@ -362,7 +362,7 @@ impl UserManager {
|
||||
let auth_service = self.cloud_services.get_user_service()?;
|
||||
let response: AuthResponse = auth_service.sign_up(params).await?;
|
||||
let user_profile = UserProfile::from((&response, &authenticator));
|
||||
if user_profile.encryption_type.is_need_encrypt_secret() {
|
||||
if user_profile.encryption_type.require_encrypt_secret() {
|
||||
self
|
||||
.resumable_sign_up
|
||||
.lock()
|
||||
|
@ -51,9 +51,7 @@ private = true
|
||||
script = [
|
||||
"""
|
||||
cd rust-lib/
|
||||
rustup show
|
||||
echo cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
RUSTFLAGS="--cfg tokio_unstable" cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
cd ../
|
||||
""",
|
||||
]
|
||||
@ -64,8 +62,6 @@ private = true
|
||||
script = [
|
||||
"""
|
||||
cd rust-lib/
|
||||
rustup show
|
||||
echo RUSTFLAGS="-C target-cpu=native -C link-arg=-mmacosx-version-min=11.0" cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
RUSTFLAGS="--cfg tokio_unstable" cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
cd ../
|
||||
""",
|
||||
@ -106,6 +102,16 @@ script = [
|
||||
]
|
||||
script_runner = "@shell"
|
||||
|
||||
[tasks.sdk-release-build.mac]
|
||||
script = [
|
||||
"""
|
||||
cd rust-lib/
|
||||
cargo build --profile ${CARGO_PROFILE} --${BUILD_FLAG} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
cd ../
|
||||
""",
|
||||
]
|
||||
script_runner = "@shell"
|
||||
|
||||
#
|
||||
[tasks.post-desktop]
|
||||
mac_alias = "post-desktop-macos"
|
||||
|
@ -33,7 +33,7 @@ dependencies = ["inner_build_test_backend"]
|
||||
description = "Run flutter unit tests"
|
||||
script = '''
|
||||
cd appflowy_flutter
|
||||
flutter test --dart-define=RUST_LOG=${RUST_LOG} -j, --concurrency=1 --coverage
|
||||
flutter test -j, --concurrency=1 --coverage
|
||||
'''
|
||||
|
||||
[tasks.dart_unit_test_no_build]
|
||||
@ -57,7 +57,7 @@ dependencies = ["copy-from-build-to-sandbox-folder"]
|
||||
description = "Run flutter unit tests"
|
||||
script = '''
|
||||
cd appflowy_flutter
|
||||
flutter test --dart-define=RUST_LOG=${RUST_LOG} -j, --concurrency=1 --coverage
|
||||
flutter test -j, --concurrency=1 --coverage
|
||||
'''
|
||||
script_runner = "@shell"
|
||||
|
||||
@ -259,6 +259,7 @@ run_task = { name = [
|
||||
|
||||
|
||||
[tasks.build_test_backend]
|
||||
env = { RUST_LOG = "trace" }
|
||||
script = '''
|
||||
cargo make --profile test-macos-$(uname -m) inner_build_test_backend
|
||||
'''
|
||||
@ -292,6 +293,7 @@ windows_alias = "compile_test_backend_windows"
|
||||
linux_alias = "compile_test_backend_default"
|
||||
|
||||
[tasks.compile_test_backend_default]
|
||||
env = { RUST_LOG = "trace" }
|
||||
private = true
|
||||
script = [
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user