Implement new thread creation

This commit is contained in:
ChronosX88 2022-05-02 00:49:03 +03:00
parent cf15aae5ac
commit 1bcc2613b4
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
5 changed files with 193 additions and 63 deletions

View File

@ -0,0 +1,112 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wind/thread_list_model.dart';
import 'package:wind/thread_model.dart';
class CreateThreadScreen extends StatefulWidget {
@override
State<StatefulWidget> createState() => _CreateThreadScreenState();
}
class _CreateThreadScreenState extends State<CreateThreadScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Создать новый тред")),
body: Center(
child: Container(
width: 640,
child: Consumer<ThreadModel>(
builder: (context, value, child) => CreateThreadForm(model: value),
),
)));
}
}
class CreateThreadForm extends StatefulWidget {
CreateThreadForm({Key? key, required this.model}) : super(key: key);
ThreadModel model;
@override
CreateThreadFormState createState() {
return CreateThreadFormState(model);
}
}
// Define a corresponding State class.
// This class holds data related to the form.
class CreateThreadFormState extends State<CreateThreadForm> {
CreateThreadFormState(this.model);
ThreadModel model;
final _formKey = GlobalKey<FormState>();
String _subject = "";
String _text = "";
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: TextFormField(
onSaved: ((newValue) {
_subject = newValue!;
}),
decoration: InputDecoration(
border: OutlineInputBorder(), labelText: "Название поста"),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Пожалуйста, введите название';
}
return null;
}),
),
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: TextFormField(
onSaved: (newValue) {
_text = newValue!;
},
minLines: 5,
keyboardType: TextInputType.multiline,
maxLines: 35,
decoration: InputDecoration(
border: OutlineInputBorder(), labelText: "Текст поста"),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Пожалуйста, введите текст';
}
return null;
},
)),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
model.createThread(_subject, _text).then((value) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Тред создан!')),
);
Provider.of<ThreadListModel>(context, listen: false)
.update();
Navigator.pop(context);
}, onError: (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'При создании треда произошла ошибка: ${error.toString()}')),
);
});
}
},
child: Text("Создать"))
],
),
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:wind/create_thread_screen.dart';
import 'package:wind/newsgroup_list_view.dart'; import 'package:wind/newsgroup_list_view.dart';
import 'package:wind/nntp_client.dart'; import 'package:wind/nntp_client.dart';
import 'package:wind/thread_list_view.dart'; import 'package:wind/thread_list_view.dart';
@ -47,6 +48,9 @@ class MyApp extends StatelessWidget {
threadNumber: threadNumber:
int.parse(uriData.queryParametersAll['num']!.first)); int.parse(uriData.queryParametersAll['num']!.first));
break; break;
case '/thread/create':
pageView = CreateThreadScreen();
break;
default: default:
pageView = MyHomePage(title: 'Wind'); pageView = MyHomePage(title: 'Wind');
break; break;
@ -82,31 +86,35 @@ class _MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done return Consumer<ThreadListModel>(
// by the _incrementCounter method above. builder: (context, value, child) => Scaffold(
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar( appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title), title: Text(widget.title),
actions: [ actions: value.currentGroup != ""
? [
TextButton.icon(
onPressed: () =>
Navigator.pushNamed(context, "/thread/create"),
icon: Icon(Icons.add),
label: Text("Создать тред"),
style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.onPrimary),
),
TextButton.icon( TextButton.icon(
onPressed: () { onPressed: () {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Обновление списка тредов...')), content: Text('Обновление списка тредов...')),
); );
Provider.of<ThreadListModel>(context, listen: false).update(); Provider.of<ThreadListModel>(context, listen: false)
.update();
}, },
label: const Text("Обновить"), label: const Text("Обновить"),
style: TextButton.styleFrom( style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.onPrimary), primary: Theme.of(context).colorScheme.onPrimary),
icon: Icon(Icons.sync)) icon: Icon(Icons.sync))
], ]
: [],
), ),
body: Center( body: Center(
child: Container( child: Container(
@ -140,6 +148,7 @@ class _MyHomePageState extends State<MyHomePage> {
], ],
), ),
), ),
)); )),
);
} }
} }

View File

@ -18,7 +18,7 @@ class MessageItemView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: this.isOpPost padding: this.isOpPost
? EdgeInsets.only(bottom: 10, left: 10, right: 10, top: 16) ? EdgeInsets.only(bottom: 10, left: 10, right: 10, top: 16)
: isLast : isLast
? EdgeInsets.only(left: 10, right: 10, bottom: 16) ? EdgeInsets.only(left: 10, right: 10, bottom: 16)

View File

@ -40,7 +40,7 @@ class ThreadListModel extends ChangeNotifier {
_curItems.add(ThreadItem( _curItems.add(ThreadItem(
msg.getHeaderValue("Message-Id")!, msg.getHeaderValue("Message-Id")!,
number, number,
msg.getHeaderValue("Subject")!, msg.decodeSubject()!,
msg.getHeaderValue("From")!, msg.getHeaderValue("From")!,
msg.getHeaderValue("Date")!, msg.getHeaderValue("Date")!,
msg.decodeTextPlainPart()!)); msg.decodeTextPlainPart()!));

View File

@ -13,7 +13,7 @@ class ThreadModel extends ChangeNotifier {
var mi = MessageItem( var mi = MessageItem(
msg.getHeaderValue("Message-Id")!, msg.getHeaderValue("Message-Id")!,
number, number,
msg.getHeaderValue("Subject")!, msg.decodeSubject()!,
msg.getHeaderValue("From")!, msg.getHeaderValue("From")!,
msg.getHeaderValue("Date")!, msg.getHeaderValue("Date")!,
msg.decodeTextPlainPart()!); msg.decodeTextPlainPart()!);
@ -37,7 +37,7 @@ class ThreadModel extends ChangeNotifier {
null, null,
message.getHeaderValue("From")!, message.getHeaderValue("From")!,
message.getHeaderValue("Date")!, message.getHeaderValue("Date")!,
message.decodeTextPlainPart()!)); message.decodeTextPlainPart()!.trim()));
}); });
return items; return items;
@ -45,7 +45,7 @@ class ThreadModel extends ChangeNotifier {
Future<int> postMessage(MimeMessage opPost, String text) async { Future<int> postMessage(MimeMessage opPost, String text) async {
var msg = MessageBuilder.buildSimpleTextMessage( var msg = MessageBuilder.buildSimpleTextMessage(
MailAddress.empty(), [], text, MailAddress.empty(), [], text.trim(),
subject: "Re: " + opPost.decodeSubject()!); subject: "Re: " + opPost.decodeSubject()!);
msg.setHeader("From", "anonymous"); msg.setHeader("From", "anonymous");
msg.addHeader("In-Reply-To", opPost.getHeaderValue("Message-Id")); msg.addHeader("In-Reply-To", opPost.getHeaderValue("Message-Id"));
@ -54,6 +54,15 @@ class ThreadModel extends ChangeNotifier {
return await client!.postArticle(msg); return await client!.postArticle(msg);
} }
Future<int> createThread(String subject, String text) async {
var msg = MessageBuilder.buildSimpleTextMessage(
MailAddress.empty(), [], text.trim(),
subject: subject);
msg.setHeader("From", "anonymous");
msg.addHeader("Newsgroups", client!.currentGroup!);
return await client!.postArticle(msg);
}
void update() { void update() {
notifyListeners(); notifyListeners();
} }