diff --git a/android/app/build.gradle b/android/app/build.gradle
index f1097ef..78dcc3a 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -44,8 +44,11 @@ android {
applicationId "com.aoihosizora.manhuagui"
minSdkVersion 16
targetSdkVersion 29
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
+ // versionCode flutterVersionCode.toInteger()
+ // versionName flutterVersionName
+
+ versionCode 3
+ versionName "1.0.2"
}
signingConfigs {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 01249d8..a634996 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,7 +6,7 @@
diff --git a/lib/model/comment.dart b/lib/model/comment.dart
index bc77c5b..2b7aeb5 100644
--- a/lib/model/comment.dart
+++ b/lib/model/comment.dart
@@ -22,6 +22,10 @@ class Comment {
Map toJson() => _$CommentToJson(this);
static const fields = ['cid', 'uid', 'username', 'avatar', 'gender', 'content', 'like_count', 'reply_count', 'comment_time', 'reply_timeline'];
+
+ RepliedComment toRepliedComment() {
+ return RepliedComment(cid: cid, uid: uid, username: username, avatar: avatar, gender: gender, content: content, likeCount: likeCount, replyCount: replyCount, commentTime: commentTime);
+ }
}
@JsonSerializable(fieldRename: FieldRename.snake)
diff --git a/lib/model/manga.dart b/lib/model/manga.dart
index 51becd7..6f05a4d 100644
--- a/lib/model/manga.dart
+++ b/lib/model/manga.dart
@@ -17,6 +17,7 @@ class Manga {
List genres;
List authors;
String alias;
+ String aliasTitle;
bool finished;
String newestChapter;
String newestDate;
@@ -30,34 +31,13 @@ class Manga {
bool copyright;
List chapterGroups;
- Manga(
- {this.mid,
- this.title,
- this.cover,
- this.url,
- this.publishYear,
- this.mangaZone,
- this.genres,
- this.authors,
- this.alias,
- this.finished,
- this.newestChapter,
- this.newestDate,
- this.briefIntroduction,
- this.introduction,
- this.mangaRank,
- this.averageScore,
- this.scoreCount,
- this.perScores,
- this.banned,
- this.copyright,
- this.chapterGroups});
+ Manga({this.mid, this.title, this.cover, this.url, this.publishYear, this.mangaZone, this.genres, this.authors, this.alias, this.aliasTitle, this.finished, this.newestChapter, this.newestDate, this.briefIntroduction, this.introduction, this.mangaRank, this.averageScore, this.scoreCount, this.perScores, this.banned, this.copyright, this.chapterGroups});
factory Manga.fromJson(Map json) => _$MangaFromJson(json);
Map toJson() => _$MangaToJson(this);
- static const fields = ['mid', 'title', 'cover', 'url', 'publish_year', 'manga_zone', 'genres', 'authors', 'alias', 'finished', 'newest_chapter', 'newest_date', 'brief_introduction', 'introduction', 'manga_rank', 'average_score', 'score_count', 'per_scores', 'banned', 'copyright', 'chapter_groups'];
+ static const fields = ['mid', 'title', 'cover', 'url', 'publish_year', 'manga_zone', 'genres', 'authors', 'alias', 'alias_title', 'finished', 'newest_chapter', 'newest_date', 'brief_introduction', 'introduction', 'manga_rank', 'average_score', 'score_count', 'per_scores', 'banned', 'copyright', 'chapter_groups'];
}
@JsonSerializable(fieldRename: FieldRename.snake)
diff --git a/lib/page/comment.dart b/lib/page/comment.dart
new file mode 100644
index 0000000..6c0097e
--- /dev/null
+++ b/lib/page/comment.dart
@@ -0,0 +1,210 @@
+import 'package:flutter/material.dart';
+import 'package:manhuagui_flutter/model/comment.dart';
+import 'package:manhuagui_flutter/page/view/network_image.dart';
+import 'package:manhuagui_flutter/service/natives/clipboard.dart';
+
+/// 评论详情页
+class CommentPage extends StatefulWidget {
+ const CommentPage({
+ Key key,
+ @required this.comment,
+ }) : assert(comment != null),
+ super(key: key);
+
+ final Comment comment;
+
+ @override
+ _CommentPageState createState() => _CommentPageState();
+}
+
+class _CommentPageState extends State {
+ Widget _buildLine({@required RepliedComment comment, @required int idx}) {
+ assert(comment != null);
+ assert(idx != null);
+ return Stack(
+ children: [
+ Container(
+ color: Colors.white,
+ padding: EdgeInsets.only(top: 15, bottom: 15, left: 15, right: 15),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ClipOval(
+ child: NetworkImageView(
+ url: comment.avatar,
+ height: 40,
+ width: 40,
+ fit: BoxFit.cover,
+ ),
+ ),
+ SizedBox(width: 15),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // ****************************************************************
+ // 第一行
+ // ****************************************************************
+ Container(
+ width: MediaQuery.of(context).size.width - 3 * 15 - 40, // | ▢▢ ▢▢▢▢▢ |
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ // ****************************************************************
+ // 用户名 性别
+ // ****************************************************************
+ Expanded(
+ child: Row(
+ children: [
+ Flexible(
+ child: Text(
+ comment.username == '-' ? '匿名用户' : comment.username,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(context).textTheme.subtitle1,
+ ),
+ ),
+ SizedBox(width: 8),
+ Container(
+ decoration: BoxDecoration(
+ color: comment.gender == 1 ? Colors.blue[300] : Colors.red[400],
+ borderRadius: BorderRadius.all(Radius.circular(3)),
+ ),
+ height: 18,
+ width: 18,
+ child: Center(
+ child: Text(
+ widget.comment.gender == 1 ? '♂' : '♀',
+ style: TextStyle(fontSize: 14, color: Colors.white),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ // ****************************************************************
+ // 楼层
+ // ****************************************************************
+ Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).primaryColor,
+ borderRadius: BorderRadius.all(Radius.circular(3)),
+ ),
+ height: 18,
+ width: 26,
+ child: Center(
+ child: Text(
+ '#$idx',
+ style: TextStyle(
+ color: Colors.white,
+ fontSize: 14,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ SizedBox(height: 15),
+ // ****************************************************************
+ // 评论内容
+ // ****************************************************************
+ Container(
+ width: MediaQuery.of(context).size.width - 3 * 15 - 40,
+ child: Text(
+ comment.content,
+ style: Theme.of(context).textTheme.subtitle1,
+ ),
+ ),
+ SizedBox(height: 15),
+ // ****************************************************************
+ // 评论数据
+ // ****************************************************************
+ Container(
+ width: MediaQuery.of(context).size.width - 3 * 15 - 40,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ comment.commentTime,
+ style: TextStyle(color: Colors.grey),
+ ),
+ Row(
+ children: [
+ Icon(
+ Icons.thumb_up,
+ color: Colors.grey[400],
+ size: 16,
+ ),
+ SizedBox(width: 4),
+ Text(comment.likeCount.toString()),
+ SizedBox(width: 10),
+ Icon(
+ Icons.chat_bubble,
+ color: Colors.grey[400],
+ size: 16,
+ ),
+ SizedBox(width: 4),
+ Text(comment.replyCount.toString()),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ Positioned.fill(
+ child: Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: () => copyText(comment.content),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ centerTitle: true,
+ toolbarHeight: 45,
+ title: Text('评论详情'),
+ ),
+ body: Container(
+ width: MediaQuery.of(context).size.width,
+ child: ListView(
+ children: [
+ _buildLine(
+ comment: widget.comment.toRepliedComment(),
+ idx: widget.comment.replyTimeline.length + 1,
+ ),
+ Container(height: 12),
+ if (widget.comment.replyTimeline.length > 0)
+ for (var i = 0; i < widget.comment.replyTimeline.length - 1; i++) ...[
+ _buildLine(
+ comment: widget.comment.replyTimeline[i],
+ idx: i + 1,
+ ),
+ Container(
+ color: Colors.white,
+ padding: EdgeInsets.only(left: 2.0 * 15 + 40),
+ width: MediaQuery.of(context).size.width - 3 * 15 - 40,
+ child: Divider(height: 1, thickness: 1),
+ ),
+ ],
+ if (widget.comment.replyTimeline.length > 0)
+ _buildLine(
+ comment: widget.comment.replyTimeline.last,
+ idx: widget.comment.replyTimeline.length,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/page/manga.dart b/lib/page/manga.dart
index 64179e9..70c1b59 100644
--- a/lib/page/manga.dart
+++ b/lib/page/manga.dart
@@ -126,6 +126,16 @@ class _MangaPageState extends State {
chapterPage: 0,
),
).catchError((_) {});
+ } else {
+ updateHistory(
+ username: AuthState.instance.username,
+ history: MangaHistory(
+ mangaId: _data.mid,
+ mangaTitle: _data.title ?? '?',
+ mangaCover: _data.cover ?? '?',
+ mangaUrl: _data.url ?? '',
+ ),
+ );
}
}).catchError((e) {
_data = null;
diff --git a/lib/page/view/comment_line.dart b/lib/page/view/comment_line.dart
index 84f268a..9f58a23 100644
--- a/lib/page/view/comment_line.dart
+++ b/lib/page/view/comment_line.dart
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
-import 'package:fluttertoast/fluttertoast.dart';
import 'package:manhuagui_flutter/model/comment.dart';
+import 'package:manhuagui_flutter/page/comment.dart';
import 'package:manhuagui_flutter/page/view/network_image.dart';
/// View for [Comment].
@@ -29,6 +29,9 @@ class _CommentLineViewState extends State {
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+ // ****************************************************************
+ // 头像
+ // ****************************************************************
ClipOval(
child: NetworkImageView(
url: widget.comment.avatar,
@@ -41,11 +44,17 @@ class _CommentLineViewState extends State {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+ // ****************************************************************
+ // 第一行
+ // ****************************************************************
Container(
width: MediaQuery.of(context).size.width - 3 * 12 - 32, // | ▢▢ ▢▢▢▢▢ |
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
+ // ****************************************************************
+ // 用户名 性别
+ // ****************************************************************
Expanded(
child: Row(
children: [
@@ -75,6 +84,9 @@ class _CommentLineViewState extends State {
],
),
),
+ // ****************************************************************
+ // 楼层
+ // ****************************************************************
if (widget.comment.replyTimeline.length > 0)
Container(
margin: EdgeInsets.only(right: 8),
@@ -98,6 +110,9 @@ class _CommentLineViewState extends State {
),
),
SizedBox(height: 8),
+ // ****************************************************************
+ // 评论内容
+ // ****************************************************************
Container(
width: MediaQuery.of(context).size.width - 3 * 12 - 32,
child: Text(
@@ -106,6 +121,9 @@ class _CommentLineViewState extends State {
),
),
if (widget.comment.replyTimeline.length > 0) SizedBox(height: 10),
+ // ****************************************************************
+ // 楼层
+ // ****************************************************************
if (widget.comment.replyTimeline.length > 0)
Container(
width: MediaQuery.of(context).size.width - 3 * 12 - 32,
@@ -117,6 +135,9 @@ class _CommentLineViewState extends State {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
+ // ****************************************************************
+ // 每一楼
+ // ****************************************************************
for (var line in widget.comment.replyTimeline.sublist(0, widget.comment.replyTimeline.length <= 3 ? widget.comment.replyTimeline.length : 3))
Padding(
padding: EdgeInsets.only(bottom: 4),
@@ -169,6 +190,9 @@ class _CommentLineViewState extends State {
),
),
SizedBox(height: 10),
+ // ****************************************************************
+ // 评论信息
+ // ****************************************************************
Container(
width: MediaQuery.of(context).size.width - 3 * 12 - 32,
child: Row(
@@ -205,11 +229,20 @@ class _CommentLineViewState extends State {
],
),
),
+ // ****************************************************************
+ // 点击效果
+ // ****************************************************************
Positioned.fill(
child: Material(
color: Colors.transparent,
child: InkWell(
- onTap: () => Fluttertoast.showToast(msg: 'TODO'),
+ onTap: () => Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (c) => CommentPage(
+ comment: widget.comment,
+ ),
+ ),
+ ),
),
),
),
diff --git a/lib/service/database/history.dart b/lib/service/database/history.dart
index a777e0b..a81bdeb 100644
--- a/lib/service/database/history.dart
+++ b/lib/service/database/history.dart
@@ -137,6 +137,20 @@ Future addHistory({@required String username, @required MangaHistory histo
return rows >= 1;
}
+Future updateHistory({@required String username, @required MangaHistory history}) async {
+ username ??= '';
+ assert(history != null && history.mangaId != null);
+
+ var db = await DBProvider.instance.getDB();
+ var rows = await db.rawUpdate(
+ '''UPDATE $_tblHistory
+ SET $_colMangaTitle = ?, $_colMangaCover = ?, $_colMangaUrl = ?
+ WHERE $_colUsername = ? AND $_colMangaId = ?''',
+ [history.mangaTitle, history.mangaCover, history.mangaUrl, username, history.mangaId],
+ ).catchError((_) {});
+ return rows >= 1;
+}
+
Future deleteHistory({@required String username, @required int mid}) async {
username ??= '';
assert(mid != null);
diff --git a/pubspec.yaml b/pubspec.yaml
index ffb7a5b..315becf 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,7 +2,7 @@ name: manhuagui_flutter
description: An unofficial application for manhuagui written in flutter
publish_to: 'none'
-version: 1.0.0+1
+version: 1.0.2
environment:
sdk: ">=2.7.0 <3.0.0"