From e7faeb082c9e74b96e931116fb16fe8874659942 Mon Sep 17 00:00:00 2001 From: losgif Date: Sun, 10 Jan 2021 14:11:24 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80ADD:=20server's=20list=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Runner.xcodeproj/project.pbxproj | 9 ++ lib/constant/app_strings.dart | 1 + lib/constant/app_urls.dart | 1 + lib/entity/server_entity.dart | 131 ++++++++++++++++++++ lib/models/server_model.dart | 38 ++++++ lib/models/user_model.dart | 2 +- lib/models/user_subscribe_model.dart | 2 +- lib/pages/login/login_page.dart | 2 +- lib/pages/server_list.dart | 160 ++++++++++++++----------- lib/service/server_service.dart | 17 +++ lib/utils/shared_preferences_util.dart | 10 ++ lib/widgets/server_list.dart | 59 --------- pubspec.lock | 7 ++ pubspec.yaml | 3 +- 15 files changed, 307 insertions(+), 137 deletions(-) create mode 100644 lib/entity/server_entity.dart create mode 100644 lib/models/server_model.dart create mode 100644 lib/service/server_service.dart delete mode 100644 lib/widgets/server_list.dart diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..f2872cf 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0ad1642..348913d 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -495,6 +495,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -510,6 +511,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 95WRM2QC52; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/PacketTunnel"; @@ -526,6 +528,7 @@ "$(inherited)", "$(PROJECT_DIR)/PacketTunnel/libleaf", ); + MARKETING_VERSION = 1.0.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kanshiyun.sail.PacketTunnel; @@ -550,6 +553,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 95WRM2QC52; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/PacketTunnel"; @@ -566,6 +570,7 @@ "$(inherited)", "$(PROJECT_DIR)/PacketTunnel/libleaf", ); + MARKETING_VERSION = 1.0.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kanshiyun.sail.PacketTunnel; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -587,6 +592,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements; CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 95WRM2QC52; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/PacketTunnel"; @@ -603,6 +609,7 @@ "$(inherited)", "$(PROJECT_DIR)/PacketTunnel/libleaf", ); + MARKETING_VERSION = 1.0.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.kanshiyun.sail.PacketTunnel; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -749,6 +756,7 @@ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -781,6 +789,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/lib/constant/app_strings.dart b/lib/constant/app_strings.dart index 553e72b..3615062 100644 --- a/lib/constant/app_strings.dart +++ b/lib/constant/app_strings.dart @@ -2,6 +2,7 @@ class AppStrings { static const String APP_NAME = '瞰视云加速'; static const String TOKEN = 'X-ACCESS-TOKEN'; static const String USER_INFO = 'userInfo'; + static const String USER_SUBSCRIBE = 'userInfo'; static const String ERR_NO = 'code'; static const String IS_FIRST = "isFirst"; static const String OPEN_DOOR = '开启加速服务'; diff --git a/lib/constant/app_urls.dart b/lib/constant/app_urls.dart index c5a28cc..0dbaddd 100644 --- a/lib/constant/app_urls.dart +++ b/lib/constant/app_urls.dart @@ -5,5 +5,6 @@ class AppUrls { static const String REGISTER = '$BASE_URL/passport/auth/register'; static const String USER_SUBSCRIBE = '$BASE_URL/user/getSubscribe'; static const String PLAN = '$BASE_URL/guest/plan/fetch'; + static const String SERVER = '$BASE_URL/user/server/fetch'; static const String USER_INFO = '$BASE_URL/user/info'; } diff --git a/lib/entity/server_entity.dart b/lib/entity/server_entity.dart new file mode 100644 index 0000000..f868f68 --- /dev/null +++ b/lib/entity/server_entity.dart @@ -0,0 +1,131 @@ +// To parse this JSON data, do +// +// final serverEntity = serverEntityFromMap(jsonString); + +import 'package:meta/meta.dart'; +import 'dart:convert'; + +List serverEntityFromList(List data) => List.from(data.map((x) => ServerEntity.fromMap(x))); + +class ServerEntity { + ServerEntity({ + @required this.id, + @required this.groupId, + @required this.parentId, + @required this.tags, + @required this.name, + @required this.rate, + @required this.host, + @required this.port, + @required this.serverPort, + @required this.cipher, + @required this.show, + @required this.sort, + @required this.createdAt, + @required this.updatedAt, + @required this.type, + @required this.link, + @required this.lastCheckAt, + }); + + final int id; + final List groupId; + final dynamic parentId; + final List tags; + final String name; + final String rate; + final String host; + final int port; + final int serverPort; + final String cipher; + final int show; + final dynamic sort; + final DateTime createdAt; + final DateTime updatedAt; + final String type; + final String link; + dynamic lastCheckAt; + + ServerEntity copyWith({ + int id, + List groupId, + dynamic parentId, + List tags, + String name, + String rate, + String host, + int port, + int serverPort, + String cipher, + int show, + dynamic sort, + DateTime createdAt, + DateTime updatedAt, + String type, + String link, + dynamic lastCheckAt, + }) => + ServerEntity( + id: id ?? this.id, + groupId: groupId ?? this.groupId, + parentId: parentId ?? this.parentId, + tags: tags ?? this.tags, + name: name ?? this.name, + rate: rate ?? this.rate, + host: host ?? this.host, + port: port ?? this.port, + serverPort: serverPort ?? this.serverPort, + cipher: cipher ?? this.cipher, + show: show ?? this.show, + sort: sort ?? this.sort, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + type: type ?? this.type, + link: link ?? this.link, + lastCheckAt: lastCheckAt ?? this.lastCheckAt, + ); + + factory ServerEntity.fromJson(String str) => ServerEntity.fromMap(json.decode(str)); + + String toJson() => json.encode(toMap()); + + factory ServerEntity.fromMap(Map map) => ServerEntity( + id: map["id"] == null ? null : map["id"], + groupId: map["group_id"] == null ? null : List.from(json.decode(map["group_id"]).map((x) => x)), + parentId: map["parent_id"], + tags: map["tags"] == null ? null : List.from(json.decode(map["tags"]).map((x) => x)), + name: map["name"] == null ? null : map["name"], + rate: map["rate"] == null ? null : map["rate"], + host: map["host"] == null ? null : map["host"], + port: map["port"] == null ? null : map["port"], + serverPort: map["server_port"] == null ? null : map["server_port"], + cipher: map["cipher"] == null ? null : map["cipher"], + show: map["show"] == null ? null : map["show"], + sort: map["sort"], + createdAt: map["created_at"] == null ? null : DateTime.parse(map["created_at"]), + updatedAt: map["updated_at"] == null ? null : DateTime.parse(map["updated_at"]), + type: map["type"] == null ? null : map["type"], + link: map["link"] == null ? null : map["link"], + lastCheckAt: map["last_check_at"], + ); + + Map toMap() => { + "id": id == null ? null : id, + "group_id": groupId == null ? null : List.from(groupId.map((x) => x)), + "parent_id": parentId, + "tags": tags == null ? null : List.from(tags.map((x) => x)), + "name": name == null ? null : name, + "rate": rate == null ? null : rate, + "host": host == null ? null : host, + "port": port == null ? null : port, + "server_port": serverPort == null ? null : serverPort, + "cipher": cipher == null ? null : cipher, + "show": show == null ? null : show, + "sort": sort, + "created_at": createdAt == null ? null : createdAt.toIso8601String(), + "updated_at": updatedAt == null ? null : updatedAt.toIso8601String(), + "type": type == null ? null : type, + "link": link == null ? null : link, + "last_check_at": lastCheckAt, + }; +} diff --git a/lib/models/server_model.dart b/lib/models/server_model.dart new file mode 100644 index 0000000..fa3c075 --- /dev/null +++ b/lib/models/server_model.dart @@ -0,0 +1,38 @@ +import 'package:sail_app/constant/app_strings.dart'; +import 'package:sail_app/entity/server_entity.dart'; +import 'package:sail_app/models/base_model.dart'; +import 'package:sail_app/service/server_service.dart'; +import 'package:sail_app/utils/shared_preferences_util.dart'; + +class ServerModel extends BaseModel { + List _serverEntityList; + + ServerService _serverService = ServerService(); + + List get serverEntityList => _serverEntityList; + + Future getServer() async { + bool result = false; + + List serverEntityList = await _serverService.server(); + setServerEntityList(serverEntityList); + + notifyListeners(); + + result = true; + + return result; + } + + setServerEntityList(List serverEntityList) { + _serverEntityList = serverEntityList; + + _saveServerEntityList(); + } + + _saveServerEntityList() async { + SharedPreferencesUtil sharedPreferencesUtil = SharedPreferencesUtil.getInstance(); + + await sharedPreferencesUtil.setList(AppStrings.USER_INFO, _serverEntityList); + } +} diff --git a/lib/models/user_model.dart b/lib/models/user_model.dart index fa5e119..ec61482 100644 --- a/lib/models/user_model.dart +++ b/lib/models/user_model.dart @@ -36,7 +36,7 @@ class UserModel extends BaseModel { SharedPreferencesUtil sharedPreferencesUtil = SharedPreferencesUtil.getInstance(); sharedPreferencesUtil.clear(); - setIsFirst(false); + // setIsFirst(false); refreshData(); } diff --git a/lib/models/user_subscribe_model.dart b/lib/models/user_subscribe_model.dart index f65754b..b4ae04b 100644 --- a/lib/models/user_subscribe_model.dart +++ b/lib/models/user_subscribe_model.dart @@ -33,6 +33,6 @@ class UserSubscribeModel extends BaseModel { _saveUserSubscribe() async { SharedPreferencesUtil sharedPreferencesUtil = SharedPreferencesUtil.getInstance(); - await sharedPreferencesUtil.setMap(AppStrings.USER_INFO, _userSubscribeEntity.toMap()); + await sharedPreferencesUtil.setMap(AppStrings.USER_SUBSCRIBE, _userSubscribeEntity.toMap()); } } diff --git a/lib/pages/login/login_page.dart b/lib/pages/login/login_page.dart index b7fa7a3..fb4e599 100644 --- a/lib/pages/login/login_page.dart +++ b/lib/pages/login/login_page.dart @@ -73,7 +73,7 @@ class LoginPage extends StatelessWidget { return FlutterLogin( title: AppStrings.APP_NAME, - logo: 'assets/usa.png', + logo: 'assets/logo.png', onLogin: _login, onSignup: _register, messages: LoginMessages( diff --git a/lib/pages/server_list.dart b/lib/pages/server_list.dart index e9bfee4..90805e7 100644 --- a/lib/pages/server_list.dart +++ b/lib/pages/server_list.dart @@ -1,6 +1,10 @@ -import 'package:circular_check_box/circular_check_box.dart'; -import 'package:sail_app/widgets/server_list.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_tags/flutter_tags.dart'; +import 'package:sail_app/constant/app_colors.dart'; +import 'package:sail_app/entity/server_entity.dart'; +import 'package:sail_app/service/server_service.dart'; import 'package:flutter/material.dart'; +import 'package:sail_app/utils/navigator_util.dart'; class ServerListPage extends StatefulWidget { @override @@ -8,25 +12,29 @@ class ServerListPage extends StatefulWidget { } class _ServerListPageState extends State { - final premiumServers = [ - ['assets/england.png', 'England'], - ['assets/usa.png', 'United States'], - ['assets/canada.png', 'Canada'], - ['assets/france.png', 'France'], - ['assets/ghana.png', 'Ghana'], - ]; + List _servers; + + @override + void initState() { + super.initState(); + + _onLoadData(); + } + + _onLoadData() async { + List servers = await ServerService().server(); + + setState(() { + _servers = servers; + }); + } - List freeServers = [ - ['assets/england.png', 'England',false], - ['assets/france.png', 'France',false], - ['assets/ghana.png', 'Ghana',false], - ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( - 'Servers', + '节点列表', style: Theme.of(context) .textTheme .headline6 @@ -39,60 +47,26 @@ class _ServerListPageState extends State { children: [ RichText( text: TextSpan( - text: 'Premuim ', + text: '请选择 ', style: Theme.of(context) .textTheme .subtitle2 .copyWith(fontWeight: FontWeight.w700), children: [ - TextSpan( - text: 'Servers', - style: Theme.of(context) - .textTheme - .subtitle2 - .copyWith(fontWeight: FontWeight.normal)) - ])), + TextSpan( + text: '节点', + style: Theme.of(context) + .textTheme + .subtitle2 + .copyWith(fontWeight: FontWeight.normal)) + ])), SizedBox( height: 20, ), Container( child: ListView.separated( shrinkWrap: true, - itemCount: premiumServers.length, - itemBuilder: (_, index) { - return ServerItemWidget( - isFaded: true, - label: premiumServers[index][1], - icon: Icons.lock, - flagAsset: premiumServers[index][0], - onTap: () {}); - }, - separatorBuilder: (_, index) => SizedBox(height: 10), - ), - ), - SizedBox(height: 30), - RichText( - text: TextSpan( - text: 'Free ', - style: Theme.of(context) - .textTheme - .subtitle2 - .copyWith(fontWeight: FontWeight.w700), - children: [ - TextSpan( - text: 'Servers', - style: Theme.of(context) - .textTheme - .subtitle2 - .copyWith(fontWeight: FontWeight.normal)) - ])), - SizedBox( - height: 20, - ), - Container( - child: ListView.separated( - shrinkWrap: true, - itemCount: freeServers.length, + itemCount: _servers?.length ?? 0, itemBuilder: (_, index) { return Material( borderRadius: BorderRadius.circular(10), @@ -106,31 +80,71 @@ class _ServerListPageState extends State { Wrap( crossAxisAlignment: WrapCrossAlignment.center, children: [ + SizedBox( + width: ScreenUtil().setWidth(10), + ), CircleAvatar( - radius: 15, - backgroundColor: Colors.white, - backgroundImage: ExactAssetImage( - freeServers[index][0], - ), + radius: ScreenUtil().setWidth(10), + backgroundColor: + ((_servers[index].lastCheckAt ?? 0) * 1000 > + DateTime.now() + .toLocal() + .microsecondsSinceEpoch + + (1000 * 60)) + ? Colors.green + : Colors.red, ), SizedBox( width: 15, ), Text( - freeServers[index][1], + _servers[index].name, style: Theme.of(context).textTheme.bodyText1, ), + SizedBox( + width: 15, + ), + Tags( + itemCount: _servers[index].tags.length, + // required + itemBuilder: (int i) { + final item = _servers[index].tags[i]; + + return ItemTags( + // Each ItemTags must contain a Key. Keys allow Flutter to + // uniquely identify widgets. + index: index, + // required + color: AppColors.THEME_COLOR, + activeColor: AppColors.THEME_COLOR, + textColor: Colors.black87, + textActiveColor: Colors.black87, + title: item, + textStyle: TextStyle( + fontSize: ScreenUtil().setSp(24)), + onPressed: (item) => print(item), + onLongPressed: (item) => print(item), + ); + }) ], ), - CircularCheckBox( - activeColor: Color.fromRGBO(37, 112, 252, 1), - value: freeServers[index][2], - materialTapTargetSize: MaterialTapTargetSize.padded, - onChanged: (bool x) { - setState(() { - freeServers[index][2] = !freeServers[index][2]; - }); - }) + InkWell( + borderRadius: + BorderRadius.circular(ScreenUtil().setWidth(30)), + onTap: () { + NavigatorUtil.goBack(context); + }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: ScreenUtil().setWidth(10), + horizontal: ScreenUtil().setWidth(30)), + child: Text( + '选择', + style: TextStyle( + color: Colors.yellow[800], + fontWeight: FontWeight.w500), + )), + ) ], ), ), diff --git a/lib/service/server_service.dart b/lib/service/server_service.dart new file mode 100644 index 0000000..8b0c45c --- /dev/null +++ b/lib/service/server_service.dart @@ -0,0 +1,17 @@ +import 'package:sail_app/constant/app_urls.dart'; +import 'package:sail_app/entity/server_entity.dart'; +import 'package:sail_app/utils/http_util.dart'; + +class ServerService { + Future> server() async { + try { + var result = await HttpUtil.instance.get(AppUrls.SERVER); + + List _serverEntityList = serverEntityFromList(result['data']); + + return _serverEntityList; + } catch (err) { + return Future.error(err); + } + } +} diff --git a/lib/utils/shared_preferences_util.dart b/lib/utils/shared_preferences_util.dart index 2b000e7..eca9532 100644 --- a/lib/utils/shared_preferences_util.dart +++ b/lib/utils/shared_preferences_util.dart @@ -70,6 +70,11 @@ class SharedPreferencesUtil { return sharedPreferences.setString(tag, jsonEncode(data)); } + Future setList(String tag, List data) async { + SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); + return sharedPreferences.setString(tag, jsonEncode(data)); + } + Future getBool(String tag) async { SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); return sharedPreferences.getBool(tag); @@ -85,6 +90,11 @@ class SharedPreferencesUtil { return jsonDecode(sharedPreferences.getString(tag) ?? '{}'); } + Future> getList(String tag) async { + SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); + return jsonDecode(sharedPreferences.getString(tag) ?? '{}'); + } + Future clear() async { SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); return sharedPreferences.clear(); diff --git a/lib/widgets/server_list.dart b/lib/widgets/server_list.dart deleted file mode 100644 index 3b10a62..0000000 --- a/lib/widgets/server_list.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/material.dart'; - -class ServerItemWidget extends StatelessWidget { - const ServerItemWidget( - {Key key, - @required this.label, - @required this.icon, - @required this.flagAsset, - @required this.onTap, this.isFaded = false}) - : super(key: key); - - final String label; - final IconData icon; - final String flagAsset; - final Function onTap; - final isFaded; - - @override - Widget build(BuildContext context) { - return Material( - borderRadius: BorderRadius.circular(10), - color: Theme.of(context).cardColor, - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - CircleAvatar( - radius: 15, - backgroundColor: Colors.white, - backgroundImage: ExactAssetImage( - flagAsset, - ), - ), - SizedBox( - width: 15, - ), - Text( - label, - style: Theme.of(context).textTheme.bodyText1, - ), - ], - ), - IconButton( - icon: Icon(icon), - onPressed: onTap, - iconSize: 18, - color: isFaded ? Colors.grey : Theme.of(context).iconTheme.color, - ) - ], - ), - ), - ); - } -} diff --git a/pubspec.lock b/pubspec.lock index dbb2631..d0be126 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -158,6 +158,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.6" + flutter_tags: + dependency: "direct main" + description: + name: flutter_tags + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.9+1" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 9529034..dd354ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 1.0.0+2 environment: sdk: ">=2.7.0 <3.0.0" @@ -40,6 +40,7 @@ dependencies: flutter_login: ^1.0.14 flutter_easyrefresh: ^2.1.8 intl: ^0.16.1 + flutter_tags: "^0.4.9+1" dev_dependencies: flutter_test: