feat: show indicator when importing appflowy data (#4357)

* feat: show indicator when importing appflowy data

* Update frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_file_import_appflowy_data_view.dart

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>

* chore: fix analyzer

* chore: fix test

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
This commit is contained in:
Nathan.fooo 2024-01-11 09:44:33 +08:00 committed by GitHub
parent b1cc4e485b
commit 6e41359fc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 103 additions and 62 deletions

View File

@ -34,6 +34,7 @@ typedef OnError = void Function(FlowyError);
@freezed @freezed
class LoadingState with _$LoadingState { class LoadingState with _$LoadingState {
const factory LoadingState.idle() = _Idle;
const factory LoadingState.loading() = _Loading; const factory LoadingState.loading() = _Loading;
const factory LoadingState.finish( const factory LoadingState.finish(
Either<Unit, FlowyError> successOrFail, Either<Unit, FlowyError> successOrFail,

View File

@ -103,6 +103,7 @@ class BoardPage extends StatelessWidget {
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
), ),
), ),
idle: (_) => const SizedBox.shrink(),
), ),
), ),
); );

View File

@ -131,6 +131,7 @@ class _GridPageState extends State<GridPage> {
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
), ),
), ),
idle: (_) => const SizedBox.shrink(),
); );
}, },
), ),

View File

@ -97,6 +97,7 @@ class _MobileGridPageState extends State<MobileGridPage> {
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
), ),
), ),
idle: (_) => const SizedBox.shrink(),
); );
}, },
), ),

View File

@ -14,17 +14,20 @@ class DeviceOrApplicationInfoTask extends LaunchTask {
@override @override
Future<void> initialize(LaunchContext context) async { Future<void> initialize(LaunchContext context) async {
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); // Can't get the device info from test environment
final PackageInfo packageInfo = await PackageInfo.fromPlatform(); if (!context.env.isTest) {
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
if (Platform.isAndroid) { if (Platform.isAndroid) {
final androidInfo = await deviceInfoPlugin.androidInfo; final androidInfo = await deviceInfoPlugin.androidInfo;
androidSDKVersion = androidInfo.version.sdkInt; androidSDKVersion = androidInfo.version.sdkInt;
} }
if (Platform.isAndroid || Platform.isIOS) { if (Platform.isAndroid || Platform.isIOS) {
applicationVersion = packageInfo.version; applicationVersion = packageInfo.version;
buildNumber = packageInfo.buildNumber; buildNumber = packageInfo.buildNumber;
}
} }
} }

View File

@ -75,7 +75,11 @@ class EncryptSecretBloc extends Bloc<EncryptSecretEvent, EncryptSecretState> {
bool isLoading() { bool isLoading() {
final loadingState = state.loadingState; final loadingState = state.loadingState;
if (loadingState != null) { if (loadingState != null) {
return loadingState.when(loading: () => true, finish: (_) => false); return loadingState.when(
loading: () => true,
finish: (_) => false,
idle: () => false,
);
} }
return false; return false;
} }

View File

@ -69,6 +69,7 @@ class _EncryptSecretScreenState extends State<EncryptSecretScreen> {
child: CircularProgressIndicator.adaptive(), child: CircularProgressIndicator.adaptive(),
), ),
finish: (result) => const SizedBox.shrink(), finish: (result) => const SizedBox.shrink(),
idle: () => const SizedBox.shrink(),
) ?? ) ??
const SizedBox.shrink(); const SizedBox.shrink();
return Center( return Center(

View File

@ -70,6 +70,7 @@ class WorkspaceErrorScreen extends StatelessWidget {
}, },
); );
}, },
idle: () {},
); );
}, },
), ),

View File

