✨ Comment create, delete and update
This commit is contained in:
@ -20,6 +20,7 @@
|
||||
"attachmentAdd": "Add new attachment",
|
||||
"pickPhoto": "Gallery photo",
|
||||
"newMoment": "Record a moment",
|
||||
"newComment": "Leave a comment",
|
||||
"postIdentityNotify": "You will create this post as",
|
||||
"postContentPlaceholder": "What's happened?!",
|
||||
"postDeleteConfirm": "Are you sure you want to delete this post? This operation cannot be revert!"
|
||||
|
@ -20,6 +20,7 @@
|
||||
"attachmentAdd": "附加新附件",
|
||||
"pickPhoto": "相册照片",
|
||||
"newMoment": "记录时刻",
|
||||
"newComment": "留下评论",
|
||||
"postIdentityNotify": "你将会以该身份发表本帖子",
|
||||
"postContentPlaceholder": "发生什么事了?!",
|
||||
"postDeleteConfirm": "你确定要删除这篇帖子吗?这意味着这个帖子将永远被我们丢弃在硬盘海中!该操作不可被反转!"
|
||||
|
@ -2,6 +2,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:solian/models/post.dart';
|
||||
import 'package:solian/screens/account.dart';
|
||||
import 'package:solian/screens/explore.dart';
|
||||
import 'package:solian/screens/posts/comment_editor.dart';
|
||||
import 'package:solian/screens/posts/moment_editor.dart';
|
||||
import 'package:solian/screens/posts/screen.dart';
|
||||
|
||||
@ -18,11 +19,20 @@ final router = GoRouter(
|
||||
builder: (context, state) => const AccountScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/posts/moments/do/editor',
|
||||
path: '/posts/publish/moments',
|
||||
name: 'posts.moments.editor',
|
||||
builder: (context, state) =>
|
||||
MomentEditorScreen(editing: state.extra as Post?),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/posts/publish/comments',
|
||||
name: 'posts.comments.editor',
|
||||
builder: (context, state) {
|
||||
final args = state.extra as CommentPostArguments;
|
||||
return CommentEditorScreen(
|
||||
editing: args.editing, related: args.related);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: '/posts/:dataset/:alias',
|
||||
name: 'posts.screen',
|
||||
|
170
lib/screens/posts/comment_editor.dart
Normal file
170
lib/screens/posts/comment_editor.dart
Normal file
@ -0,0 +1,170 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:solian/models/post.dart';
|
||||
import 'package:solian/providers/auth.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:solian/utils/service_url.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:solian/widgets/indent_wrapper.dart';
|
||||
import 'package:solian/widgets/posts/attachment_editor.dart';
|
||||
|
||||
class CommentPostArguments {
|
||||
final Post related;
|
||||
final Post? editing;
|
||||
|
||||
CommentPostArguments({required this.related, this.editing});
|
||||
}
|
||||
|
||||
class CommentEditorScreen extends StatefulWidget {
|
||||
final Post related;
|
||||
final Post? editing;
|
||||
|
||||
const CommentEditorScreen({super.key, required this.related, this.editing});
|
||||
|
||||
@override
|
||||
State<CommentEditorScreen> createState() => _CommentEditorScreenState();
|
||||
}
|
||||
|
||||
class _CommentEditorScreenState extends State<CommentEditorScreen> {
|
||||
final _textController = TextEditingController();
|
||||
|
||||
String? _alias;
|
||||
bool _isSubmitting = false;
|
||||
|
||||
List<Attachment> _attachments = List.empty(growable: true);
|
||||
|
||||
void viewAttachments(BuildContext context) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => AttachmentEditor(
|
||||
current: _attachments,
|
||||
onUpdate: (value) => _attachments = value,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> applyPost(BuildContext context) async {
|
||||
final auth = context.read<AuthProvider>();
|
||||
if (!await auth.isAuthorized()) return;
|
||||
|
||||
final relatedDataset = '${widget.related.modelType}s';
|
||||
final uri = widget.editing == null
|
||||
? getRequestUri('interactive', '/api/p/$relatedDataset/${widget.related.alias}/comments')
|
||||
: getRequestUri('interactive', '/api/p/comments/${widget.editing!.id}');
|
||||
|
||||
final req = Request(widget.editing == null ? "POST" : "PUT", uri);
|
||||
req.headers['Content-Type'] = 'application/json';
|
||||
req.body = jsonEncode(<String, dynamic>{
|
||||
'alias': _alias,
|
||||
'content': _textController.value.text,
|
||||
'attachments': _attachments,
|
||||
});
|
||||
|
||||
setState(() => _isSubmitting = true);
|
||||
var res = await Response.fromStream(await auth.client!.send(req));
|
||||
if (res.statusCode != 200) {
|
||||
var message = utf8.decode(res.bodyBytes);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Something went wrong... $message")),
|
||||
);
|
||||
} else {
|
||||
if (router.canPop()) {
|
||||
router.pop(true);
|
||||
}
|
||||
}
|
||||
setState(() => _isSubmitting = false);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.editing != null) {
|
||||
_alias = widget.editing!.alias;
|
||||
_textController.text = widget.editing!.content;
|
||||
_attachments = widget.editing!.attachments ?? List.empty(growable: true);
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final auth = context.read<AuthProvider>();
|
||||
|
||||
return IndentWrapper(
|
||||
hideDrawer: true,
|
||||
title: AppLocalizations.of(context)!.newComment,
|
||||
appBarActions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: !_isSubmitting ? () => applyPost(context) : null,
|
||||
child: Text(AppLocalizations.of(context)!.postVerb.toUpperCase()),
|
||||
),
|
||||
],
|
||||
child: Center(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
child: Column(
|
||||
children: [
|
||||
_isSubmitting ? const LinearProgressIndicator() : Container(),
|
||||
FutureBuilder(
|
||||
future: auth.getProfiles(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
var userinfo = snapshot.data;
|
||||
return ListTile(
|
||||
title: Text(userinfo["nick"]),
|
||||
subtitle: Text(
|
||||
AppLocalizations.of(context)!.postIdentityNotify,
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: NetworkImage(userinfo["picture"]),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
),
|
||||
const Divider(thickness: 0.3),
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
autofocus: true,
|
||||
autocorrect: true,
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: _textController,
|
||||
decoration: InputDecoration.collapsed(
|
||||
hintText:
|
||||
AppLocalizations.of(context)!.postContentPlaceholder,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(width: 0.3, color: Color(0xffdedede)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(shape: const CircleBorder()),
|
||||
child: const Icon(Icons.camera_alt),
|
||||
onPressed: () => viewAttachments(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import 'package:solian/models/post.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:solian/providers/auth.dart';
|
||||
import 'package:solian/router.dart';
|
||||
import 'package:solian/screens/posts/comment_editor.dart';
|
||||
import 'package:solian/utils/service_url.dart';
|
||||
import 'package:solian/widgets/posts/item.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
@ -117,14 +118,16 @@ class CommentListHeader extends StatelessWidget {
|
||||
future: auth.isAuthorized(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData && snapshot.data == true) {
|
||||
return TextButton.icon(
|
||||
icon: const Icon(Icons.add_comment_outlined),
|
||||
label: const Text("LEAVE COMMENT"),
|
||||
return TextButton(
|
||||
onPressed: () async {
|
||||
final did =
|
||||
await router.push("posts.comments.new", extra: related);
|
||||
final did = await router.pushNamed(
|
||||
"posts.comments.editor",
|
||||
extra: CommentPostArguments(related: related),
|
||||
);
|
||||
if (did == true) paging.refresh();
|
||||
},
|
||||
style: TextButton.styleFrom(shape: const CircleBorder()),
|
||||
child: const Icon(Icons.add_comment_outlined),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
|
@ -130,6 +130,7 @@ class AttachmentList extends StatelessWidget {
|
||||
options: CarouselOptions(
|
||||
aspectRatio: 16 / 9,
|
||||
viewportFraction: 1,
|
||||
showIndicator: false,
|
||||
),
|
||||
items: items.map((item) {
|
||||
renderProgress++;
|
||||
|
@ -145,10 +145,13 @@ class _PostItemState extends State<PostItem> {
|
||||
child: Divider(thickness: 0.3),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: renderContent(),
|
||||
),
|
||||
renderAttachments()
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: renderAttachments(),
|
||||
)
|
||||
],
|
||||
),
|
||||
onLongPress: () {
|
||||
|
Reference in New Issue
Block a user