mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[Fix] delete cover image document update (#2224)
* fix: reset cover on deleting selected cover image * feat: add alert dialog on delete * fix: update cover image list when file not found * fix: missing const in change cover popup * fix: added dialog string in translation file * fix: analytical issue in changeCoverPopover * fix: added trailing commas in change_cover_popover
This commit is contained in:
parent
92f980347f
commit
01ced3bdc0
@ -384,7 +384,9 @@
|
|||||||
"pickFromFiles": "Pick from files",
|
"pickFromFiles": "Pick from files",
|
||||||
"couldNotFetchImage": "Could not fetch image",
|
"couldNotFetchImage": "Could not fetch image",
|
||||||
"imageSavingFailed": "Image Saving Failed",
|
"imageSavingFailed": "Image Saving Failed",
|
||||||
"addIcon": "Add Icon"
|
"addIcon": "Add Icon",
|
||||||
|
"coverRemoveAlert" : "It will be removed from cover after it is deleted.",
|
||||||
|
"alertDialogConfirmation" : "Are you sure, you want to continue?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -79,8 +79,10 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => ChangeCoverPopoverBloc()
|
create: (context) => ChangeCoverPopoverBloc(
|
||||||
..add(const ChangeCoverPopoverEvent.fetchPickedImagePaths()),
|
editorState: widget.editorState,
|
||||||
|
node: widget.node,
|
||||||
|
)..add(const ChangeCoverPopoverEvent.fetchPickedImagePaths()),
|
||||||
child: BlocBuilder<ChangeCoverPopoverBloc, ChangeCoverPopoverState>(
|
child: BlocBuilder<ChangeCoverPopoverBloc, ChangeCoverPopoverState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -149,10 +151,31 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
|
|||||||
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
hoverColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
LocaleKeys.document_plugins_cover_clearAll.tr(),
|
LocaleKeys.document_plugins_cover_clearAll.tr(),
|
||||||
fontColor: Theme.of(context).colorScheme.tertiary,
|
fontColor: Theme.of(context).colorScheme.tertiary,
|
||||||
onPressed: () {
|
onPressed: () async {
|
||||||
|
final hasFileImageCover = CoverSelectionType.fromString(
|
||||||
|
widget.node.attributes[kCoverSelectionTypeAttribute],
|
||||||
|
) ==
|
||||||
|
CoverSelectionType.file;
|
||||||
|
final changeCoverBloc = context.read<ChangeCoverPopoverBloc>();
|
||||||
|
if (hasFileImageCover) {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return DeleteImageAlertDialog(
|
||||||
|
onSubmit: () {
|
||||||
|
changeCoverBloc.add(
|
||||||
|
const ChangeCoverPopoverEvent.clearAllImages(),
|
||||||
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
context
|
context
|
||||||
.read<ChangeCoverPopoverBloc>()
|
.read<ChangeCoverPopoverBloc>()
|
||||||
.add(const ChangeCoverPopoverEvent.clearAllImages());
|
.add(const ChangeCoverPopoverEvent.clearAllImages());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
),
|
),
|
||||||
@ -263,6 +286,32 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
|
|||||||
images[index - 1],
|
images[index - 1],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
onImageDelete: () async {
|
||||||
|
final changeCoverBloc =
|
||||||
|
context.read<ChangeCoverPopoverBloc>();
|
||||||
|
final deletingCurrentCover =
|
||||||
|
widget.node.attributes[kCoverSelectionAttribute] ==
|
||||||
|
images[index - 1];
|
||||||
|
if (deletingCurrentCover) {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return DeleteImageAlertDialog(
|
||||||
|
onSubmit: () {
|
||||||
|
changeCoverBloc.add(
|
||||||
|
ChangeCoverPopoverEvent.deleteImage(
|
||||||
|
images[index - 1],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
changeCoverBloc.add(DeleteImage(images[index - 1]));
|
||||||
|
}
|
||||||
|
},
|
||||||
imagePath: images[index - 1],
|
imagePath: images[index - 1],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -285,14 +334,68 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DeleteImageAlertDialog extends StatelessWidget {
|
||||||
|
const DeleteImageAlertDialog({
|
||||||
|
Key? key,
|
||||||
|
required this.onSubmit,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Function() onSubmit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: FlowyText.semibold(
|
||||||
|
"Image is used in cover",
|
||||||
|
fontSize: 20,
|
||||||
|
color: Theme.of(context).colorScheme.tertiary,
|
||||||
|
),
|
||||||
|
content: Container(
|
||||||
|
constraints: const BoxConstraints(minHeight: 100),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 20,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Text(LocaleKeys.document_plugins_cover_coverRemoveAlert).tr(),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
const Text(
|
||||||
|
LocaleKeys.document_plugins_cover_alertDialogConfirmation,
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10.0,
|
||||||
|
horizontal: 20.0,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text(LocaleKeys.button_Cancel).tr(),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: onSubmit,
|
||||||
|
child: const Text(LocaleKeys.button_OK).tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ImageGridItem extends StatefulWidget {
|
class ImageGridItem extends StatefulWidget {
|
||||||
const ImageGridItem({
|
const ImageGridItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.onImageSelect,
|
required this.onImageSelect,
|
||||||
|
required this.onImageDelete,
|
||||||
required this.imagePath,
|
required this.imagePath,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Function() onImageSelect;
|
final Function() onImageSelect;
|
||||||
|
final Function() onImageDelete;
|
||||||
final String imagePath;
|
final String imagePath;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -343,11 +446,7 @@ class _ImageGridItemState extends State<ImageGridItem> {
|
|||||||
'editor/delete',
|
'editor/delete',
|
||||||
color: Theme.of(context).colorScheme.tertiary,
|
color: Theme.of(context).colorScheme.tertiary,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: widget.onImageDelete,
|
||||||
context.read<ChangeCoverPopoverBloc>().add(
|
|
||||||
ChangeCoverPopoverEvent.deleteImage(widget.imagePath),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -2,6 +2,8 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/document/presentation/plugins/cover/change_cover_popover.dart';
|
import 'package:appflowy/plugins/document/presentation/plugins/cover/change_cover_popover.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart';
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@ -10,9 +12,12 @@ part 'change_cover_popover_bloc.freezed.dart';
|
|||||||
|
|
||||||
class ChangeCoverPopoverBloc
|
class ChangeCoverPopoverBloc
|
||||||
extends Bloc<ChangeCoverPopoverEvent, ChangeCoverPopoverState> {
|
extends Bloc<ChangeCoverPopoverEvent, ChangeCoverPopoverState> {
|
||||||
|
final EditorState editorState;
|
||||||
|
final Node node;
|
||||||
late final SharedPreferences _prefs;
|
late final SharedPreferences _prefs;
|
||||||
final _initCompleter = Completer<void>();
|
final _initCompleter = Completer<void>();
|
||||||
ChangeCoverPopoverBloc() : super(const ChangeCoverPopoverState.initial()) {
|
ChangeCoverPopoverBloc({required this.editorState, required this.node})
|
||||||
|
: super(const ChangeCoverPopoverState.initial()) {
|
||||||
SharedPreferences.getInstance().then((prefs) {
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
_prefs = prefs;
|
_prefs = prefs;
|
||||||
_initCompleter.complete();
|
_initCompleter.complete();
|
||||||
@ -26,8 +31,13 @@ class ChangeCoverPopoverBloc
|
|||||||
},
|
},
|
||||||
deleteImage: (DeleteImage deleteImage) async {
|
deleteImage: (DeleteImage deleteImage) async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
final currentlySelectedImage =
|
||||||
|
node.attributes[kCoverSelectionAttribute];
|
||||||
if (currentState is Loaded) {
|
if (currentState is Loaded) {
|
||||||
await _deleteImageInStorage(deleteImage.path);
|
await _deleteImageInStorage(deleteImage.path);
|
||||||
|
if (currentlySelectedImage == deleteImage.path) {
|
||||||
|
_removeCoverImageFromNode();
|
||||||
|
}
|
||||||
final updateImageList = currentState.imageNames
|
final updateImageList = currentState.imageNames
|
||||||
.where((path) => path != deleteImage.path)
|
.where((path) => path != deleteImage.path)
|
||||||
.toList();
|
.toList();
|
||||||
@ -37,9 +47,15 @@ class ChangeCoverPopoverBloc
|
|||||||
},
|
},
|
||||||
clearAllImages: (ClearAllImages clearAllImages) async {
|
clearAllImages: (ClearAllImages clearAllImages) async {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
final currentlySelectedImage =
|
||||||
|
node.attributes[kCoverSelectionAttribute];
|
||||||
|
|
||||||
if (currentState is Loaded) {
|
if (currentState is Loaded) {
|
||||||
for (final image in currentState.imageNames) {
|
for (final image in currentState.imageNames) {
|
||||||
await _deleteImageInStorage(image);
|
await _deleteImageInStorage(image);
|
||||||
|
if (currentlySelectedImage == image) {
|
||||||
|
_removeCoverImageFromNode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await _updateImagePathsInStorage([]);
|
await _updateImagePathsInStorage([]);
|
||||||
emit(const Loaded([]));
|
emit(const Loaded([]));
|
||||||
@ -56,6 +72,7 @@ class ChangeCoverPopoverBloc
|
|||||||
return imageNames;
|
return imageNames;
|
||||||
}
|
}
|
||||||
imageNames.removeWhere((name) => !File(name).existsSync());
|
imageNames.removeWhere((name) => !File(name).existsSync());
|
||||||
|
_prefs.setStringList(kLocalImagesKey, imageNames);
|
||||||
return imageNames;
|
return imageNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +86,15 @@ class ChangeCoverPopoverBloc
|
|||||||
final imageFile = File(path);
|
final imageFile = File(path);
|
||||||
await imageFile.delete();
|
await imageFile.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _removeCoverImageFromNode() async {
|
||||||
|
final transaction = editorState.transaction;
|
||||||
|
transaction.updateNode(node, {
|
||||||
|
kCoverSelectionTypeAttribute: CoverSelectionType.initial.toString(),
|
||||||
|
kIconSelectionAttribute: node.attributes[kIconSelectionAttribute]
|
||||||
|
});
|
||||||
|
return editorState.apply(transaction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
@ -443,8 +443,18 @@ class _CoverImageState extends State<_CoverImage> {
|
|||||||
final Widget coverImage;
|
final Widget coverImage;
|
||||||
switch (selectionType) {
|
switch (selectionType) {
|
||||||
case CoverSelectionType.file:
|
case CoverSelectionType.file:
|
||||||
|
final imageFile =
|
||||||
|
File(widget.node.attributes[kCoverSelectionAttribute]);
|
||||||
|
if (!imageFile.existsSync()) {
|
||||||
|
// reset cover state
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
widget.onCoverChanged(CoverSelectionType.initial, null);
|
||||||
|
});
|
||||||
|
coverImage = const SizedBox();
|
||||||
|
break;
|
||||||
|
}
|
||||||
coverImage = Image.file(
|
coverImage = Image.file(
|
||||||
File(widget.node.attributes[kCoverSelectionAttribute]),
|
imageFile,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user