@ -1,3 +1,4 @@
import 'package:appflowy/plugins/database/application/defines.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
@ -9,39 +10,51 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'setting_file_importer_bloc.freezed.dart'; part 'setting_file_importer_bloc.freezed.dart';
class SettingFileImporterBloc class SettingFileImportBloc
extends Bloc<SettingFileImportEvent, SettingFileImportState> { extends Bloc<SettingFileImportEvent, SettingFileImportState> {
SettingFileImporterBloc() : super(SettingFileImportState.initial()) { SettingFileImportBloc() : super(SettingFileImportState.initial()) {
on<SettingFileImportEvent>((event, emit) async { on<SettingFileImportEvent>(
await event.when( (event, emit) async {
importAppFlowyDataFolder: (String path) async { await event.when(
final formattedDate = importAppFlowyDataFolder: (String path) async {
DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()); final formattedDate =
final payload = ImportAppFlowyDataPB.create() DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now());
..path = path final payload = ImportAppFlowyDataPB.create()
..importContainerName = "appflowy_import_$formattedDate"; ..path = path
final result = ..importContainerName = "appflowy_import_$formattedDate";
await FolderEventImportAppFlowyDataFolder(payload).send(); emit(
result.fold( state.copyWith(loadingState: const LoadingState.loading()),
(l) { );
emit( FolderEventImportAppFlowyDataFolder(payload).send().then((result) {
state.copyWith( if (!isClosed) {
successOrFail: some(left(unit)), add(SettingFileImportEvent.finishImport(result));
), }
); });
}, },
(err) { finishImport: (result) {
Log.error(err); result.fold(
emit( (l) {
state.copyWith( emit(
successOrFail: some(right(err)), state.copyWith(
), successOrFail: some(left(unit)),
); loadingState: LoadingState.finish(left(unit)),
}, ),
); );
}, },
); (err) {
}); Log.error(err);
emit(
state.copyWith(
successOrFail: some(right(err)),
loadingState: LoadingState.finish(right(err)),
),
);
},
);
},
);
},
);
} }
} }
@ -49,15 +62,20 @@ class SettingFileImporterBloc
class SettingFileImportEvent with _$SettingFileImportEvent { class SettingFileImportEvent with _$SettingFileImportEvent {
const factory SettingFileImportEvent.importAppFlowyDataFolder(String path) = const factory SettingFileImportEvent.importAppFlowyDataFolder(String path) =
_ImportAppFlowyDataFolder; _ImportAppFlowyDataFolder;
const factory SettingFileImportEvent.finishImport(
Either<Unit, FlowyError> result,
) = _ImportResult;
} }
@freezed @freezed
class SettingFileImportState with _$SettingFileImportState { class SettingFileImportState with _$SettingFileImportState {
const factory SettingFileImportState({ const factory SettingFileImportState({
required LoadingState loadingState,
required Option<Either<Unit, FlowyError>> successOrFail, required Option<Either<Unit, FlowyError>> successOrFail,
}) = _SettingFileImportState; }) = _SettingFileImportState;
factory SettingFileImportState.initial() => SettingFileImportState( factory SettingFileImportState.initial() => SettingFileImportState(
loadingState: const LoadingState.idle(),
successOrFail: none(), successOrFail: none(),
); );
} }

View File

@ -30,8 +30,8 @@ class _ImportAppFlowyDataState extends State<ImportAppFlowyData> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => SettingFileImporterBloc(), create: (context) => SettingFileImportBloc(),
child: BlocListener<SettingFileImporterBloc, SettingFileImportState>( child: BlocListener<SettingFileImportBloc, SettingFileImportState>(
listener: (context, state) { listener: (context, state) {
state.successOrFail.fold( state.successOrFail.fold(
() {}, () {},
@ -47,7 +47,7 @@ class _ImportAppFlowyDataState extends State<ImportAppFlowyData> {
}, },
); );
}, },
child: BlocBuilder<SettingFileImporterBloc, SettingFileImportState>( child: BlocBuilder<SettingFileImportBloc, SettingFileImportState>(
builder: (context, state) { builder: (context, state) {
return const Column( return const Column(
children: [ children: [
@ -120,24 +120,33 @@ class ImportAppFlowyDataButton extends StatefulWidget {
class _ImportAppFlowyDataButtonState extends State<ImportAppFlowyDataButton> { class _ImportAppFlowyDataButtonState extends State<ImportAppFlowyDataButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return BlocBuilder<SettingFileImportBloc, SettingFileImportState>(
height: 40, builder: (context, state) {
child: FlowyButton( return Column(
text: FlowyText(LocaleKeys.settings_menu_importAppFlowyData.tr()), children: [
onTap: () async { SizedBox(
final path = await getIt<FilePickerService>().getDirectoryPath(); height: 40,
if (path == null) { child: FlowyButton(
return; text:
} FlowyText(LocaleKeys.settings_menu_importAppFlowyData.tr()),
if (!mounted) { onTap: () async {
return; final path =
} await getIt<FilePickerService>().getDirectoryPath();
if (path == null || !mounted) {
return;
}
context context.read<SettingFileImportBloc>().add(
.read<SettingFileImporterBloc>() SettingFileImportEvent.importAppFlowyDataFolder(path),
.add(SettingFileImportEvent.importAppFlowyDataFolder(path)); );
}, },
), ),
),
if (state.loadingState.isLoading())
const LinearProgressIndicator(minHeight: 1),
],
);
},
); );
} }
} }

View File

@ -154,6 +154,7 @@ class EnableEncrypt extends StatelessWidget {
final indicator = state.loadingState.when( final indicator = state.loadingState.when(
loading: () => const CircularProgressIndicator.adaptive(), loading: () => const CircularProgressIndicator.adaptive(),
finish: (successOrFail) => const SizedBox.shrink(), finish: (successOrFail) => const SizedBox.shrink(),
idle: () => const SizedBox.shrink(),
); );
return Column( return Column(