mirror of
https://github.com/haorendashu/nowser.git
synced 2025-12-17 09:54:19 +01:00
bookmark and history deletable
This commit is contained in:
65
lib/component/deletable_list_mixin.dart
Normal file
65
lib/component/deletable_list_mixin.dart
Normal file
@@ -0,0 +1,65 @@
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../const/base.dart';
|
||||
|
||||
mixin DeletableListMixin<T extends StatefulWidget> on State<T> {
|
||||
bool deleting = false;
|
||||
|
||||
List<Widget> genAppBarActions(BuildContext context) {
|
||||
return deleting
|
||||
? [
|
||||
GestureDetector(
|
||||
onTap: delete,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(Base.BASE_PADDING),
|
||||
child: Icon(Icons.delete_sweep_outlined),
|
||||
),
|
||||
),
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
Widget wrapListItem(Widget child,
|
||||
{required Function onTap, required Function onSelect}) {
|
||||
if (deleting) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onSelect();
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
} else {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap();
|
||||
},
|
||||
onLongPress: () {
|
||||
setState(() {
|
||||
deleting = true;
|
||||
});
|
||||
|
||||
onSelect();
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> delete() async {
|
||||
var cancelFunc = BotToast.showLoading();
|
||||
try {
|
||||
await doDelete();
|
||||
} catch (e) {
|
||||
print("delete error " + e.toString());
|
||||
} finally {
|
||||
cancelFunc.call();
|
||||
}
|
||||
|
||||
setState(() {
|
||||
deleting = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> doDelete() async {}
|
||||
}
|
||||
@@ -3,6 +3,10 @@ import 'package:nowser/component/image_component.dart';
|
||||
import 'package:nowser/const/base.dart';
|
||||
|
||||
class UrlListItemComponnet extends StatefulWidget {
|
||||
bool selected;
|
||||
|
||||
bool selectable;
|
||||
|
||||
String? image;
|
||||
|
||||
String title;
|
||||
@@ -12,6 +16,8 @@ class UrlListItemComponnet extends StatefulWidget {
|
||||
int? dateTime;
|
||||
|
||||
UrlListItemComponnet({
|
||||
this.selected = false,
|
||||
this.selectable = false,
|
||||
this.image,
|
||||
required this.title,
|
||||
required this.url,
|
||||
@@ -30,6 +36,16 @@ class _UrlListItemComponnet extends State<UrlListItemComponnet> {
|
||||
var themeData = Theme.of(context);
|
||||
var smallFontSize = themeData.textTheme.bodySmall!.fontSize;
|
||||
|
||||
List<Widget> list = [];
|
||||
if (widget.selectable) {
|
||||
list.add(Container(
|
||||
child: Checkbox(
|
||||
value: widget.selected,
|
||||
onChanged: (bool? value) {},
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
Widget iconWidget = Icon(
|
||||
Icons.image,
|
||||
weight: 60,
|
||||
@@ -43,6 +59,13 @@ class _UrlListItemComponnet extends State<UrlListItemComponnet> {
|
||||
);
|
||||
}
|
||||
|
||||
list.add(Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
alignment: Alignment.center,
|
||||
child: iconWidget,
|
||||
));
|
||||
|
||||
String host = widget.url;
|
||||
try {
|
||||
var uri = Uri.parse(widget.url);
|
||||
@@ -55,68 +78,62 @@ class _UrlListItemComponnet extends State<UrlListItemComponnet> {
|
||||
time = "${dt.hour}:${dt.minute}";
|
||||
}
|
||||
|
||||
list.add(Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 1, color: themeData.hintColor.withOpacity(0.5)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: Base.BASE_PADDING_HALF,
|
||||
bottom: Base.BASE_PADDING_HALF,
|
||||
),
|
||||
padding: EdgeInsets.only(right: Base.BASE_PADDING_HALF),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
host,
|
||||
style: TextStyle(
|
||||
fontSize: smallFontSize,
|
||||
color: themeData.hintColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
time,
|
||||
style: TextStyle(
|
||||
color: themeData.hintColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: Base.BASE_PADDING,
|
||||
right: Base.BASE_PADDING,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
alignment: Alignment.center,
|
||||
child: iconWidget,
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 1, color: themeData.hintColor.withOpacity(0.5)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(
|
||||
top: Base.BASE_PADDING_HALF,
|
||||
bottom: Base.BASE_PADDING_HALF,
|
||||
),
|
||||
padding: EdgeInsets.only(right: Base.BASE_PADDING_HALF),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
host,
|
||||
style: TextStyle(
|
||||
fontSize: smallFontSize,
|
||||
color: themeData.hintColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: Text(
|
||||
time,
|
||||
style: TextStyle(
|
||||
color: themeData.hintColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
children: list,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,4 +37,16 @@ class AuthLogDB {
|
||||
db = await DB.getDB(db);
|
||||
db.execute("delete from auth_log where app_id = ?", [appId]);
|
||||
}
|
||||
|
||||
static Future<void> deleteByIds(List<int> ids, {DatabaseExecutor? db}) async {
|
||||
var sql = "delete from auth_log where id in(";
|
||||
for (var id in ids) {
|
||||
sql += "?,";
|
||||
}
|
||||
sql = sql.substring(0, sql.length - 1);
|
||||
sql += ")";
|
||||
|
||||
db = await DB.getDB(db);
|
||||
db.execute(sql, ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,16 @@ class BookmarkDB {
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
static Future<void> deleteByIds(List<int> ids, {DatabaseExecutor? db}) async {
|
||||
var sql = "delete from bookmark where id in(";
|
||||
for (var id in ids) {
|
||||
sql += "?,";
|
||||
}
|
||||
sql = sql.substring(0, sql.length - 1);
|
||||
sql += ")";
|
||||
|
||||
db = await DB.getDB(db);
|
||||
await db.execute(sql, ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,16 @@ class BrowserHistoryDB {
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
static Future<void> deleteByIds(List<int> ids, {DatabaseExecutor? db}) async {
|
||||
var sql = "delete from browser_history where id in(";
|
||||
for (var id in ids) {
|
||||
sql += "?,";
|
||||
}
|
||||
sql = sql.substring(0, sql.length - 1);
|
||||
sql += ")";
|
||||
|
||||
db = await DB.getDB(db);
|
||||
await db.execute(sql, ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nowser/component/cust_state.dart';
|
||||
import 'package:nowser/component/deletable_list_mixin.dart';
|
||||
import 'package:nowser/component/url_list_item_componnet.dart';
|
||||
import 'package:nowser/data/browser_history_db.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
|
||||
import '../../component/appbar_back_btn_component.dart';
|
||||
import '../../const/base.dart';
|
||||
import '../../data/bookmark.dart';
|
||||
import '../../data/bookmark_db.dart';
|
||||
|
||||
@@ -16,7 +15,8 @@ class BookmarkRouter extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _BookmarkRouter extends CustState<BookmarkRouter> {
|
||||
class _BookmarkRouter extends CustState<BookmarkRouter>
|
||||
with DeletableListMixin {
|
||||
List<Bookmark> bookmarks = [];
|
||||
|
||||
@override
|
||||
@@ -25,6 +25,8 @@ class _BookmarkRouter extends CustState<BookmarkRouter> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
List<int> selectedIds = [];
|
||||
|
||||
@override
|
||||
Widget doBuild(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
@@ -39,15 +41,7 @@ class _BookmarkRouter extends CustState<BookmarkRouter> {
|
||||
fontSize: themeData.textTheme.bodyLarge!.fontSize,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
// GestureDetector(
|
||||
// onTap: () {},
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.all(Base.BASE_PADDING),
|
||||
// child: Icon(Icons.delete_sweep_outlined),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
actions: genAppBarActions(context),
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
@@ -57,23 +51,39 @@ class _BookmarkRouter extends CustState<BookmarkRouter> {
|
||||
|
||||
var bookmark = bookmarks[index];
|
||||
|
||||
var main = Container(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
RouterUtil.back(context, bookmark.url);
|
||||
},
|
||||
child: UrlListItemComponnet(
|
||||
image: bookmark.favicon,
|
||||
title: bookmark.title ?? "",
|
||||
url: bookmark.url ?? "",
|
||||
),
|
||||
),
|
||||
Widget main = UrlListItemComponnet(
|
||||
selectable: deleting,
|
||||
selected: selectedIds.contains(bookmark.id),
|
||||
image: bookmark.favicon,
|
||||
title: bookmark.title ?? "",
|
||||
url: bookmark.url ?? "",
|
||||
);
|
||||
|
||||
main = wrapListItem(main, onTap: () {
|
||||
RouterUtil.back(context, bookmark.url);
|
||||
}, onSelect: () {
|
||||
if (!selectedIds.contains(bookmark.id)) {
|
||||
setState(() {
|
||||
selectedIds.add(bookmark.id!);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return main;
|
||||
},
|
||||
itemCount: bookmarks.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> doDelete() async {
|
||||
if (selectedIds.isNotEmpty) {
|
||||
await BookmarkDB.deleteByIds(selectedIds);
|
||||
bookmarks.removeWhere((o) {
|
||||
return selectedIds.contains(o.id);
|
||||
});
|
||||
selectedIds.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nowser/component/cust_state.dart';
|
||||
import 'package:nowser/component/deletable_list_mixin.dart';
|
||||
import 'package:nowser/component/url_list_item_componnet.dart';
|
||||
import 'package:nowser/data/browser_history_db.dart';
|
||||
import 'package:nowser/util/router_util.dart';
|
||||
@@ -15,7 +17,7 @@ class HistoryRouter extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _HistoryRouter extends CustState<HistoryRouter> {
|
||||
class _HistoryRouter extends CustState<HistoryRouter> with DeletableListMixin {
|
||||
List<BrowserHistory> historys = [];
|
||||
|
||||
@override
|
||||
@@ -24,6 +26,8 @@ class _HistoryRouter extends CustState<HistoryRouter> {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
List<int> selectedIds = [];
|
||||
|
||||
@override
|
||||
Widget doBuild(BuildContext context) {
|
||||
var themeData = Theme.of(context);
|
||||
@@ -38,15 +42,7 @@ class _HistoryRouter extends CustState<HistoryRouter> {
|
||||
fontSize: themeData.textTheme.bodyLarge!.fontSize,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
// GestureDetector(
|
||||
// onTap: () {},
|
||||
// child: Container(
|
||||
// padding: const EdgeInsets.all(Base.BASE_PADDING),
|
||||
// child: Icon(Icons.delete_sweep_outlined),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
actions: genAppBarActions(context),
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
@@ -73,20 +69,25 @@ class _HistoryRouter extends CustState<HistoryRouter> {
|
||||
}
|
||||
}
|
||||
|
||||
var main = Container(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
RouterUtil.back(context, history.url);
|
||||
},
|
||||
child: UrlListItemComponnet(
|
||||
image: history.favicon,
|
||||
title: history.title ?? "",
|
||||
url: history.url ?? "",
|
||||
dateTime: history.createdAt,
|
||||
),
|
||||
),
|
||||
Widget main = UrlListItemComponnet(
|
||||
selectable: deleting,
|
||||
selected: selectedIds.contains(history.id),
|
||||
image: history.favicon,
|
||||
title: history.title ?? "",
|
||||
url: history.url ?? "",
|
||||
dateTime: history.createdAt,
|
||||
);
|
||||
|
||||
main = wrapListItem(main, onTap: () {
|
||||
RouterUtil.back(context, history.url);
|
||||
}, onSelect: () {
|
||||
if (!selectedIds.contains(history.id)) {
|
||||
setState(() {
|
||||
selectedIds.add(history.id!);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (dateWidget != null) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -100,4 +101,15 @@ class _HistoryRouter extends CustState<HistoryRouter> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> doDelete() async {
|
||||
if (selectedIds.isNotEmpty) {
|
||||
await BrowserHistoryDB.deleteByIds(selectedIds);
|
||||
historys.removeWhere((o) {
|
||||
return selectedIds.contains(o.id);
|
||||
});
|
||||
selectedIds.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user