mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-19 18:43:32 +00:00
157 lines
5 KiB
Dart
157 lines
5 KiB
Dart
|
import 'package:flutter/material.dart';
|
||
|
import 'package:go_router/go_router.dart';
|
||
|
import 'package:result_monad/result_monad.dart';
|
||
|
|
||
|
import '../controls/login_aware_cached_network_image.dart';
|
||
|
import '../controls/padding.dart';
|
||
|
import '../controls/responsive_max_width.dart';
|
||
|
import '../controls/standard_appbar.dart';
|
||
|
import '../globals.dart';
|
||
|
import '../models/exec_error.dart';
|
||
|
import '../models/image_entry.dart';
|
||
|
import '../models/visibility.dart';
|
||
|
import '../services/gallery_service.dart';
|
||
|
import '../utils/active_profile_selector.dart';
|
||
|
import '../utils/snackbar_builder.dart';
|
||
|
|
||
|
class ImageEditorScreen extends StatefulWidget {
|
||
|
final String galleryName;
|
||
|
final String imageId;
|
||
|
|
||
|
const ImageEditorScreen({
|
||
|
super.key,
|
||
|
required this.galleryName,
|
||
|
required this.imageId,
|
||
|
});
|
||
|
|
||
|
@override
|
||
|
State<ImageEditorScreen> createState() => _ImageEditorScreenState();
|
||
|
}
|
||
|
|
||
|
class _ImageEditorScreenState extends State<ImageEditorScreen> {
|
||
|
late final Result<ImageEntry, ExecError> originalImageResult;
|
||
|
final altTextController = TextEditingController();
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
originalImageResult = getIt<ActiveProfileSelector<GalleryService>>()
|
||
|
.activeEntry
|
||
|
.andThen((gs) => gs.getImage(widget.galleryName, widget.imageId))
|
||
|
.withResult((image) {
|
||
|
altTextController.text = image.description;
|
||
|
}).execErrorCast();
|
||
|
}
|
||
|
|
||
|
bool get changed => originalImageResult
|
||
|
.transform((image) => image.description != altTextController.text)
|
||
|
.getValueOrElse(() => false);
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: StandardAppBar.build(
|
||
|
context,
|
||
|
'Edit Image',
|
||
|
withDrawer: true,
|
||
|
),
|
||
|
body: SingleChildScrollView(
|
||
|
child: ResponsiveMaxWidth(
|
||
|
child: Padding(
|
||
|
padding: const EdgeInsets.all(8.0),
|
||
|
child: Column(
|
||
|
children: [
|
||
|
...originalImageResult.fold(
|
||
|
onSuccess: (image) => buildEditor(image),
|
||
|
onError: (error) => buildError(error),
|
||
|
),
|
||
|
const VerticalPadding(),
|
||
|
Row(
|
||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
children: [
|
||
|
ElevatedButton(
|
||
|
onPressed: () async {
|
||
|
if (!changed) {
|
||
|
return;
|
||
|
}
|
||
|
final result = await getIt<
|
||
|
ActiveProfileSelector<GalleryService>>()
|
||
|
.activeEntry
|
||
|
.andThenAsync(
|
||
|
(gs) async => await gs
|
||
|
.updateImage(originalImageResult.value.copy(
|
||
|
description: altTextController.text,
|
||
|
)),
|
||
|
);
|
||
|
|
||
|
if (!mounted) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
result.match(
|
||
|
onSuccess: (_) => context.pop(),
|
||
|
onError: (error) => buildSnackbar(context,
|
||
|
'Error attempting to update image: $error'),
|
||
|
);
|
||
|
},
|
||
|
child: const Text('Save')),
|
||
|
const HorizontalPadding(),
|
||
|
ElevatedButton(
|
||
|
onPressed: () async {
|
||
|
if (!changed) {
|
||
|
context.pop();
|
||
|
}
|
||
|
|
||
|
final ok = await showYesNoDialog(
|
||
|
context,
|
||
|
'Cancel changes?',
|
||
|
);
|
||
|
if (ok == true && mounted) {
|
||
|
context.pop();
|
||
|
}
|
||
|
},
|
||
|
child: const Text('Cancel')),
|
||
|
],
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
|
||
|
List<Widget> buildEditor(ImageEntry originalImage) {
|
||
|
return [
|
||
|
Row(
|
||
|
children: [
|
||
|
const Text('Visibility:'),
|
||
|
const HorizontalPadding(),
|
||
|
originalImage.visibility.type == VisibilityType.public
|
||
|
? const Icon(Icons.public)
|
||
|
: const Icon(Icons.lock),
|
||
|
],
|
||
|
),
|
||
|
const VerticalPadding(),
|
||
|
LoginAwareCachedNetworkImage(imageUrl: originalImage.thumbnailUrl),
|
||
|
const VerticalPadding(),
|
||
|
TextField(
|
||
|
controller: altTextController,
|
||
|
maxLines: 10,
|
||
|
decoration: InputDecoration(
|
||
|
labelText: 'ALT Text',
|
||
|
alignLabelWithHint: true,
|
||
|
border: OutlineInputBorder(
|
||
|
borderSide: const BorderSide(),
|
||
|
borderRadius: BorderRadius.circular(5.0),
|
||
|
),
|
||
|
),
|
||
|
),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
List<Widget> buildError(ExecError error) {
|
||
|
return [Text('Error loading image: $error')];
|
||
|
}
|
||
|
}
|