Improve FileSharerDialog to match the deprecated one in HomePhotos

This commit is contained in:
Ming Ming 2023-12-26 17:47:41 +08:00
parent 782f73448e
commit 21ac3c5530
5 changed files with 149 additions and 44 deletions

View file

@ -27,7 +27,7 @@ class DownloadProgressDialog extends StatelessWidget {
Align(
alignment: AlignmentDirectional.centerEnd,
child: Text(
"$current/$max",
"${current + 1}/$max",
style: Theme.of(context).textTheme.labelMedium,
),
),

View file

@ -15,10 +15,12 @@ import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/file_descriptor.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/exception.dart';
import 'package:nc_photos/exception_event.dart';
import 'package:nc_photos/exception_util.dart' as exception_util;
import 'package:nc_photos/k.dart' as k;
import 'package:nc_photos/mobile/share.dart';
import 'package:nc_photos/platform/download.dart';
import 'package:nc_photos/remote_storage_util.dart' as remote_storage_util;
import 'package:nc_photos/toast.dart';
import 'package:nc_photos/use_case/copy.dart';
@ -27,6 +29,7 @@ import 'package:nc_photos/use_case/create_share.dart';
import 'package:nc_photos/use_case/download_file.dart';
import 'package:nc_photos/use_case/download_preview.dart';
import 'package:nc_photos/use_case/inflate_file_descriptor.dart';
import 'package:nc_photos/widget/download_progress_dialog.dart';
import 'package:nc_photos/widget/processing_dialog.dart';
import 'package:nc_photos/widget/share_link_multiple_files_dialog.dart';
import 'package:nc_photos/widget/simple_input_dialog.dart';
@ -42,8 +45,6 @@ part 'file_sharer_dialog/bloc.dart';
part 'file_sharer_dialog/state_event.dart';
part 'file_sharer_dialog/type.dart';
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
/// Dialog to let user share files with different options
///
/// Return true if the files are actually shared, false if user cancelled or
@ -78,7 +79,7 @@ class _WrappedFileSharerDialog extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocListener(
listeners: [
BlocListener<_Bloc, _State>(
_BlocListener(
listenWhen: (previous, current) => previous.error != current.error,
listener: (context, state) {
if (state.error != null) {
@ -98,7 +99,7 @@ class _WrappedFileSharerDialog extends StatelessWidget {
}
},
),
BlocListener<_Bloc, _State>(
_BlocListener(
listenWhen: (previous, current) =>
previous.message != current.message,
listener: (context, state) {
@ -111,7 +112,7 @@ class _WrappedFileSharerDialog extends StatelessWidget {
}
},
),
BlocListener<_Bloc, _State>(
_BlocListener(
listenWhen: (previous, current) => previous.result != current.result,
listener: (context, state) {
if (state.result != null) {
@ -146,10 +147,8 @@ class _ShareMethodDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isSupportPerview = context
.read<_Bloc>()
.files
.any((f) => file_util.isSupportedImageFormat(f));
final isSupportPerview =
context.bloc.files.any((f) => file_util.isSupportedImageFormat(f));
return SimpleDialog(
title: Text(L10n.global().shareMethodDialogTitle),
children: [
@ -161,9 +160,7 @@ class _ShareMethodDialog extends StatelessWidget {
subtitle: Text(L10n.global().shareMethodPreviewDescription),
),
onPressed: () {
context
.read<_Bloc>()
.add(const _SetMethod(ShareMethod.preview));
context.addEvent(const _SetMethod(ShareMethod.preview));
},
),
SimpleDialogOption(
@ -172,7 +169,7 @@ class _ShareMethodDialog extends StatelessWidget {
subtitle: Text(L10n.global().shareMethodOriginalFileDescription),
),
onPressed: () {
context.read<_Bloc>().add(const _SetMethod(ShareMethod.file));
context.addEvent(const _SetMethod(ShareMethod.file));
},
),
],
@ -182,7 +179,7 @@ class _ShareMethodDialog extends StatelessWidget {
subtitle: Text(L10n.global().shareMethodPublicLinkDescription),
),
onPressed: () {
context.read<_Bloc>().add(const _SetMethod(ShareMethod.publicLink));
context.addEvent(const _SetMethod(ShareMethod.publicLink));
},
),
SimpleDialogOption(
@ -191,9 +188,7 @@ class _ShareMethodDialog extends StatelessWidget {
subtitle: Text(L10n.global().shareMethodPasswordLinkDescription),
),
onPressed: () {
context
.read<_Bloc>()
.add(const _SetMethod(ShareMethod.passwordLink));
context.addEvent(const _SetMethod(ShareMethod.passwordLink));
},
),
],
@ -206,18 +201,24 @@ class _ShareFileDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _BlocBuilder(
buildWhen: (previous, current) =>
previous.previewState?.index != current.previewState?.index ||
previous.previewState?.count != current.previewState?.count,
builder: (context, state) {
final text = state.previewState?.index != null &&
state.previewState?.count != null
? " (${state.previewState!.index}/${state.previewState!.count})"
: "";
return ProcessingDialog(
text: L10n.global().shareDownloadingDialogContent + text,
return _BlocSelector<_FileState?>(
selector: (state) => state.fileState,
builder: (context, fileState) {
if (fileState != null) {
return DownloadProgressDialog(
max: fileState.count,
current: fileState.index,
progress: fileState.progress,
label: context.bloc.files[fileState.index].filename,
onCancel: () {
context.addEvent(const _CancelFileDownload());
},
);
} else {
return ProcessingDialog(
text: L10n.global().genericProcessingDialogContent,
);
}
},
);
}
@ -288,7 +289,7 @@ class _SharePublicLinkDialogState extends State<_SharePublicLinkDialog> {
}
}
late final _bloc = context.read<_Bloc>();
late final _bloc = context.bloc;
}
class _SharePasswordLinkDialog extends StatefulWidget {
@ -368,5 +369,15 @@ class _SharePasswordLinkDialogState extends State<_SharePasswordLinkDialog> {
}
}
late final _bloc = context.read<_Bloc>();
late final _bloc = context.bloc;
}
typedef _BlocBuilder = BlocBuilder<_Bloc, _State>;
typedef _BlocListener = BlocListener<_Bloc, _State>;
typedef _BlocSelector<T> = BlocSelector<_Bloc, _State, T>;
extension on BuildContext {
_Bloc get bloc => read<_Bloc>();
// _State get state => bloc.state;
void addEvent(_Event event) => bloc.add(event);
}

View file

@ -88,16 +88,32 @@ extension $_PreviewStateCopyWith on _PreviewState {
}
abstract class $_FileStateCopyWithWorker {
_FileState call({int? index, int? count});
_FileState call(
{int? index,
double? progress,
int? count,
Download? download,
bool? shouldRun});
}
class _$_FileStateCopyWithWorkerImpl implements $_FileStateCopyWithWorker {
_$_FileStateCopyWithWorkerImpl(this.that);
@override
_FileState call({dynamic index, dynamic count}) {
_FileState call(
{dynamic index,
dynamic progress = copyWithNull,
dynamic count,
dynamic download = copyWithNull,
dynamic shouldRun}) {
return _FileState(
index: index as int? ?? that.index, count: count as int? ?? that.count);
index: index as int? ?? that.index,
progress:
progress == copyWithNull ? that.progress : progress as double?,
count: count as int? ?? that.count,
download:
download == copyWithNull ? that.download : download as Download?,
shouldRun: shouldRun as bool? ?? that.shouldRun);
}
final _FileState that;
@ -165,7 +181,7 @@ extension _$_PreviewStateToString on _PreviewState {
extension _$_FileStateToString on _FileState {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_FileState {index: $index, count: $count}";
return "_FileState {index: $index, progress: ${progress == null ? null : "${progress!.toStringAsFixed(3)}"}, count: $count, download: $download, shouldRun: $shouldRun}";
}
}
@ -197,6 +213,13 @@ extension _$_SetResultToString on _SetResult {
}
}
extension _$_CancelFileDownloadToString on _CancelFileDownload {
String _$toString() {
// ignore: unnecessary_string_interpolations
return "_CancelFileDownload {}";
}
}
extension _$_SetPublicLinkDetailsToString on _SetPublicLinkDetails {
String _$toString() {
// ignore: unnecessary_string_interpolations

View file

@ -12,6 +12,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
on<_SetResult>(_onSetResult);
on<_SetPublicLinkDetails>(_onSetPublicLinkDetails);
on<_SetPasswordLinkDetails>(_onSetPasswordLinkDetails);
on<_CancelFileDownload>(_onCancelFileDownload);
on<_SetError>(_onSetError);
}
@ -19,6 +20,20 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
@override
String get tag => _log.fullName;
@override
bool Function(dynamic, dynamic)? get shouldLog => (currentState, nextState) {
currentState = currentState as _State;
nextState = nextState as _State;
if (identical(currentState.fileState, nextState.fileState)) {
return true;
}
// don't log download progress
if (currentState.fileState?.progress != nextState.fileState?.progress) {
return false;
}
return true;
};
@override
void onError(Object error, StackTrace stackTrace) {
// we need this to prevent onError being triggered recursively
@ -33,7 +48,7 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
}
Future<void> _onSetMethod(_SetMethod ev, Emitter<_State> emit) async {
_log.info("$ev");
_log.info(ev);
emit(state.copyWith(method: ev.method));
switch (ev.method) {
case ShareMethod.file:
@ -48,22 +63,30 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
}
void _onSetResult(_SetResult ev, Emitter<_State> emit) {
_log.info("$ev");
_log.info(ev);
emit(state.copyWith(result: ev.result));
}
Future<void> _onSetPublicLinkDetails(
_SetPublicLinkDetails ev, Emitter<_State> emit) {
_log.info("$ev");
_log.info(ev);
return _doShareLink(emit, albumName: ev.albumName, password: null);
}
Future<void> _onSetPasswordLinkDetails(
_SetPasswordLinkDetails ev, Emitter<_State> emit) {
_log.info("$ev");
_log.info(ev);
return _doShareLink(emit, albumName: ev.albumName, password: ev.password);
}
void _onCancelFileDownload(_CancelFileDownload ev, Emitter<_State> emit) {
_log.info(ev);
state.fileState?.download?.cancel();
emit(state.copyWith(
fileState: state.fileState?.copyWith(shouldRun: false),
));
}
void _onSetError(_SetError ev, Emitter<_State> emit) {
_log.info(ev);
emit(state.copyWith(error: ExceptionEvent(ev.error, ev.stackTrace)));
@ -72,22 +95,45 @@ class _Bloc extends Bloc<_Event, _State> with BlocLogger {
Future<void> _doShareFile(Emitter<_State> emit) async {
assert(getRawPlatform() == NpPlatform.android);
emit(state.copyWith(
previewState: _PreviewState(index: 0, count: files.length),
fileState: _FileState.init(count: files.length),
));
final results = <Tuple2<FileDescriptor, dynamic>>[];
for (final pair in files.withIndex()) {
final i = pair.item1, f = pair.item2;
emit(state.copyWith(
previewState: state.previewState?.copyWith(index: i),
fileState: state.fileState?.copyWith(
index: i,
progress: null,
),
));
try {
final uri = await DownloadFile()(account, f, shouldNotify: false);
results.add(Tuple2(f, uri));
final download = DownloadFile().build(
account,
f,
shouldNotify: false,
onProgress: (progress) {
emit(state.copyWith(
fileState: state.fileState?.copyWith(progress: progress),
));
},
);
emit(state.copyWith(
fileState: state.fileState?.copyWith(download: download),
));
final result = await download();
if (state.fileState?.shouldRun == false) {
throw const JobCanceledException();
}
results.add(Tuple2(f, result));
} on PermissionException catch (e, stackTrace) {
_log.warning("[_doShareFile] Permission not granted");
emit(state.copyWith(error: ExceptionEvent(e, stackTrace)));
emit(state.copyWith(result: false));
return;
} on JobCanceledException catch (_) {
_log.info("[_doShareFile] Job canceled");
emit(state.copyWith(result: false));
return;
} catch (e, stackTrace) {
_log.shout("[_doShareFile] Failed while DownloadFile", e, stackTrace);
emit(state.copyWith(error: ExceptionEvent(e, stackTrace)));

View file

@ -51,14 +51,31 @@ class _PreviewState {
class _FileState {
const _FileState({
required this.index,
required this.progress,
required this.count,
required this.download,
required this.shouldRun,
});
factory _FileState.init({
required int count,
}) =>
_FileState(
index: 0,
progress: null,
count: count,
download: null,
shouldRun: true,
);
@override
String toString() => _$toString();
final int index;
final double? progress;
final int count;
final Download? download;
final bool shouldRun;
}
@toString
@ -108,6 +125,14 @@ class _SetResult implements _Event {
final bool result;
}
@toString
class _CancelFileDownload implements _Event {
const _CancelFileDownload();
@override
String toString() => _$toString();
}
/// Set the details needed to share files as public link
@toString
class _SetPublicLinkDetails implements _Event {