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';
|
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user