Skip to content

Commit

Permalink
Add reading history, show last time you read chapter in detail page o…
Browse files Browse the repository at this point in the history
…f book you read
  • Loading branch information
CGQAQ committed Oct 29, 2020
1 parent 7d767a4 commit eeb5cc6
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 63 deletions.
20 changes: 17 additions & 3 deletions lib/api/content.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
import 'package:http/http.dart' as Http;
import 'package:html/parser.dart' show parse;
import 'package:http/http.dart' as Http;

class FictionContent {
final String fictionTitle;
final String chapterTitle;
final String fictionID;
final String chapterID;
final List<String> lines;
FictionContent(this.lines);
FictionContent(this.fictionTitle, this.chapterTitle, this.fictionID,
this.chapterID, this.lines);
static Future<FictionContent> create(String novelID, String chapterID) async {
try {
// https://cn.ttkan.co/novel/user/page_direct?novel_id=jiansong-danshuiluyu&page=760
final url =
"https://cn.ttkan.co/novel/user/page_direct?novel_id=$novelID&page=$chapterID";
final content = await Http.get(url);
final dom = parse(content.body);
final fictionTitle = dom
.querySelectorAll(
"#__layout > div > div > div.frame_body > div.breadcrumb_nav.target > div > a")[2]
.text;
final chapterTitle = dom
.querySelector(
"#__layout > div > div > div.frame_body > div.title > h1")
.text;
final lines = dom
.querySelector(".content")
.querySelectorAll("p")
.map((e) => e.text)
.toList();
return FictionContent(lines);
return FictionContent(
fictionTitle, chapterTitle, novelID, chapterID, lines);
} catch (_) {
rethrow;
}
Expand Down
124 changes: 117 additions & 7 deletions lib/database.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import 'package:fiction_reader/api/content.dart';
import 'package:fiction_reader/api/search.dart';
import 'package:sqflite/sqflite.dart' as Sqflite;

class Database {
final _bookmarksTable = "fiction_bookmark";
final _fictionCacheTable = "fiction_cache";
final _historyTable = "fiction_history";
final Sqflite.Database _database;

Database(this._database);
Expand All @@ -10,24 +14,46 @@ class Database {
final database = await Sqflite.openDatabase("data.db");
final result = Database(database);
result._createBookmarkTable();
result._createFictionCacheTable();
result._createFictionHistoryTable();
return result;
}

void _createBookmarkTable() {
if (_database.isOpen) {
_database.execute("""
CREATE TABLE IF NOT EXISTS bookmarks(
_database.execute('''
CREATE TABLE IF NOT EXISTS $_bookmarksTable(
title TEXT NOT NULL,
author TEXT NOT NULL,
description TEXT NOT NULL ,
book_id TEXT NOT NULL PRIMARY KEY
)""");
)''');
}
}

void _createFictionCacheTable() {
if (_database.isOpen) _database.execute("""
CREATE TABLE IF NOT EXISTS $_fictionCacheTable(
fiction_id TEXT NOT NULL PRIMARY KEY,
chapter_id TEXT NOT NULL,
fiction_title TEXT NOT NULL,
chapter_title TEXT NOT NULL,
content TEXT
)
""");
}

void _createFictionHistoryTable() {
if (_database.isOpen) _database.execute("""
CREATE TABLE IF NOT EXISTS $_historyTable (
fiction_id TEXT NOT NULL PRIMARY KEY,
last_read TEXT
)""");
}

Future<int> addBookmark(Novel novel) {
return _database.insert(
"bookmarks",
_bookmarksTable,
{
"title": novel.title,
"author": novel.author,
Expand All @@ -38,11 +64,95 @@ CREATE TABLE IF NOT EXISTS bookmarks(
}

Future<void> removeBookmark(Novel novel) {
return _database
.execute("delete from bookmarks where book_id=?", [novel.novelID]);
return _database.execute(
"delete from $_bookmarksTable where book_id=?", [novel.novelID]);
}

Future<List<Map<String, dynamic>>> listBookmarks() async {
return _database.query("bookmarks");
return _database.query(_bookmarksTable);
}

Future<bool> cacheExists(String fictionId, String chapterId) async {
return (await _database.query(
_fictionCacheTable,
where: "fiction_id=? and chapter_id=?",
whereArgs: [fictionId, chapterId],
))
.length >
0;
}

Future<FictionContent> readFromCache(
String fictionId, String chapterId) async {
final result = (await _database.query(_fictionCacheTable,
where: "fiction_id=? and chapter_id=?",
whereArgs: [fictionId, chapterId]))
.first;
return FictionContent(
result["fiction_title"],
result["chapter_title"],
result["fiction_id"],
result["chapter_id"],
result["content"].split("\n"));
}

Future<int> cacheIfNotCached(FictionContent fictionContent) async {
// fiction_id TEXT NOT NULL PRIMARY KEY,
// chapter_id TEXT NOT NULL,
// fiction_title TEXT NOT NULL,
// chapter_title TEXT NOT NULL,
// content TEXT
if (!await cacheExists(
fictionContent.fictionID, fictionContent.chapterID)) {
return _database.insert(_fictionCacheTable, {
"fiction_id": fictionContent.fictionID,
"chapter_id": fictionContent.chapterID,
"fiction_title": fictionContent.fictionTitle,
"chapter_title": fictionContent.chapterTitle,
"content": fictionContent.lines.join("\n"),
});
}
return Future.value(0);
}

Future<bool> historyExists(String fictionId) async {
return (await _database.query(
_historyTable,
where: "fiction_id=?",
whereArgs: [fictionId],
))
.length >
0;
}

remember(String fictionId, String lastRead) async {
if (!await historyExists(fictionId)) {
_database.insert(_historyTable, {
"fiction_id": fictionId,
"last_read": lastRead,
});
} else {
_database.update(
_historyTable,
{
"last_read": lastRead,
},
where: "fiction_id=?",
whereArgs: [fictionId],
);
}
}

Future<String> tellMe(String fictionId) async {
final result = await _database.query(
_historyTable,
where: "fiction_id=?",
whereArgs: [fictionId],
);
if (result.length > 0) {
return result.first["last_read"] as String;
} else {
return null;
}
}
}
16 changes: 12 additions & 4 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,23 @@ class _MyAppState extends State<MyApp> {

void jumpToDetail(String id) {
setState(() {
pages.add(MaterialPage(
child:
Provider.value(value: jumpToReader, child: FictionDetail(id))));
pages.add(
MaterialPage(
child: Provider.value(
value: jumpToReader,
child: _databaseProvider(
child: FictionDetail(id),
),
),
),
);
});
}

void jumpToReader(String id, Chapter chapter) {
setState(() {
pages.add(MaterialPage(child: FictionReaderPage(id, chapter)));
pages.add(MaterialPage(
child: _databaseProvider(child: FictionReaderPage(id, chapter))));
});
}

Expand Down
59 changes: 50 additions & 9 deletions lib/pages/fictiondetail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import '../api/detail.dart' as DetailAPI;
import '../database.dart';

class FictionDetail extends StatelessWidget {
final String _id;
Expand Down Expand Up @@ -89,15 +90,55 @@ class FictionDetail extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data.title,
style:
TextStyle(fontSize: 20, color: Colors.blueGrey[700]),
),
Text(
data.author,
style:
TextStyle(fontSize: 15, color: Colors.blueGrey[500]),
Consumer2<Database, JumpToReaderType>(
builder: (_, database, jumpToDetail, __) => Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
data.title,
style: TextStyle(
fontSize: 20, color: Colors.blueGrey[700]),
),
Text(
data.author,
style: TextStyle(
fontSize: 15, color: Colors.blueGrey[500]),
),
],
),
if (database != null)
FutureBuilder(
future: database.historyExists(_id),
builder: (_, snapshot) {
if (!snapshot.hasData) {
return Container();
}
if (snapshot.data) {
return FlatButton(
child: Text(
"上次阅读",
style: TextStyle(fontSize: 20),
),
onPressed: () {
database.tellMe(_id).then((value) =>
jumpToDetail(
_id,
data.chapters
.where((element) =>
element.id == value)
.first));
},
);
}
return Container();
},
)
],
),
),
Text(
data.status,
Expand Down
Loading

0 comments on commit eeb5cc6

Please sign in to comment.