add web apps

This commit is contained in:
DASHU
2025-07-24 20:09:55 +08:00
parent bb9129106b
commit f4ad8da8a1
13 changed files with 583 additions and 2 deletions

View File

@@ -196,6 +196,15 @@ class _WebHomeComponent extends State<WebHomeComponent> {
main = WebHomeBtnComponent(bookmark!); main = WebHomeBtnComponent(bookmark!);
} else { } else {
if (bookmarks.length == index) { if (bookmarks.length == index) {
main = GestureDetector(
onTap: () {
RouterUtil.router(context, RouterPath.WEB_APPS);
},
child: Container(
child: Icon(Icons.apps),
),
);
} else if (bookmarks.length + 1 == index) {
main = GestureDetector( main = GestureDetector(
onTap: () { onTap: () {
BookmarkEditDialog.show( BookmarkEditDialog.show(

View File

@@ -13,4 +13,6 @@ class Base {
static String USER_AGENT = static String USER_AGENT =
"${Base.APP_NAME} ${PlatformUtil.getPlatformName()} ${Base.VERSION_NAME}"; "${Base.APP_NAME} ${PlatformUtil.getPlatformName()} ${Base.VERSION_NAME}";
static String WEB_APPS = "https://nowser.nostrmo.com/jsons/webapps.json";
} }

View File

@@ -5,6 +5,7 @@ class RouterPath {
static const String ME = "/me"; static const String ME = "/me";
static const String KEYS = "/keys"; static const String KEYS = "/keys";
static const String APPS = "/apps"; static const String APPS = "/apps";
static const String WEB_APPS = "/webApps";
static const String ADD_REMOTE_APP = "/addRemoteApp"; static const String ADD_REMOTE_APP = "/addRemoteApp";
static const String APP_DETAIL = "/appDetail"; static const String APP_DETAIL = "/appDetail";
static const String HISTORY = "/history"; static const String HISTORY = "/history";

View File

@@ -30,6 +30,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Add_to_index": MessageLookupByLibrary.simpleMessage("Add to index"), "Add_to_index": MessageLookupByLibrary.simpleMessage("Add to index"),
"Add_to_quick_action": "Add_to_quick_action":
MessageLookupByLibrary.simpleMessage("Add to quick action"), MessageLookupByLibrary.simpleMessage("Add to quick action"),
"All": MessageLookupByLibrary.simpleMessage("All"),
"Allow": MessageLookupByLibrary.simpleMessage("Allow"), "Allow": MessageLookupByLibrary.simpleMessage("Allow"),
"Alway_reject": MessageLookupByLibrary.simpleMessage("Alway reject"), "Alway_reject": MessageLookupByLibrary.simpleMessage("Alway reject"),
"Always": MessageLookupByLibrary.simpleMessage("Always"), "Always": MessageLookupByLibrary.simpleMessage("Always"),
@@ -89,6 +90,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Get_Public_Key": "Get_Public_Key":
MessageLookupByLibrary.simpleMessage("Get Public Key"), MessageLookupByLibrary.simpleMessage("Get Public Key"),
"Get_Relays": MessageLookupByLibrary.simpleMessage("Get Relays"), "Get_Relays": MessageLookupByLibrary.simpleMessage("Get Relays"),
"Group_Chat": MessageLookupByLibrary.simpleMessage("Group Chat"),
"Historys": MessageLookupByLibrary.simpleMessage("Historys"), "Historys": MessageLookupByLibrary.simpleMessage("Historys"),
"Incognito": MessageLookupByLibrary.simpleMessage("Incognito"), "Incognito": MessageLookupByLibrary.simpleMessage("Incognito"),
"Input_can_not_be_null": "Input_can_not_be_null":
@@ -103,7 +105,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Login_fail": MessageLookupByLibrary.simpleMessage("Login_fail"), "Login_fail": MessageLookupByLibrary.simpleMessage("Login_fail"),
"Login_with_Nesigner": "Login_with_Nesigner":
MessageLookupByLibrary.simpleMessage("Login with Nesigner"), MessageLookupByLibrary.simpleMessage("Login with Nesigner"),
"Long_Form": MessageLookupByLibrary.simpleMessage("Long Form"),
"Marketplaces": MessageLookupByLibrary.simpleMessage("Marketplaces"),
"Name": MessageLookupByLibrary.simpleMessage("Name"), "Name": MessageLookupByLibrary.simpleMessage("Name"),
"Notes": MessageLookupByLibrary.simpleMessage("Notes"),
"Open_backgroundly": "Open_backgroundly":
MessageLookupByLibrary.simpleMessage("Open backgroundly"), MessageLookupByLibrary.simpleMessage("Open backgroundly"),
"Open_image_in_a_New_Tab": "Open_image_in_a_New_Tab":
@@ -114,6 +119,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Open with Incognito Mode"), MessageLookupByLibrary.simpleMessage("Open with Incognito Mode"),
"Pendding_connect_remote_apps": MessageLookupByLibrary.simpleMessage( "Pendding_connect_remote_apps": MessageLookupByLibrary.simpleMessage(
"Pendding connect remote apps"), "Pendding connect remote apps"),
"Photos": MessageLookupByLibrary.simpleMessage("Photos"),
"Privacy": MessageLookupByLibrary.simpleMessage("Privacy"), "Privacy": MessageLookupByLibrary.simpleMessage("Privacy"),
"Pubkey": MessageLookupByLibrary.simpleMessage("Pubkey"), "Pubkey": MessageLookupByLibrary.simpleMessage("Pubkey"),
"Reasonable": MessageLookupByLibrary.simpleMessage("Reasonable"), "Reasonable": MessageLookupByLibrary.simpleMessage("Reasonable"),
@@ -134,9 +140,13 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("Show more logs"), MessageLookupByLibrary.simpleMessage("Show more logs"),
"Sign_Event": MessageLookupByLibrary.simpleMessage("Sign Event"), "Sign_Event": MessageLookupByLibrary.simpleMessage("Sign Event"),
"Stars": MessageLookupByLibrary.simpleMessage("Stars"), "Stars": MessageLookupByLibrary.simpleMessage("Stars"),
"Streaming": MessageLookupByLibrary.simpleMessage("Streaming"),
"ThemeStyle": MessageLookupByLibrary.simpleMessage("ThemeStyle"), "ThemeStyle": MessageLookupByLibrary.simpleMessage("ThemeStyle"),
"Tools": MessageLookupByLibrary.simpleMessage("Tools"),
"Url": MessageLookupByLibrary.simpleMessage("Url"), "Url": MessageLookupByLibrary.simpleMessage("Url"),
"WEB": MessageLookupByLibrary.simpleMessage("WEB"), "WEB": MessageLookupByLibrary.simpleMessage("WEB"),
"Web_APPs": MessageLookupByLibrary.simpleMessage("Web APPs"),
"Zaps": MessageLookupByLibrary.simpleMessage("Zaps"),
"a": MessageLookupByLibrary.simpleMessage("a"), "a": MessageLookupByLibrary.simpleMessage("a"),
"auto": MessageLookupByLibrary.simpleMessage("auto"), "auto": MessageLookupByLibrary.simpleMessage("auto"),
"detail": MessageLookupByLibrary.simpleMessage("detail"), "detail": MessageLookupByLibrary.simpleMessage("detail"),

View File

@@ -28,6 +28,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Add_bookmark": MessageLookupByLibrary.simpleMessage("添加书签"), "Add_bookmark": MessageLookupByLibrary.simpleMessage("添加书签"),
"Add_to_index": MessageLookupByLibrary.simpleMessage("添加到首页"), "Add_to_index": MessageLookupByLibrary.simpleMessage("添加到首页"),
"Add_to_quick_action": MessageLookupByLibrary.simpleMessage("添加到快捷方式"), "Add_to_quick_action": MessageLookupByLibrary.simpleMessage("添加到快捷方式"),
"All": MessageLookupByLibrary.simpleMessage("全部"),
"Allow": MessageLookupByLibrary.simpleMessage("允许"), "Allow": MessageLookupByLibrary.simpleMessage("允许"),
"Alway_reject": MessageLookupByLibrary.simpleMessage("总是拒绝"), "Alway_reject": MessageLookupByLibrary.simpleMessage("总是拒绝"),
"Always": MessageLookupByLibrary.simpleMessage("总是"), "Always": MessageLookupByLibrary.simpleMessage("总是"),
@@ -77,6 +78,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("生成一个秘钥"), MessageLookupByLibrary.simpleMessage("生成一个秘钥"),
"Get_Public_Key": MessageLookupByLibrary.simpleMessage("获取公钥"), "Get_Public_Key": MessageLookupByLibrary.simpleMessage("获取公钥"),
"Get_Relays": MessageLookupByLibrary.simpleMessage("获取中继"), "Get_Relays": MessageLookupByLibrary.simpleMessage("获取中继"),
"Group_Chat": MessageLookupByLibrary.simpleMessage("群组聊天"),
"Historys": MessageLookupByLibrary.simpleMessage("历史"), "Historys": MessageLookupByLibrary.simpleMessage("历史"),
"Incognito": MessageLookupByLibrary.simpleMessage("无痕"), "Incognito": MessageLookupByLibrary.simpleMessage("无痕"),
"Input_can_not_be_null": MessageLookupByLibrary.simpleMessage("输入不能为空"), "Input_can_not_be_null": MessageLookupByLibrary.simpleMessage("输入不能为空"),
@@ -89,7 +91,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Login_fail": MessageLookupByLibrary.simpleMessage("登录失败"), "Login_fail": MessageLookupByLibrary.simpleMessage("登录失败"),
"Login_with_Nesigner": "Login_with_Nesigner":
MessageLookupByLibrary.simpleMessage("使用 Nesigner 登录"), MessageLookupByLibrary.simpleMessage("使用 Nesigner 登录"),
"Long_Form": MessageLookupByLibrary.simpleMessage("长文本"),
"Marketplaces": MessageLookupByLibrary.simpleMessage("市场"),
"Name": MessageLookupByLibrary.simpleMessage("名称"), "Name": MessageLookupByLibrary.simpleMessage("名称"),
"Notes": MessageLookupByLibrary.simpleMessage("笔记"),
"Open_backgroundly": MessageLookupByLibrary.simpleMessage("后台打开"), "Open_backgroundly": MessageLookupByLibrary.simpleMessage("后台打开"),
"Open_image_in_a_New_Tab": "Open_image_in_a_New_Tab":
MessageLookupByLibrary.simpleMessage("新标签打开图片"), MessageLookupByLibrary.simpleMessage("新标签打开图片"),
@@ -98,6 +103,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("无痕模式打开"), MessageLookupByLibrary.simpleMessage("无痕模式打开"),
"Pendding_connect_remote_apps": "Pendding_connect_remote_apps":
MessageLookupByLibrary.simpleMessage("待链接的远程应用"), MessageLookupByLibrary.simpleMessage("待链接的远程应用"),
"Photos": MessageLookupByLibrary.simpleMessage("照片"),
"Privacy": MessageLookupByLibrary.simpleMessage("隐私"), "Privacy": MessageLookupByLibrary.simpleMessage("隐私"),
"Pubkey": MessageLookupByLibrary.simpleMessage("公钥"), "Pubkey": MessageLookupByLibrary.simpleMessage("公钥"),
"Reasonable": MessageLookupByLibrary.simpleMessage("合理的授权"), "Reasonable": MessageLookupByLibrary.simpleMessage("合理的授权"),
@@ -114,9 +120,13 @@ class MessageLookup extends MessageLookupByLibrary {
"Show_more_logs": MessageLookupByLibrary.simpleMessage("显示更多日志"), "Show_more_logs": MessageLookupByLibrary.simpleMessage("显示更多日志"),
"Sign_Event": MessageLookupByLibrary.simpleMessage("签名事件"), "Sign_Event": MessageLookupByLibrary.simpleMessage("签名事件"),
"Stars": MessageLookupByLibrary.simpleMessage("保存书签"), "Stars": MessageLookupByLibrary.simpleMessage("保存书签"),
"Streaming": MessageLookupByLibrary.simpleMessage("直播"),
"ThemeStyle": MessageLookupByLibrary.simpleMessage("主题类型"), "ThemeStyle": MessageLookupByLibrary.simpleMessage("主题类型"),
"Tools": MessageLookupByLibrary.simpleMessage("工具"),
"Url": MessageLookupByLibrary.simpleMessage("链接"), "Url": MessageLookupByLibrary.simpleMessage("链接"),
"WEB": MessageLookupByLibrary.simpleMessage("网页"), "WEB": MessageLookupByLibrary.simpleMessage("网页"),
"Web_APPs": MessageLookupByLibrary.simpleMessage("网页应用"),
"Zaps": MessageLookupByLibrary.simpleMessage("Zaps"),
"a": MessageLookupByLibrary.simpleMessage("一个"), "a": MessageLookupByLibrary.simpleMessage("一个"),
"auto": MessageLookupByLibrary.simpleMessage("自动"), "auto": MessageLookupByLibrary.simpleMessage("自动"),
"detail": MessageLookupByLibrary.simpleMessage("详情"), "detail": MessageLookupByLibrary.simpleMessage("详情"),

View File

@@ -1019,6 +1019,106 @@ class S {
args: [], args: [],
); );
} }
/// `Notes`
String get Notes {
return Intl.message(
'Notes',
name: 'Notes',
desc: '',
args: [],
);
}
/// `Long Form`
String get Long_Form {
return Intl.message(
'Long Form',
name: 'Long_Form',
desc: '',
args: [],
);
}
/// `Group Chat`
String get Group_Chat {
return Intl.message(
'Group Chat',
name: 'Group_Chat',
desc: '',
args: [],
);
}
/// `Tools`
String get Tools {
return Intl.message(
'Tools',
name: 'Tools',
desc: '',
args: [],
);
}
/// `Photos`
String get Photos {
return Intl.message(
'Photos',
name: 'Photos',
desc: '',
args: [],
);
}
/// `Streaming`
String get Streaming {
return Intl.message(
'Streaming',
name: 'Streaming',
desc: '',
args: [],
);
}
/// `Zaps`
String get Zaps {
return Intl.message(
'Zaps',
name: 'Zaps',
desc: '',
args: [],
);
}
/// `Marketplaces`
String get Marketplaces {
return Intl.message(
'Marketplaces',
name: 'Marketplaces',
desc: '',
args: [],
);
}
/// `All`
String get All {
return Intl.message(
'All',
name: 'All',
desc: '',
args: [],
);
}
/// `Web APPs`
String get Web_APPs {
return Intl.message(
'Web APPs',
name: 'Web_APPs',
desc: '',
args: [],
);
}
} }
class AppLocalizationDelegate extends LocalizationsDelegate<S> { class AppLocalizationDelegate extends LocalizationsDelegate<S> {

View File

@@ -95,5 +95,15 @@
"or": "or", "or": "or",
"Login_with_Nesigner": "Login with Nesigner", "Login_with_Nesigner": "Login with Nesigner",
"Login_fail": "Login_fail", "Login_fail": "Login_fail",
"Delete": "Delete" "Delete": "Delete",
"Notes": "Notes",
"Long_Form": "Long Form",
"Group_Chat": "Group Chat",
"Tools": "Tools",
"Photos": "Photos",
"Streaming": "Streaming",
"Zaps": "Zaps",
"Marketplaces": "Marketplaces",
"All": "All",
"Web_APPs": "Web APPs"
} }

View File

@@ -95,5 +95,15 @@
"or": "或者", "or": "或者",
"Login_with_Nesigner": "使用 Nesigner 登录", "Login_with_Nesigner": "使用 Nesigner 登录",
"Login_fail": "登录失败", "Login_fail": "登录失败",
"Delete": "删除" "Delete": "删除",
"Notes": "笔记",
"Long_Form": "长文本",
"Group_Chat": "群组聊天",
"Tools": "工具",
"Photos": "照片",
"Streaming": "直播",
"Zaps": "Zaps",
"Marketplaces": "市场",
"All": "全部",
"Web_APPs": "网页应用"
} }

View File

@@ -36,6 +36,7 @@ import 'package:nowser/router/index/index_router.dart';
import 'package:nowser/router/keys/keys_router.dart'; import 'package:nowser/router/keys/keys_router.dart';
import 'package:nowser/router/me/me_router.dart'; import 'package:nowser/router/me/me_router.dart';
import 'package:nowser/router/setting/setting_router.dart'; import 'package:nowser/router/setting/setting_router.dart';
import 'package:nowser/router/web_apps/web_apps_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';
@@ -220,6 +221,7 @@ class _MyApp extends State<MyApp> {
RouterPath.SETTING: (context) => SettingRouter(indexReload: reload), RouterPath.SETTING: (context) => SettingRouter(indexReload: reload),
RouterPath.ABOUT_ME: (context) => AboutMeRouter(), RouterPath.ABOUT_ME: (context) => AboutMeRouter(),
RouterPath.DOWNLOADS: (context) => DownloadsRouter(), RouterPath.DOWNLOADS: (context) => DownloadsRouter(),
RouterPath.WEB_APPS: (context) => WebAppsRouter(),
}; };
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(

View File

@@ -0,0 +1,23 @@
class WebAppItem {
String link;
String name;
String desc;
String? image;
List<String> types;
WebAppItem(this.link, this.name, this.desc, this.types, {this.image});
Map<String, dynamic> toJson() {
return {
"link": link,
"name": name,
"desc": desc,
"image": image,
"types": types,
};
}
}

View File

@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:nowser/router/web_apps/web_app_item.dart';
import '../../const/base.dart';
class WebAppItemComponent extends StatefulWidget {
WebAppItem item;
Function(WebAppItem item)? onTap;
WebAppItemComponent(
this.item, {
this.onTap,
});
@override
State<StatefulWidget> createState() {
return _WebAppItemComponent();
}
}
class _WebAppItemComponent extends State<WebAppItemComponent> {
double IMAGE_WIDTH = 74;
@override
Widget build(BuildContext context) {
var themeData = Theme.of(context);
var titleFontSize = themeData.textTheme.bodyLarge!.fontSize;
return GestureDetector(
onTap: () {
if (widget.onTap != null) {
widget.onTap!(widget.item);
}
},
child: Container(
padding: const EdgeInsets.only(
left: Base.BASE_PADDING,
right: Base.BASE_PADDING,
bottom: Base.BASE_PADDING,
),
child: Row(
children: [
Container(
height: IMAGE_WIDTH,
width: IMAGE_WIDTH,
clipBehavior: Clip.hardEdge,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
),
child: widget.item.image != null
? Image.network(
widget.item.image!,
height: IMAGE_WIDTH,
width: IMAGE_WIDTH,
)
: Icon(
Icons.public,
size: 70,
),
),
Expanded(
child: Container(
margin: EdgeInsets.only(left: Base.BASE_PADDING),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Text(
widget.item.name,
style: TextStyle(
fontSize: titleFontSize,
fontWeight: FontWeight.bold,
),
),
),
Container(
child: Text(
widget.item.desc,
style: TextStyle(
color: themeData.hintColor,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,10 @@
class WebAppTypes {
static String NOTES = "notes";
static String LONG_FORM = "longForm";
static String GROUP_CHAT = "groupChat";
static String TOOLS = "tools";
static String PHOTOS = "photos";
static String STREAMING = "streaming";
static String ZAPS = "zaps";
static String Marketplaces = "marketplaces";
}

View File

@@ -0,0 +1,296 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:nostr_sdk/utils/platform_util.dart';
import 'package:nostr_sdk/utils/string_util.dart';
import 'package:nowser/component/cust_state.dart';
import 'package:nowser/const/base.dart';
import 'package:nowser/const/base_consts.dart';
import 'package:nowser/main.dart';
import 'package:nowser/router/web_apps/web_app_item_component.dart';
import 'package:nowser/util/router_util.dart';
import '../../component/appbar_back_btn_component.dart';
import '../../generated/l10n.dart';
import '../../util/dio_util.dart';
import 'web_app_item.dart';
import 'web_app_types.dart';
class WebAppsRouter extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return WebAppsRouterState();
}
}
class WebAppsRouterState extends CustState<WebAppsRouter> {
late S s;
List<WebAppItem> items = [];
Map<String, int> selectedMap = {};
List<EnumObj> typeEnums = [];
@override
Widget doBuild(BuildContext context) {
s = S.of(context);
var themeData = Theme.of(context);
if (typeEnums.isEmpty) {
typeEnums.add(EnumObj(WebAppTypes.NOTES, s.Notes));
typeEnums.add(EnumObj(WebAppTypes.LONG_FORM, s.Long_Form));
typeEnums.add(EnumObj(WebAppTypes.GROUP_CHAT, s.Group_Chat));
typeEnums.add(EnumObj(WebAppTypes.TOOLS, s.Tools));
typeEnums.add(EnumObj(WebAppTypes.PHOTOS, s.Photos));
typeEnums.add(EnumObj(WebAppTypes.STREAMING, s.Streaming));
typeEnums.add(EnumObj(WebAppTypes.ZAPS, s.Zaps));
typeEnums.add(EnumObj(WebAppTypes.Marketplaces, s.Marketplaces));
}
List<Widget> list = [];
List<Widget> typeWidgetList = [];
typeWidgetList
.add(buildTypeWidget(EnumObj("all", s.All), selectedMap.isEmpty, () {
if (selectedMap.isNotEmpty) {
setState(() {
selectedMap.clear();
});
}
}));
for (var typeEnum in typeEnums) {
var selected = selectedMap[typeEnum.value] != null;
typeWidgetList.add(buildTypeWidget(typeEnum, selected, () {
if (selected) {
setState(() {
selectedMap.remove(typeEnum.value);
});
} else {
setState(() {
selectedMap[typeEnum.value] = 1;
});
}
}));
}
list.add(Container(
margin: const EdgeInsets.only(
top: Base.BASE_PADDING,
bottom: Base.BASE_PADDING,
),
child: Wrap(
children: typeWidgetList,
),
));
List<WebAppItem> showItems = [];
if (selectedMap.isNotEmpty) {
for (var item in items) {
for (var typeValue in item.types) {
if (selectedMap[typeValue] != null) {
showItems.add(item);
break;
}
}
}
} else {
showItems.addAll(items);
}
List<Widget> itemWidgetList = [];
if (PlatformUtil.isPC()) {
for (var i = 0; i < showItems.length; i += 2) {
var item = showItems[i];
if (i + 1 < showItems.length) {
var item1 = showItems[i + 1];
itemWidgetList.add(Container(
child: Row(
children: [
Expanded(child: WebAppItemComponent(item, onTap: onTap)),
Expanded(child: WebAppItemComponent(item1, onTap: onTap)),
],
),
));
} else {
itemWidgetList.add(Container(
child: Row(
children: [
Expanded(child: WebAppItemComponent(item, onTap: onTap)),
Expanded(child: Container()),
],
),
));
}
}
} else {
for (var item in showItems) {
itemWidgetList.add(WebAppItemComponent(item));
}
}
list.add(Expanded(
child: Container(
child: SingleChildScrollView(
child: Column(
children: itemWidgetList,
),
),
),
));
return Scaffold(
appBar: AppBar(
leading: AppbarBackBtnComponent(),
title: Text(
"Web APPs",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: themeData.textTheme.bodyLarge!.fontSize,
),
),
),
body: Column(
children: list,
),
);
}
void onTap(WebAppItem item) {
RouterUtil.back(context);
webProvider.addTab(url: item.link);
}
@override
Future<void> onReady(BuildContext context) async {
load();
}
Future<void> load() async {
var str = await DioUtil.getStr(Base.WEB_APPS);
if (StringUtil.isNotBlank(str)) {
var jsonList = jsonDecode(str!);
if (jsonList is List) {
items.clear();
for (var jsonObj in jsonList) {
var link = jsonObj["link"];
var name = jsonObj["name"];
var desc = jsonObj["desc"];
var types = jsonObj["types"];
var image = jsonObj["image"];
// print(link);
// print(name);
// print(desc);
// print(types);
// print(types! is List);
// print(image);
if (StringUtil.isBlank(link) ||
StringUtil.isBlank(name) ||
StringUtil.isBlank(desc) ||
types is! List) {
continue;
}
items.add(WebAppItem(
link, name, desc, types.map((item) => item.toString()).toList(),
image: image));
}
}
}
setState(() {});
}
Widget buildTypeWidget(EnumObj enumObj, bool selected, Function onTap) {
return Container(
child: GestureDetector(
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
child: Checkbox(
value: selected,
onChanged: (_) {
onTap();
},
),
),
Container(
child: Text(enumObj.name),
),
],
),
),
);
}
@override
void initState() {
super.initState();
if (items.isEmpty) {
items.add(WebAppItem(
"https://app.flotilla.social/",
"Flotilla",
"Relay chat client",
[WebAppTypes.GROUP_CHAT],
image: "https://nowser.nostrmo.com/images/apps/flotilla.png",
));
items.add(WebAppItem(
"https://www.zapplepay.com/",
"Zapplepay",
"Zap from any client 🖕",
[WebAppTypes.TOOLS],
image: "https://nowser.nostrmo.com/images/apps/zapplepay.png",
));
items.add(WebAppItem(
"https://habla.news/",
"Habla",
"A long form content client for nostr notes",
[WebAppTypes.LONG_FORM],
image: "https://nowser.nostrmo.com/images/apps/habla.png",
));
items.add(WebAppItem(
"https://listr.lol/",
"Listr",
"Create nostr lists",
[WebAppTypes.TOOLS],
image: "https://nowser.nostrmo.com/images/apps/listr.png",
));
items.add(WebAppItem(
"https://groups.nip29.com/",
"Groups",
"A relay-based NIP-29 group chat client",
[WebAppTypes.GROUP_CHAT],
image: "https://nowser.nostrmo.com/images/apps/groups.png",
));
items.add(WebAppItem(
"https://lumilumi.app/",
"lumilumi",
"Switch between full and low-data modes — a flexible Nostr web client",
[WebAppTypes.NOTES],
image: "https://nowser.nostrmo.com/images/apps/lumilumi.ico",
));
items.add(WebAppItem(
"https://iris.to/",
"Iris",
"Simple and fast web client",
[WebAppTypes.NOTES],
image: "https://nowser.nostrmo.com/images/apps/iris.png",
));
// List<Map> jsonList = [];
// for (var item in items) {
// jsonList.add(item.toJson());
// }
// log(jsonEncode(jsonList));
setState(() {});
}
}
}