mirror of
https://github.com/ChronosX88/wind.git
synced 2024-11-09 17:21:00 +00:00
Implement thread loading
This commit is contained in:
parent
fabb0767b5
commit
839f91d735
@ -3,6 +3,7 @@ import 'package:provider/provider.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';
|
||||||
|
import 'package:wind/thread_model.dart';
|
||||||
import 'package:wind/thread_screen.dart';
|
import 'package:wind/thread_screen.dart';
|
||||||
|
|
||||||
import 'thread_list_model.dart';
|
import 'thread_list_model.dart';
|
||||||
@ -16,6 +17,12 @@ void main() {
|
|||||||
model!.client = client;
|
model!.client = client;
|
||||||
return model;
|
return model;
|
||||||
}),
|
}),
|
||||||
|
ChangeNotifierProxyProvider<NNTPClient, ThreadModel>(
|
||||||
|
create: (context) => ThreadModel(),
|
||||||
|
update: (context, client, model) {
|
||||||
|
model!.client = client;
|
||||||
|
return model;
|
||||||
|
}),
|
||||||
], child: MyApp()));
|
], child: MyApp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
80
lib/message_item_view.dart
Normal file
80
lib/message_item_view.dart
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MessageItemView extends StatelessWidget {
|
||||||
|
const MessageItemView({Key? key, required this.item, required this.isOpPost})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final MessageItem item;
|
||||||
|
final bool isOpPost;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: this.isOpPost ? EdgeInsets.only(bottom: 10) : EdgeInsets.all(0),
|
||||||
|
child: Card(
|
||||||
|
elevation: 5,
|
||||||
|
child: InkWell(
|
||||||
|
splashColor: Colors.indigo.withAlpha(30),
|
||||||
|
onTap: () => {},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
isOpPost
|
||||||
|
? Container(
|
||||||
|
child: Text(
|
||||||
|
item.subject!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold, fontSize: 21),
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.only(top: 16, left: 16, right: 16),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Container(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.author,
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Colors.blue,
|
||||||
|
fontSize: 15),
|
||||||
|
),
|
||||||
|
SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
item.date,
|
||||||
|
style: TextStyle(fontSize: 15),
|
||||||
|
),
|
||||||
|
SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
"#${item.number}",
|
||||||
|
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
margin: isOpPost
|
||||||
|
? EdgeInsets.only(
|
||||||
|
top: 5, bottom: 2, left: 16, right: 16)
|
||||||
|
: EdgeInsets.only(top: 16, left: 16),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Text(item.body, style: TextStyle(fontSize: 17)),
|
||||||
|
margin: EdgeInsets.all(16),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageItem {
|
||||||
|
final String id;
|
||||||
|
final int number;
|
||||||
|
final String? subject;
|
||||||
|
final String author;
|
||||||
|
final String date;
|
||||||
|
final String body;
|
||||||
|
|
||||||
|
MessageItem(
|
||||||
|
this.id, this.number, this.subject, this.author, this.date, this.body);
|
||||||
|
}
|
@ -126,6 +126,31 @@ class NNTPClient {
|
|||||||
|
|
||||||
return threads;
|
return threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Tuple2<int, MimeMessage>>> getThread(
|
||||||
|
int threadNumber) async {
|
||||||
|
if (currentGroup == null) throw new ArgumentError("current group is null");
|
||||||
|
|
||||||
|
List<Tuple2<int, MimeMessage>> threads = [];
|
||||||
|
|
||||||
|
var newThreadList = await _sendCommand(
|
||||||
|
"THREAD", [threadNumber.toString()]);
|
||||||
|
|
||||||
|
newThreadList.lines.removeAt(0);
|
||||||
|
newThreadList.lines.removeLast(); // remove dot
|
||||||
|
|
||||||
|
await Future.forEach<String>(newThreadList.lines, (element) async {
|
||||||
|
await _sendCommand("ARTICLE", [element]).then((response) {
|
||||||
|
response.lines.removeAt(0);
|
||||||
|
response.lines.removeLast();
|
||||||
|
var rawMsg = response.lines.join("\r\n");
|
||||||
|
threads
|
||||||
|
.add(Tuple2(int.parse(element), MimeMessage.parseFromText(rawMsg)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NNTPCommand {
|
class _NNTPCommand {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:wind/thread_list_model.dart';
|
import 'package:wind/thread_list_model.dart';
|
||||||
|
import 'package:wind/thread_screen.dart';
|
||||||
|
|
||||||
class ThreadListView extends StatefulWidget {
|
class ThreadListView extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -96,7 +97,8 @@ class ThreadListItemView extends StatelessWidget {
|
|||||||
elevation: 5,
|
elevation: 5,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
splashColor: Colors.indigo.withAlpha(30),
|
splashColor: Colors.indigo.withAlpha(30),
|
||||||
onTap: () => Navigator.pushNamed(context, "/thread"),
|
onTap: () => Navigator.pushNamed(context, "/thread",
|
||||||
|
arguments: ThreadScreenArguments(item)),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -123,6 +125,11 @@ class ThreadListItemView extends StatelessWidget {
|
|||||||
item.date,
|
item.date,
|
||||||
style: TextStyle(fontSize: 15),
|
style: TextStyle(fontSize: 15),
|
||||||
),
|
),
|
||||||
|
SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
"#${item.number}",
|
||||||
|
style: TextStyle(fontSize: 15, color: Colors.grey),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
margin: EdgeInsets.only(top: 5, bottom: 2, left: 16, right: 16),
|
margin: EdgeInsets.only(top: 5, bottom: 2, left: 16, right: 16),
|
||||||
|
29
lib/thread_model.dart
Normal file
29
lib/thread_model.dart
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:wind/message_item_view.dart';
|
||||||
|
import 'package:wind/nntp_client.dart';
|
||||||
|
|
||||||
|
class ThreadModel extends ChangeNotifier {
|
||||||
|
NNTPClient? client;
|
||||||
|
|
||||||
|
Future<List<MessageItem>> getThread(int threadNumber) async {
|
||||||
|
if (client!.currentGroup == "") return [];
|
||||||
|
|
||||||
|
List<MessageItem> items = [];
|
||||||
|
|
||||||
|
var thread = await client!.getThread(threadNumber);
|
||||||
|
|
||||||
|
thread.forEach((pair) {
|
||||||
|
var messageNum = pair.item1;
|
||||||
|
var message = pair.item2;
|
||||||
|
items.add(MessageItem(
|
||||||
|
message.getHeaderValue("Message-Id")!,
|
||||||
|
messageNum,
|
||||||
|
null,
|
||||||
|
message.getHeaderValue("From")!,
|
||||||
|
message.getHeaderValue("Date")!,
|
||||||
|
message.decodeTextPlainPart()!));
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:wind/message_item_view.dart';
|
||||||
|
import 'package:wind/nntp_client.dart';
|
||||||
|
import 'package:wind/thread_list_view.dart';
|
||||||
|
import 'package:wind/thread_model.dart';
|
||||||
|
|
||||||
|
class ThreadScreenArguments {
|
||||||
|
final ThreadItem item;
|
||||||
|
|
||||||
|
ThreadScreenArguments(this.item);
|
||||||
|
}
|
||||||
|
|
||||||
class ThreadScreen extends StatefulWidget {
|
class ThreadScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -6,12 +17,53 @@ class ThreadScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ThreadScreenState extends State<ThreadScreen> {
|
class ThreadScreenState extends State<ThreadScreen> {
|
||||||
|
late NNTPClient client;
|
||||||
|
late int threadNumber;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var args =
|
||||||
|
ModalRoute.of(context)!.settings.arguments as ThreadScreenArguments;
|
||||||
|
client = context.read<NNTPClient>();
|
||||||
|
threadNumber = args.item.number;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("Thread"),
|
title: Text("Thread #${args.item.number}"),
|
||||||
),
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Container(
|
||||||
|
width: 640,
|
||||||
|
child: FutureBuilder<List<MessageItem>>(
|
||||||
|
future: _fetch(context),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
List<MessageItem> data = List.from(snapshot.data!);
|
||||||
|
data.insert(
|
||||||
|
0,
|
||||||
|
MessageItem(args.item.id, args.item.number, args.item.subject,
|
||||||
|
args.item.author, args.item.date, args.item.body));
|
||||||
|
return _listView(data);
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
return Text("${snapshot.error}");
|
||||||
|
}
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<MessageItem>> _fetch(BuildContext context) async {
|
||||||
|
var model = context.read<ThreadModel>();
|
||||||
|
return await model.getThread(threadNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _listView(List<MessageItem> data) {
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: data.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return MessageItemView(item: data[index], isOpPost: index == 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user