feat: select cover image on upload (#3488)

This commit is contained in:
Mathias Mogensen 2023-09-21 04:02:39 +02:00 committed by GitHub
parent 2c757e9b6c
commit 048434024b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 93 deletions

View File

@ -41,36 +41,6 @@ class ChangeCoverPopover extends StatefulWidget {
State<ChangeCoverPopover> createState() => _ChangeCoverPopoverState();
}
class ColorOption {
final String colorHex;
final String name;
const ColorOption({
required this.colorHex,
required this.name,
});
}
class CoverColorPicker extends StatefulWidget {
final String? selectedBackgroundColorHex;
final Color pickerBackgroundColor;
final Color pickerItemHoverColor;
final void Function(String color) onSubmittedBackgroundColorHex;
final List<ColorOption> backgroundColorOptions;
const CoverColorPicker({
super.key,
this.selectedBackgroundColorHex,
required this.pickerBackgroundColor,
required this.backgroundColorOptions,
required this.pickerItemHoverColor,
required this.onSubmittedBackgroundColorHex,
});
@override
State<CoverColorPicker> createState() => _CoverColorPickerState();
}
class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
bool isAddingImage = false;
@ -81,7 +51,15 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
editorState: widget.editorState,
node: widget.node,
)..add(const ChangeCoverPopoverEvent.fetchPickedImagePaths()),
child: BlocBuilder<ChangeCoverPopoverBloc, ChangeCoverPopoverState>(
child: BlocConsumer<ChangeCoverPopoverBloc, ChangeCoverPopoverState>(
listener: (context, state) {
if (state is Loaded && state.selectLatestImage) {
widget.onCoverChanged(
CoverType.file,
state.imageNames.last,
);
}
},
builder: (context, state) {
return Padding(
padding: const EdgeInsets.all(12),
@ -91,14 +69,15 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
onBackPressed: () => setState(() {
isAddingImage = false;
}),
onFileSubmit: (List<String> path) {
onFileSubmit: (_) {
context.read<ChangeCoverPopoverBloc>().add(
const ChangeCoverPopoverEvent
.fetchPickedImagePaths(),
.fetchPickedImagePaths(
selectLatestImage: true,
),
);
setState(() {
isAddingImage = false;
});
setState(() => isAddingImage = false);
},
)
: _buildCoverSelection(),
@ -294,7 +273,8 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
},
);
}
return Container();
return const SizedBox.shrink();
},
);
}
@ -314,6 +294,7 @@ class _ChangeCoverPopoverState extends State<ChangeCoverPopover> {
@visibleForTesting
class NewCustomCoverButton extends StatelessWidget {
final VoidCallback onPressed;
const NewCustomCoverButton({super.key, required this.onPressed});
@override
@ -337,6 +318,85 @@ class NewCustomCoverButton extends StatelessWidget {
}
}
class ColorOption {
final String colorHex;
final String name;
const ColorOption({
required this.colorHex,
required this.name,
});
}
class CoverColorPicker extends StatefulWidget {
final String? selectedBackgroundColorHex;
final Color pickerBackgroundColor;
final Color pickerItemHoverColor;
final void Function(String color) onSubmittedBackgroundColorHex;
final List<ColorOption> backgroundColorOptions;
const CoverColorPicker({
super.key,
this.selectedBackgroundColorHex,
required this.pickerBackgroundColor,
required this.backgroundColorOptions,
required this.pickerItemHoverColor,
required this.onSubmittedBackgroundColorHex,
});
@override
State<CoverColorPicker> createState() => _CoverColorPickerState();
}
class _CoverColorPickerState extends State<CoverColorPicker> {
final scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return Container(
height: 30,
alignment: Alignment.center,
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
},
platform: TargetPlatform.windows,
),
child: SingleChildScrollView(
child: _buildColorItems(
widget.backgroundColorOptions,
widget.selectedBackgroundColorHex,
),
),
),
);
}
@override
void dispose() {
super.dispose();
scrollController.dispose();
}
Widget _buildColorItems(List<ColorOption> options, String? selectedColor) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: options
.map(
(e) => ColorItem(
option: e,
isChecked: e.colorHex == selectedColor,
hoverColor: widget.pickerItemHoverColor,
onTap: widget.onSubmittedBackgroundColorHex,
),
)
.toList(),
);
}
}
class DeleteImageAlertDialog extends StatelessWidget {
const DeleteImageAlertDialog({
Key? key,
@ -458,55 +518,6 @@ class _ImageGridItemState extends State<ImageGridItem> {
}
}
class _CoverColorPickerState extends State<CoverColorPicker> {
final scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return Container(
height: 30,
alignment: Alignment.center,
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
},
platform: TargetPlatform.windows,
),
child: SingleChildScrollView(
child: _buildColorItems(
widget.backgroundColorOptions,
widget.selectedBackgroundColorHex,
),
),
),
);
}
@override
void dispose() {
super.dispose();
scrollController.dispose();
}
Widget _buildColorItems(List<ColorOption> options, String? selectedColor) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: options
.map(
(e) => ColorItem(
option: e,
isChecked: e.colorHex == selectedColor,
hoverColor: widget.pickerItemHoverColor,
onTap: widget.onSubmittedBackgroundColorHex,
),
)
.toList(),
);
}
}
@visibleForTesting
class ColorItem extends StatelessWidget {
final ColorOption option;

View File

@ -16,18 +16,28 @@ class ChangeCoverPopoverBloc
final Node node;
late final SharedPreferences _prefs;
final _initCompleter = Completer<void>();
ChangeCoverPopoverBloc({required this.editorState, required this.node})
: super(const ChangeCoverPopoverState.initial()) {
ChangeCoverPopoverBloc({
required this.editorState,
required this.node,
}) : super(const ChangeCoverPopoverState.initial()) {
SharedPreferences.getInstance().then((prefs) {
_prefs = prefs;
_initCompleter.complete();
});
on<ChangeCoverPopoverEvent>((event, emit) async {
await event.map(
fetchPickedImagePaths:
(FetchPickedImagePaths fetchPickedImagePaths) async {
final imageNames = await _getPreviouslyPickedImagePaths();
emit(ChangeCoverPopoverState.loaded(imageNames));
emit(
ChangeCoverPopoverState.loaded(
imageNames,
selectLatestImage: fetchPickedImagePaths.selectLatestImage,
),
);
},
deleteImage: (DeleteImage deleteImage) async {
final currentState = state;
@ -100,8 +110,9 @@ class ChangeCoverPopoverBloc
@freezed
class ChangeCoverPopoverEvent with _$ChangeCoverPopoverEvent {
const factory ChangeCoverPopoverEvent.fetchPickedImagePaths() =
FetchPickedImagePaths;
const factory ChangeCoverPopoverEvent.fetchPickedImagePaths({
@Default(false) bool selectLatestImage,
}) = FetchPickedImagePaths;
const factory ChangeCoverPopoverEvent.deleteImage(String path) = DeleteImage;
const factory ChangeCoverPopoverEvent.clearAllImages() = ClearAllImages;
@ -112,6 +123,7 @@ class ChangeCoverPopoverState with _$ChangeCoverPopoverState {
const factory ChangeCoverPopoverState.initial() = Initial;
const factory ChangeCoverPopoverState.loading() = Loading;
const factory ChangeCoverPopoverState.loaded(
List<String> imageNames,
) = Loaded;
List<String> imageNames, {
@Default(false) selectLatestImage,
}) = Loaded;
}