mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 16:56:19 +01:00
Improve FileSharerDialog to match the deprecated one in HomePhotos
This commit is contained in:
parent
782f73448e
commit
21ac3c5530
5 changed files with 149 additions and 44 deletions
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue