♻️ Refactored using controller as post editing

This commit is contained in:
2024-11-11 21:30:05 +08:00
parent 1ff4dc2a4b
commit f23ffe61f5
12 changed files with 851 additions and 558 deletions

View File

@ -35,6 +35,7 @@ class AccountImage extends StatelessWidget {
UniversalImage.provider(url),
width: ((radius ?? 20) * devicePixelRatio * 2).round(),
height: ((radius ?? 20) * devicePixelRatio * 2).round(),
policy: ResizeImagePolicy.fit,
)
: null,
child: (content?.isEmpty ?? true)

View File

@ -1,15 +1,13 @@
import 'dart:io';
import 'package:cross_file/cross_file.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_context_menu/flutter_context_menu.dart';
import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/post_write_controller.dart';
class PostMediaPendingList extends StatelessWidget {
final List<XFile> data;
final List<PostWriteMedia> data;
final Function(int idx)? onRemove;
const PostMediaPendingList({
super.key,
@ -19,6 +17,8 @@ class PostMediaPendingList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
return Container(
constraints: const BoxConstraints(maxHeight: 120),
child: ListView.separated(
@ -53,9 +53,25 @@ class PostMediaPendingList extends StatelessWidget {
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AspectRatio(
aspectRatio: 1,
child: kIsWeb
? Image.network(file.path, fit: BoxFit.cover)
: Image.file(File(file.path), fit: BoxFit.cover),
child: switch (file.type) {
PostWriteMediaType.image =>
LayoutBuilder(builder: (context, constraints) {
return Image(
image: file.getImageProvider(
context,
width: (constraints.maxWidth * devicePixelRatio)
.round(),
height: (constraints.maxHeight * devicePixelRatio)
.round(),
)!,
fit: BoxFit.cover,
);
}),
_ => Container(
color: Theme.of(context).colorScheme.surface,
child: const Icon(Symbols.docs).center(),
),
},
),
),
),

View File

@ -1,87 +1,130 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/controllers/post_write_controller.dart';
class PostMetaResult {
final String title;
final String description;
class PostMetaEditor extends StatelessWidget {
final PostWriteController controller;
const PostMetaEditor({super.key, required this.controller});
PostMetaResult({required this.title, required this.description});
}
class PostMetaEditor extends StatefulWidget {
final String? initialTitle;
final String? initialDescription;
const PostMetaEditor({super.key, this.initialTitle, this.initialDescription});
@override
State<PostMetaEditor> createState() => _PostMetaEditorState();
}
class _PostMetaEditorState extends State<PostMetaEditor> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
void _applyChanges() {
Navigator.pop(
context,
PostMetaResult(
title: _titleController.text,
description: _descriptionController.text,
Future<DateTime?> _selectDate(
BuildContext context, {
DateTime? initialDateTime,
}) async {
DateTime? picked;
await showCupertinoModalPopup(
context: context,
builder: (BuildContext context) => Container(
height: 216,
padding: const EdgeInsets.only(top: 6.0),
margin: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
top: false,
child: CupertinoDatePicker(
initialDateTime: initialDateTime,
mode: CupertinoDatePickerMode.dateAndTime,
use24hFormat: true,
onDateTimeChanged: (DateTime newDate) {
picked = newDate;
},
),
),
),
);
}
@override
void initState() {
super.initState();
_titleController.text = widget.initialTitle ?? '';
_descriptionController.text = widget.initialDescription ?? '';
}
@override
void dispose() {
_titleController.dispose();
_descriptionController.dispose();
super.dispose();
return picked;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(
labelText: 'fieldPostTitle'.tr(),
border: UnderlineInputBorder(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const Gap(4),
TextField(
controller: _descriptionController,
maxLines: null,
decoration: InputDecoration(
labelText: 'fieldPostDescription'.tr(),
border: UnderlineInputBorder(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.end,
final dateFormatter = DateFormat('y/M/d HH:mm:ss');
return ListenableBuilder(
listenable: controller,
builder: (context, _) {
return Column(
children: [
ElevatedButton.icon(
onPressed: _applyChanges,
icon: const Icon(Symbols.save),
label: Text('apply').tr(),
TextField(
controller: controller.titleController,
decoration: InputDecoration(
labelText: 'fieldPostTitle'.tr(),
border: UnderlineInputBorder(),
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 24),
if (controller.mode == 'article') const Gap(4),
if (controller.mode == 'article')
TextField(
controller: controller.descriptionController,
maxLines: null,
decoration: InputDecoration(
labelText: 'fieldPostDescription'.tr(),
border: UnderlineInputBorder(),
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
).padding(horizontal: 24),
const Gap(12),
ListTile(
leading: const Icon(Symbols.event_available),
title: Text('postPublishedAt').tr(),
subtitle: Text(
controller.publishedAt != null
? dateFormatter.format(controller.publishedAt!)
: 'unset'.tr(),
),
trailing: controller.publishedAt != null
? IconButton(
icon: const Icon(Symbols.cancel),
onPressed: () {
controller.setPublishedAt(null);
},
)
: null,
contentPadding: const EdgeInsets.only(left: 24, right: 18),
onTap: () {
_selectDate(
context,
initialDateTime: controller.publishedAt,
).then((value) {
controller.setPublishedAt(value);
});
},
),
ListTile(
leading: const Icon(Symbols.event_busy),
title: Text('postPublishedUntil').tr(),
subtitle: Text(
controller.publishedUntil != null
? dateFormatter.format(controller.publishedUntil!)
: 'unset'.tr(),
),
trailing: controller.publishedUntil != null
? IconButton(
icon: const Icon(Symbols.cancel),
onPressed: () {
controller.setPublishedUntil(null);
},
)
: null,
contentPadding: const EdgeInsets.only(left: 24, right: 18),
onTap: () {
_selectDate(
context,
initialDateTime: controller.publishedUntil,
).then((value) {
controller.setPublishedUntil(value);
});
},
),
],
)
],
).padding(horizontal: 24, vertical: 8);
).padding(vertical: 8);
},
);
}
}

View File

@ -0,0 +1,10 @@
import 'package:flutter/widgets.dart';
class PostMiniEditor extends StatelessWidget {
const PostMiniEditor({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}