bookmark and history deletable

This commit is contained in:
DASHU
2024-09-27 11:48:29 +08:00
parent 1b04d52f14
commit c121370c3b
7 changed files with 241 additions and 101 deletions

View 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 {}
}

View File

@@ -3,6 +3,10 @@ import 'package:nowser/component/image_component.dart';
import 'package:nowser/const/base.dart'; import 'package:nowser/const/base.dart';
class UrlListItemComponnet extends StatefulWidget { class UrlListItemComponnet extends StatefulWidget {
bool selected;
bool selectable;
String? image; String? image;
String title; String title;
@@ -12,6 +16,8 @@ class UrlListItemComponnet extends StatefulWidget {
int? dateTime; int? dateTime;
UrlListItemComponnet({ UrlListItemComponnet({
this.selected = false,
this.selectable = false,
this.image, this.image,
required this.title, required this.title,
required this.url, required this.url,
@@ -30,6 +36,16 @@ class _UrlListItemComponnet extends State<UrlListItemComponnet> {
var themeData = Theme.of(context); var themeData = Theme.of(context);
var smallFontSize = themeData.textTheme.bodySmall!.fontSize; 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( Widget iconWidget = Icon(
Icons.image, Icons.image,
weight: 60, 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; String host = widget.url;
try { try {
var uri = Uri.parse(widget.url); var uri = Uri.parse(widget.url);
@@ -55,68 +78,62 @@ class _UrlListItemComponnet extends State<UrlListItemComponnet> {
time = "${dt.hour}:${dt.minute}"; 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( return Container(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: Base.BASE_PADDING, left: Base.BASE_PADDING,
right: Base.BASE_PADDING, right: Base.BASE_PADDING,
), ),
child: Row( child: Row(
children: [ children: list,
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,
),
),
),
],
),
),
),
],
), ),
); );
} }

View File

@@ -37,4 +37,16 @@ class AuthLogDB {
db = await DB.getDB(db); db = await DB.getDB(db);
db.execute("delete from auth_log where app_id = ?", [appId]); 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);
}
} }

View File

@@ -27,4 +27,16 @@ class BookmarkDB {
} }
return objs; 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);
}
} }

View File

@@ -27,4 +27,16 @@ class BrowserHistoryDB {
} }
return objs; 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);
}
} }

View File

@@ -1,11 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nowser/component/cust_state.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/component/url_list_item_componnet.dart';
import 'package:nowser/data/browser_history_db.dart';
import 'package:nowser/util/router_util.dart'; import 'package:nowser/util/router_util.dart';
import '../../component/appbar_back_btn_component.dart'; import '../../component/appbar_back_btn_component.dart';
import '../../const/base.dart';
import '../../data/bookmark.dart'; import '../../data/bookmark.dart';
import '../../data/bookmark_db.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 = []; List<Bookmark> bookmarks = [];
@override @override
@@ -25,6 +25,8 @@ class _BookmarkRouter extends CustState<BookmarkRouter> {
setState(() {}); setState(() {});
} }
List<int> selectedIds = [];
@override @override
Widget doBuild(BuildContext context) { Widget doBuild(BuildContext context) {
var themeData = Theme.of(context); var themeData = Theme.of(context);
@@ -39,15 +41,7 @@ class _BookmarkRouter extends CustState<BookmarkRouter> {
fontSize: themeData.textTheme.bodyLarge!.fontSize, fontSize: themeData.textTheme.bodyLarge!.fontSize,
), ),
), ),
actions: [ actions: genAppBarActions(context),
// GestureDetector(
// onTap: () {},
// child: Container(
// padding: const EdgeInsets.all(Base.BASE_PADDING),
// child: Icon(Icons.delete_sweep_outlined),
// ),
// ),
],
), ),
body: ListView.builder( body: ListView.builder(
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -57,23 +51,39 @@ class _BookmarkRouter extends CustState<BookmarkRouter> {
var bookmark = bookmarks[index]; var bookmark = bookmarks[index];
var main = Container( Widget main = UrlListItemComponnet(
child: GestureDetector( selectable: deleting,
onTap: () { selected: selectedIds.contains(bookmark.id),
RouterUtil.back(context, bookmark.url); image: bookmark.favicon,
}, title: bookmark.title ?? "",
child: UrlListItemComponnet( url: bookmark.url ?? "",
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; return main;
}, },
itemCount: bookmarks.length, 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();
}
}
} }

View File

@@ -1,5 +1,7 @@
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:nowser/component/cust_state.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/component/url_list_item_componnet.dart';
import 'package:nowser/data/browser_history_db.dart'; import 'package:nowser/data/browser_history_db.dart';
import 'package:nowser/util/router_util.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 = []; List<BrowserHistory> historys = [];
@override @override
@@ -24,6 +26,8 @@ class _HistoryRouter extends CustState<HistoryRouter> {
setState(() {}); setState(() {});
} }
List<int> selectedIds = [];
@override @override
Widget doBuild(BuildContext context) { Widget doBuild(BuildContext context) {
var themeData = Theme.of(context); var themeData = Theme.of(context);
@@ -38,15 +42,7 @@ class _HistoryRouter extends CustState<HistoryRouter> {
fontSize: themeData.textTheme.bodyLarge!.fontSize, fontSize: themeData.textTheme.bodyLarge!.fontSize,
), ),
), ),
actions: [ actions: genAppBarActions(context),
// GestureDetector(
// onTap: () {},
// child: Container(
// padding: const EdgeInsets.all(Base.BASE_PADDING),
// child: Icon(Icons.delete_sweep_outlined),
// ),
// ),
],
), ),
body: ListView.builder( body: ListView.builder(
itemBuilder: (context, index) { itemBuilder: (context, index) {
@@ -73,20 +69,25 @@ class _HistoryRouter extends CustState<HistoryRouter> {
} }
} }
var main = Container( Widget main = UrlListItemComponnet(
child: GestureDetector( selectable: deleting,
onTap: () { selected: selectedIds.contains(history.id),
RouterUtil.back(context, history.url); image: history.favicon,
}, title: history.title ?? "",
child: UrlListItemComponnet( url: history.url ?? "",
image: history.favicon, dateTime: history.createdAt,
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) { if (dateWidget != null) {
return Column( return Column(
mainAxisSize: MainAxisSize.min, 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();
}
}
} }