mirror of
https://github.com/haorendashu/nowser.git
synced 2025-12-18 18:14:21 +01:00
add quick action and desktop, bookmark add edit dialog
This commit is contained in:
BIN
assets/imgs/browser.png
Normal file
BIN
assets/imgs/browser.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
201
lib/component/bookmark_edit_dialog.dart
Normal file
201
lib/component/bookmark_edit_dialog.dart
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nostr_sdk/utils/platform_util.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
import 'package:nowser/data/bookmark.dart';
|
||||||
|
import 'package:nowser/data/bookmark_db.dart';
|
||||||
|
import 'package:quick_actions/quick_actions.dart';
|
||||||
|
|
||||||
|
import '../const/base.dart';
|
||||||
|
import '../main.dart';
|
||||||
|
import '../util/router_util.dart';
|
||||||
|
import '../util/table_mode_util.dart';
|
||||||
|
import '../util/theme_util.dart';
|
||||||
|
|
||||||
|
class BookmarkEditDialog extends StatefulWidget {
|
||||||
|
Bookmark bookmark;
|
||||||
|
|
||||||
|
BookmarkEditDialog(this.bookmark);
|
||||||
|
|
||||||
|
static Future<void> show(BuildContext context, Bookmark bookmark) async {
|
||||||
|
await showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (_context) {
|
||||||
|
return BookmarkEditDialog(bookmark);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _BookmarkEditDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BookmarkEditDialog extends State<BookmarkEditDialog> {
|
||||||
|
TextEditingController nameTextController = TextEditingController();
|
||||||
|
|
||||||
|
TextEditingController urlTextController = TextEditingController();
|
||||||
|
|
||||||
|
bool addedToIndex = false;
|
||||||
|
|
||||||
|
bool addedToQa = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
if (StringUtil.isNotBlank(widget.bookmark.title)) {
|
||||||
|
nameTextController.text = widget.bookmark.title!;
|
||||||
|
}
|
||||||
|
if (StringUtil.isNotBlank(widget.bookmark.url)) {
|
||||||
|
urlTextController.text = widget.bookmark.url!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var themeData = Theme.of(context);
|
||||||
|
|
||||||
|
List<Widget> list = [];
|
||||||
|
list.add(Container(
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
bottom: Base.BASE_PADDING,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
"Add bookmark",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: themeData.textTheme.bodyLarge!.fontSize,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
list.add(Container(
|
||||||
|
child: TextField(
|
||||||
|
controller: nameTextController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Name",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
list.add(Container(
|
||||||
|
child: TextField(
|
||||||
|
controller: urlTextController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Url",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
list.add(Container(
|
||||||
|
margin: EdgeInsets.only(top: Base.BASE_PADDING_HALF),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text("Add to index"),
|
||||||
|
Expanded(
|
||||||
|
child: Checkbox(
|
||||||
|
value: addedToIndex,
|
||||||
|
onChanged: (v) {
|
||||||
|
if (v != null) {
|
||||||
|
setState(() {
|
||||||
|
addedToIndex = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
list.add(Container(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text("Add to quick action"),
|
||||||
|
Expanded(
|
||||||
|
child: Checkbox(
|
||||||
|
value: addedToQa,
|
||||||
|
onChanged: (v) {
|
||||||
|
if (v != null) {
|
||||||
|
setState(() {
|
||||||
|
addedToQa = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
list.add(Container(
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
top: Base.BASE_PADDING * 2,
|
||||||
|
bottom: Base.BASE_PADDING,
|
||||||
|
),
|
||||||
|
width: double.infinity,
|
||||||
|
child: FilledButton(onPressed: confirm, child: Text("Confirm")),
|
||||||
|
));
|
||||||
|
|
||||||
|
Widget main = Container(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: list,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (PlatformUtil.isPC() || TableModeUtil.isTableMode()) {
|
||||||
|
main = Container(
|
||||||
|
width: mediaDataCache.size.width / 2,
|
||||||
|
child: main,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dialog(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(Base.BASE_PADDING * 2),
|
||||||
|
child: main,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> confirm() async {
|
||||||
|
var title = nameTextController.text;
|
||||||
|
var url = urlTextController.text;
|
||||||
|
|
||||||
|
var bookmark = Bookmark(
|
||||||
|
title: title,
|
||||||
|
url: url,
|
||||||
|
id: widget.bookmark.id,
|
||||||
|
favicon: widget.bookmark.favicon,
|
||||||
|
weight: widget.bookmark.weight,
|
||||||
|
createdAt: widget.bookmark.createdAt,
|
||||||
|
addedToIndex: addedToIndex ? 1 : -1,
|
||||||
|
addedToQa: addedToQa ? 1 : -1,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (bookmark.id == null) {
|
||||||
|
await BookmarkDB.insert(bookmark);
|
||||||
|
} else {
|
||||||
|
await BookmarkDB.update(bookmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var allQas = await BookmarkDB.allQas();
|
||||||
|
List<ShortcutItem> qas = [];
|
||||||
|
for (var bk in allQas) {
|
||||||
|
if (StringUtil.isBlank(bk.title) || StringUtil.isBlank(bk.url)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qas.add(ShortcutItem(
|
||||||
|
type: bk.url!, localizedTitle: bk.title!, icon: 'ic_launcher'));
|
||||||
|
quickActions.setShortcutItems(qas);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
RouterUtil.back(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ class Bookmark {
|
|||||||
String? favicon;
|
String? favicon;
|
||||||
int? weight;
|
int? weight;
|
||||||
int? addedToIndex;
|
int? addedToIndex;
|
||||||
|
int? addedToQa;
|
||||||
int? createdAt;
|
int? createdAt;
|
||||||
|
|
||||||
Bookmark(
|
Bookmark(
|
||||||
@@ -14,6 +15,7 @@ class Bookmark {
|
|||||||
this.favicon,
|
this.favicon,
|
||||||
this.weight,
|
this.weight,
|
||||||
this.addedToIndex,
|
this.addedToIndex,
|
||||||
|
this.addedToQa,
|
||||||
this.createdAt});
|
this.createdAt});
|
||||||
|
|
||||||
Bookmark.fromJson(Map<String, dynamic> json) {
|
Bookmark.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -23,6 +25,7 @@ class Bookmark {
|
|||||||
favicon = json['favicon'];
|
favicon = json['favicon'];
|
||||||
weight = json['weight'];
|
weight = json['weight'];
|
||||||
addedToIndex = json['added_to_index'];
|
addedToIndex = json['added_to_index'];
|
||||||
|
addedToQa = json['added_to_qa'];
|
||||||
createdAt = json['created_at'];
|
createdAt = json['created_at'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,6 +37,7 @@ class Bookmark {
|
|||||||
data['favicon'] = this.favicon;
|
data['favicon'] = this.favicon;
|
||||||
data['weight'] = this.weight;
|
data['weight'] = this.weight;
|
||||||
data['added_to_index'] = this.addedToIndex;
|
data['added_to_index'] = this.addedToIndex;
|
||||||
|
data['added_to_qa'] = this.addedToQa;
|
||||||
data['created_at'] = this.createdAt;
|
data['created_at'] = this.createdAt;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,20 @@ class BookmarkDB {
|
|||||||
return objs;
|
return objs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<List<Bookmark>> allQas({DatabaseExecutor? db}) async {
|
||||||
|
List<Bookmark> objs = [];
|
||||||
|
List<Object?>? arguments = [];
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
var sql =
|
||||||
|
"select * from bookmark where added_to_qa = 1 order by created_at desc";
|
||||||
|
List<Map<String, dynamic>> list = await db.rawQuery(sql, arguments);
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
var json = list[i];
|
||||||
|
objs.add(Bookmark.fromJson(json));
|
||||||
|
}
|
||||||
|
return objs;
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> deleteByIds(List<int> ids, {DatabaseExecutor? db}) async {
|
static Future<void> deleteByIds(List<int> ids, {DatabaseExecutor? db}) async {
|
||||||
var sql = "delete from bookmark where id in(";
|
var sql = "delete from bookmark where id in(";
|
||||||
for (var id in ids) {
|
for (var id in ids) {
|
||||||
@@ -39,4 +53,9 @@ class BookmarkDB {
|
|||||||
db = await DB.getDB(db);
|
db = await DB.getDB(db);
|
||||||
await db.execute(sql, ids);
|
await db.execute(sql, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future update(Bookmark o, {DatabaseExecutor? db}) async {
|
||||||
|
db = await DB.getDB(db);
|
||||||
|
await db.update("bookmark", o.toJson(), where: "id = ?", whereArgs: [o.id]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:path/path.dart';
|
|||||||
import 'package:process_run/shell_run.dart';
|
import 'package:process_run/shell_run.dart';
|
||||||
|
|
||||||
class DB {
|
class DB {
|
||||||
static const _VERSION = 1;
|
static const _VERSION = 2;
|
||||||
|
|
||||||
static const _dbName = "nowser.db";
|
static const _dbName = "nowser.db";
|
||||||
|
|
||||||
@@ -21,14 +21,14 @@ class DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_database =
|
_database = await openDatabase(path,
|
||||||
await openDatabase(path, version: _VERSION, onCreate: _onCreate);
|
version: _VERSION, onCreate: _onCreate, onUpgrade: _onUpgrade);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (Platform.isLinux) {
|
if (Platform.isLinux) {
|
||||||
// maybe it need install sqlite first, but this command need run by root.
|
// maybe it need install sqlite first, but this command need run by root.
|
||||||
await run('sudo apt-get -y install libsqlite3-0 libsqlite3-dev');
|
await run('sudo apt-get -y install libsqlite3-0 libsqlite3-dev');
|
||||||
_database =
|
_database = await openDatabase(path,
|
||||||
await openDatabase(path, version: _VERSION, onCreate: _onCreate);
|
version: _VERSION, onCreate: _onCreate, onUpgrade: _onUpgrade);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,11 +50,19 @@ class DB {
|
|||||||
db.execute("create index zap_log_index on zap_log (app_id);");
|
db.execute("create index zap_log_index on zap_log (app_id);");
|
||||||
|
|
||||||
db.execute(
|
db.execute(
|
||||||
"create table bookmark(id integer not null constraint bookmark_pk primary key autoincrement,title text,url text not null,favicon text,weight integer,added_to_index integer,created_at integer);");
|
"create table bookmark(id integer not null constraint bookmark_pk primary key autoincrement,title text,url text not null,favicon text,weight integer,added_to_index integer, added_to_qa integer,created_at integer);");
|
||||||
db.execute(
|
db.execute(
|
||||||
"create table browser_history(id integer not null constraint browser_history_pk primary key autoincrement,title text,url text not null,favicon text,created_at integer);");
|
"create table browser_history(id integer not null constraint browser_history_pk primary key autoincrement,title text,url text not null,favicon text,created_at integer);");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> _onUpgrade(
|
||||||
|
Database db, int oldVersion, int newVersion) async {
|
||||||
|
if (oldVersion == 1 && newVersion == 2) {
|
||||||
|
db.execute(
|
||||||
|
"alter table bookmark add added_to_qa integer after added_to_index");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Future<Database> getCurrentDatabase() async {
|
static Future<Database> getCurrentDatabase() async {
|
||||||
if (_database == null) {
|
if (_database == null) {
|
||||||
await init();
|
await init();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import 'package:nowser/router/me/me_router.dart';
|
|||||||
import 'package:nowser/router/web_tabs_select/web_tabs_select_router.dart';
|
import 'package:nowser/router/web_tabs_select/web_tabs_select_router.dart';
|
||||||
import 'package:nowser/router/web_url_input/web_url_input_router.dart';
|
import 'package:nowser/router/web_url_input/web_url_input_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:quick_actions/quick_actions.dart';
|
||||||
import 'package:receive_intent/receive_intent.dart' as receiveIntent;
|
import 'package:receive_intent/receive_intent.dart' as receiveIntent;
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
@@ -41,6 +42,7 @@ import 'provider/data_util.dart';
|
|||||||
import 'provider/remote_signing_provider.dart';
|
import 'provider/remote_signing_provider.dart';
|
||||||
import 'provider/setting_provider.dart';
|
import 'provider/setting_provider.dart';
|
||||||
import 'util/colors_util.dart';
|
import 'util/colors_util.dart';
|
||||||
|
import 'util/media_data_cache.dart';
|
||||||
|
|
||||||
late WebProvider webProvider;
|
late WebProvider webProvider;
|
||||||
|
|
||||||
@@ -58,6 +60,10 @@ late RootIsolateToken rootIsolateToken;
|
|||||||
|
|
||||||
late BuildInRelayProvider buildInRelayProvider;
|
late BuildInRelayProvider buildInRelayProvider;
|
||||||
|
|
||||||
|
const QuickActions quickActions = QuickActions();
|
||||||
|
|
||||||
|
late MediaDataCache mediaDataCache;
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||||
rootIsolateToken = RootIsolateToken.instance!;
|
rootIsolateToken = RootIsolateToken.instance!;
|
||||||
|
|||||||
@@ -38,15 +38,26 @@ mixin AndroidSignerMixin on PermissionCheckMixin {
|
|||||||
static const String PREFIX = "nostrsigner:";
|
static const String PREFIX = "nostrsigner:";
|
||||||
|
|
||||||
Future<void> handleInitialIntent(BuildContext context) async {
|
Future<void> handleInitialIntent(BuildContext context) async {
|
||||||
final intent = await receiveIntent.ReceiveIntent.getInitialIntent();
|
var intent = await getInitialIntent();
|
||||||
if (intent != null) {
|
await dohandleInitialIntent(context, intent);
|
||||||
// log(intent.toString());
|
}
|
||||||
// log("from ${intent.fromPackageName}");
|
|
||||||
// log("action ${intent.action}");
|
|
||||||
// log("data ${intent.data}");
|
|
||||||
// log("categories ${intent.categories}");
|
|
||||||
// log("extra ${intent.extra}");
|
|
||||||
|
|
||||||
|
Future<receiveIntent.Intent?> getInitialIntent() async {
|
||||||
|
var intent = await receiveIntent.ReceiveIntent.getInitialIntent();
|
||||||
|
if (intent != null) {
|
||||||
|
log(intent.toString());
|
||||||
|
log("from ${intent.fromPackageName}");
|
||||||
|
log("action ${intent.action}");
|
||||||
|
log("data ${intent.data}");
|
||||||
|
log("categories ${intent.categories}");
|
||||||
|
log("extra ${intent.extra}");
|
||||||
|
}
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> dohandleInitialIntent(
|
||||||
|
BuildContext context, receiveIntent.Intent? intent) async {
|
||||||
|
if (intent != null) {
|
||||||
if (StringUtil.isNotBlank(intent.data) &&
|
if (StringUtil.isNotBlank(intent.data) &&
|
||||||
intent.data!.startsWith(PREFIX)) {
|
intent.data!.startsWith(PREFIX)) {
|
||||||
// This is an android signer intent
|
// This is an android signer intent
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:nostr_sdk/utils/string_util.dart';
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
import 'package:nowser/component/bookmark_edit_dialog.dart';
|
||||||
import 'package:nowser/data/bookmark_db.dart';
|
import 'package:nowser/data/bookmark_db.dart';
|
||||||
import 'package:nowser/util/router_util.dart';
|
import 'package:nowser/util/router_util.dart';
|
||||||
|
|
||||||
@@ -74,12 +75,42 @@ class WebProvider extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTab() {
|
void addTab({String url = ""}) {
|
||||||
webInfos.add(WebInfo(_rndId(), ""));
|
webInfos.add(WebInfo(_rndId(), url));
|
||||||
index = webInfos.length - 1;
|
index = webInfos.length - 1;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void checkAndOpenUrl(String url) {
|
||||||
|
if (!url.startsWith("http")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int targetIndex = -1;
|
||||||
|
for (var i = 0; i < webInfos.length; i++) {
|
||||||
|
var webInfo = webInfos[i];
|
||||||
|
if (webInfo.url.contains(url)) {
|
||||||
|
targetIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetIndex > -1) {
|
||||||
|
if (index != targetIndex) {
|
||||||
|
index = targetIndex;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var _currentWebInfo = currentWebInfo();
|
||||||
|
if (_currentWebInfo != null && _currentWebInfo.url == "") {
|
||||||
|
_currentWebInfo.url = url;
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
addTab(url: url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void changeIndex(WebInfo webInfo) {
|
void changeIndex(WebInfo webInfo) {
|
||||||
for (var i = 0; i < webInfos.length; i++) {
|
for (var i = 0; i < webInfos.length; i++) {
|
||||||
var owi = webInfos[i];
|
var owi = webInfos[i];
|
||||||
@@ -146,7 +177,7 @@ class WebProvider extends ChangeNotifier {
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
void addBookmark(WebInfo webInfo) {
|
void addBookmark(BuildContext context, WebInfo webInfo) {
|
||||||
if (webInfo.browserHistory == null) {
|
if (webInfo.browserHistory == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -159,7 +190,8 @@ class WebProvider extends ChangeNotifier {
|
|||||||
bookmark.addedToIndex = -1;
|
bookmark.addedToIndex = -1;
|
||||||
bookmark.createdAt = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
bookmark.createdAt = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
|
|
||||||
BookmarkDB.insert(bookmark);
|
// BookmarkDB.insert(bookmark);
|
||||||
|
BookmarkEditDialog.show(context, bookmark);
|
||||||
}
|
}
|
||||||
|
|
||||||
void back(BuildContext context) {
|
void back(BuildContext context) {
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
|
import 'package:flutter_pinned_shortcut_plus/flutter_pinned_shortcut_plus.dart';
|
||||||
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
import 'package:nostr_sdk/utils/string_util.dart';
|
||||||
|
import 'package:nowser/component/bookmark_edit_dialog.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/deletable_list_mixin.dart';
|
||||||
import 'package:nowser/component/url_list_item_componnet.dart';
|
import 'package:nowser/component/url_list_item_componnet.dart';
|
||||||
@@ -21,6 +28,10 @@ class _BookmarkRouter extends CustState<BookmarkRouter>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onReady(BuildContext context) async {
|
Future<void> onReady(BuildContext context) async {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> reload() async {
|
||||||
bookmarks = await BookmarkDB.all();
|
bookmarks = await BookmarkDB.all();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
@@ -59,6 +70,36 @@ class _BookmarkRouter extends CustState<BookmarkRouter>
|
|||||||
url: bookmark.url ?? "",
|
url: bookmark.url ?? "",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
List<Widget> slidableActionList = [];
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
slidableActionList.add(SlidableAction(
|
||||||
|
onPressed: (context) {
|
||||||
|
doAddPinnedShortcut(bookmark);
|
||||||
|
},
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: Icons.add_home,
|
||||||
|
label: 'Desktop',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
slidableActionList.add(SlidableAction(
|
||||||
|
onPressed: (context) {
|
||||||
|
doEdit(bookmark);
|
||||||
|
},
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
icon: Icons.edit,
|
||||||
|
label: 'Edit',
|
||||||
|
));
|
||||||
|
|
||||||
|
main = Slidable(
|
||||||
|
endActionPane: ActionPane(
|
||||||
|
motion: ScrollMotion(),
|
||||||
|
children: slidableActionList,
|
||||||
|
),
|
||||||
|
child: main,
|
||||||
|
);
|
||||||
|
|
||||||
main = wrapListItem(main, onTap: () {
|
main = wrapListItem(main, onTap: () {
|
||||||
RouterUtil.back(context, bookmark.url);
|
RouterUtil.back(context, bookmark.url);
|
||||||
}, onSelect: () {
|
}, onSelect: () {
|
||||||
@@ -86,4 +127,36 @@ class _BookmarkRouter extends CustState<BookmarkRouter>
|
|||||||
selectedIds.clear();
|
selectedIds.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final _flutterPinnedShortcutPlugin = FlutterPinnedShortcut();
|
||||||
|
|
||||||
|
Future<void> doAddPinnedShortcut(Bookmark bookmark) async {
|
||||||
|
File? file;
|
||||||
|
if (StringUtil.isNotBlank(bookmark.favicon)) {
|
||||||
|
// use favicon as icon
|
||||||
|
file = await DefaultCacheManager().getSingleFile(bookmark.favicon!);
|
||||||
|
} else {
|
||||||
|
// use default image as icon
|
||||||
|
// file =
|
||||||
|
print("favicon not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtil.isBlank(bookmark.title) ||
|
||||||
|
StringUtil.isBlank(bookmark.url)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flutterPinnedShortcutPlugin.createPinnedShortcut(
|
||||||
|
id: StringUtil.rndNameStr(10),
|
||||||
|
label: bookmark.title!,
|
||||||
|
action: bookmark.url!,
|
||||||
|
iconAssetName: "assets/logo_android.png",
|
||||||
|
iconUri: Uri.file(file.path).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> doEdit(Bookmark bookmark) async {
|
||||||
|
await BookmarkEditDialog.show(context, bookmark);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,15 +40,53 @@ class _IndexRouter extends CustState<IndexRouter>
|
|||||||
|
|
||||||
// start build-in
|
// start build-in
|
||||||
buildInRelayProvider.start();
|
buildInRelayProvider.start();
|
||||||
|
|
||||||
|
if (PlatformUtil.isAndroid()) {
|
||||||
|
var intent = await getInitialIntent();
|
||||||
|
if (intent != null) {
|
||||||
|
if (intent.categories != null &&
|
||||||
|
intent.categories!.contains("android.intent.category.LAUNCHER") &&
|
||||||
|
intent.extra != null &&
|
||||||
|
intent.extra!["flutter_pinned_shortcuts"] != null) {
|
||||||
|
var url = intent.extra!["flutter_pinned_shortcuts"];
|
||||||
|
print("find url! $url");
|
||||||
|
webProvider.checkAndOpenUrl(url);
|
||||||
|
} else {
|
||||||
|
dohandleInitialIntent(context, intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
quickActions.initialize((shortcutType) {
|
||||||
|
print("find quickAction $shortcutType");
|
||||||
|
webProvider.checkAndOpenUrl(shortcutType);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget doBuild(BuildContext context) {
|
Widget doBuild(BuildContext context) {
|
||||||
if (PlatformUtil.isAndroid()) {
|
// if (PlatformUtil.isAndroid()) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
// WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
handleInitialIntent(context);
|
// var intent = await getInitialIntent();
|
||||||
});
|
// if (intent != null) {
|
||||||
}
|
// if (intent.categories != null &&
|
||||||
|
// intent.categories!.contains("android.intent.category.LAUNCHER") &&
|
||||||
|
// intent.extra != null &&
|
||||||
|
// intent.extra!["flutter_pinned_shortcuts"] != null) {
|
||||||
|
// var url = intent.extra!["flutter_pinned_shortcuts"];
|
||||||
|
// print("find url! $url");
|
||||||
|
// webProvider.checkAndOpenUrl(url);
|
||||||
|
// } else {
|
||||||
|
// dohandleInitialIntent(context, intent);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// quickActions.initialize((shortcutType) {
|
||||||
|
// print("find quickAction $shortcutType");
|
||||||
|
// webProvider.checkAndOpenUrl(shortcutType);
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
remoteSigningProvider.updateContext(context);
|
remoteSigningProvider.updateContext(context);
|
||||||
webProvider.checkBlank();
|
webProvider.checkBlank();
|
||||||
|
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ class _WebControlComponent extends State<WebControlComponent> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
var webInfo = webProvider.currentWebInfo();
|
var webInfo = webProvider.currentWebInfo();
|
||||||
if (webInfo != null) {
|
if (webInfo != null) {
|
||||||
webProvider.addBookmark(webInfo);
|
webProvider.addBookmark(context, webInfo);
|
||||||
widget.closeControl();
|
widget.closeControl();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
5
lib/util/desktop_shutcut_util.dart
Normal file
5
lib/util/desktop_shutcut_util.dart
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class DesktopShutcutUtil {
|
||||||
|
void create() {}
|
||||||
|
}
|
||||||
101
lib/util/dio_util.dart
Normal file
101
lib/util/dio_util.dart
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:dio/io.dart';
|
||||||
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
|
import 'package:cookie_jar/cookie_jar.dart';
|
||||||
|
import 'dart:convert' as convert;
|
||||||
|
|
||||||
|
Dio? _dio;
|
||||||
|
var cookieJar = CookieJar();
|
||||||
|
|
||||||
|
class DioUtil {
|
||||||
|
static Dio getDio() {
|
||||||
|
if (_dio == null) {
|
||||||
|
_dio = Dio();
|
||||||
|
if (_dio!.httpClientAdapter is IOHttpClientAdapter) {
|
||||||
|
(_dio!.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate =
|
||||||
|
(client) {
|
||||||
|
client.badCertificateCallback = (cert, host, port) {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// _dio!.options.connectTimeout = Duration(minutes: 1);
|
||||||
|
// _dio!.options.receiveTimeout = Duration(minutes: 1);
|
||||||
|
_dio!.options.headers["user-agent"] =
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36";
|
||||||
|
_dio!.options.headers["accept-encoding"] = "gzip";
|
||||||
|
CookieManager cookieManager = CookieManager(cookieJar);
|
||||||
|
_dio!.interceptors.add(cookieManager);
|
||||||
|
}
|
||||||
|
return _dio!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static setCookie(String link, String key, String value) {
|
||||||
|
cookieJar.saveFromResponse(Uri.parse(link), [Cookie(key, value)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>?> get(String link,
|
||||||
|
[Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? header]) async {
|
||||||
|
var dio = getDio();
|
||||||
|
if (header != null) {
|
||||||
|
dio.options.headers.addAll(header);
|
||||||
|
}
|
||||||
|
Response resp = await dio.get(link, queryParameters: queryParameters);
|
||||||
|
if (resp.statusCode == 200) {
|
||||||
|
if (resp.data is String) {
|
||||||
|
return json.decode(resp.data);
|
||||||
|
}
|
||||||
|
return resp.data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<String?> getStr(String link,
|
||||||
|
[Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? header]) async {
|
||||||
|
var dio = getDio();
|
||||||
|
if (header != null) {
|
||||||
|
dio.options.headers.addAll(header);
|
||||||
|
}
|
||||||
|
Response resp =
|
||||||
|
await dio.get<String>(link, queryParameters: queryParameters);
|
||||||
|
if (resp.statusCode == 200) {
|
||||||
|
return resp.data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<int>?> getBytes(String link,
|
||||||
|
[Map<String, dynamic>? queryParameters,
|
||||||
|
Map<String, String>? header]) async {
|
||||||
|
var dio = getDio();
|
||||||
|
if (header != null) {
|
||||||
|
dio.options.headers.addAll(header);
|
||||||
|
}
|
||||||
|
Response resp =
|
||||||
|
await dio.get<List<int>>(link, queryParameters: queryParameters);
|
||||||
|
if (resp.statusCode == 200) {
|
||||||
|
return resp.data;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> post(
|
||||||
|
String link, Map<String, dynamic> parameters,
|
||||||
|
[Map<String, String>? header]) async {
|
||||||
|
var dio = getDio();
|
||||||
|
if (header != null) {
|
||||||
|
dio.options.headers.addAll(header);
|
||||||
|
}
|
||||||
|
Response resp = await dio.post(link, data: parameters);
|
||||||
|
return resp.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
lib/util/media_data_cache.dart
Normal file
13
lib/util/media_data_cache.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MediaDataCache {
|
||||||
|
late Size size;
|
||||||
|
|
||||||
|
late EdgeInsets padding;
|
||||||
|
|
||||||
|
void update(BuildContext context) {
|
||||||
|
var mediaData = MediaQuery.of(context);
|
||||||
|
size = mediaData.size;
|
||||||
|
padding = mediaData.padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
lib/util/theme_util.dart
Normal file
9
lib/util/theme_util.dart
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Add some support to get value from theme data.
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ThemeUtil {
|
||||||
|
static Color getDialogCoverColor(ThemeData themeData) {
|
||||||
|
return (themeData.textTheme.bodyMedium!.color ?? Colors.black)
|
||||||
|
.withOpacity(0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
pubspec.lock
42
pubspec.lock
@@ -271,7 +271,7 @@ packages:
|
|||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_cache_manager:
|
flutter_cache_manager:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_cache_manager
|
name: flutter_cache_manager
|
||||||
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
||||||
@@ -371,6 +371,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_pinned_shortcut_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_pinned_shortcut_plus
|
||||||
|
sha256: "03d5e6bf8ca157e2b478e0b38a7bf6020b6646d8050e324fc1001f717b365e7c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.2"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -756,6 +764,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
quick_actions:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: quick_actions
|
||||||
|
sha256: "2c1d9a91f3218b4e987a7e1e95ba0415b7f48a2cb3ffacc027a1e3d3c117223f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.8"
|
||||||
|
quick_actions_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quick_actions_android
|
||||||
|
sha256: "926e50d6f879287b34d21934e6c9457f0d851f554179f2a9e8136c4acd1b7062"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.18"
|
||||||
|
quick_actions_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quick_actions_ios
|
||||||
|
sha256: "402596dea62a1028960b93f7651ec22be0e2a91e4fbf92a1c62d3b95f8ff95a5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
quick_actions_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: quick_actions_platform_interface
|
||||||
|
sha256: "1fec7068db5122cd019e9340d3d7be5d36eab099695ef3402c7059ee058329a4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
receive_intent:
|
receive_intent:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ dependencies:
|
|||||||
qr_code_scanner: ^1.0.1
|
qr_code_scanner: ^1.0.1
|
||||||
flutter_slidable: ^3.1.1
|
flutter_slidable: ^3.1.1
|
||||||
window_manager: ^0.4.2
|
window_manager: ^0.4.2
|
||||||
|
quick_actions: ^1.0.8
|
||||||
|
flutter_pinned_shortcut_plus: ^0.0.2
|
||||||
|
flutter_cache_manager: ^3.4.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_launcher_icons: ^0.13.1
|
flutter_launcher_icons: ^0.13.1
|
||||||
|
|||||||
Reference in New Issue
Block a user