diff --git a/lib/timetable/timetable_menu/timetable_menu_view.dart b/lib/timetable/timetable_menu/timetable_menu_view.dart index 6c2988cd..2cc6ed5a 100644 --- a/lib/timetable/timetable_menu/timetable_menu_view.dart +++ b/lib/timetable/timetable_menu/timetable_menu_view.dart @@ -3,9 +3,11 @@ import 'package:flutter_settings_screens/flutter_settings_screens.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; +import '../../utils/database.dart'; import '../../utils/gu.dart'; import '../../utils/string.dart'; import '../../utils/web.dart'; +import '../ecnu/ecnu_logic.dart'; import '../ecnu/ecnu_view.dart'; import 'timetable_menu_logic.dart'; @@ -90,13 +92,20 @@ class TimetableMenuPage extends StatelessWidget { context: context, tiles: [ ListTile( - title: const Text('壁纸'), - subtitle: Text('🕊' * 5), - trailing: const FaIcon(FontAwesomeIcons.dove), - onTap: gu, + title: Text('ICS文件'.s), + subtitle: + user.id == null ? const Text('需先登录公共数据库') : null, + enabled: user.id != null, + trailing: const FaIcon(FontAwesomeIcons.fileExport), + onTap: Url.ics( + user.id!, + user.password!, + EcnuLogic.guessYear(DateTime.now()), + EcnuLogic.guessSemester(DateTime.now()), + ).launch, ), ListTile( - title: const Text('ics'), + title: const Text('壁纸'), subtitle: Text('🕊' * 5), trailing: const FaIcon(FontAwesomeIcons.dove), onTap: gu, diff --git a/lib/toolbox/toolbox_menu/toolbox_menu_view.dart b/lib/toolbox/toolbox_menu/toolbox_menu_view.dart index 3f0da9aa..6d6712a4 100644 --- a/lib/toolbox/toolbox_menu/toolbox_menu_view.dart +++ b/lib/toolbox/toolbox_menu/toolbox_menu_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive_flutter/hive_flutter.dart'; -import 'package:quiver/iterables.dart'; import '../../utils/database.dart'; import '../toolbox_view.dart'; @@ -15,8 +14,6 @@ class ToolboxMenuPage extends StatelessWidget { @override Widget build(BuildContext context) { - final tools_ = tools(); - return Scaffold( appBar: AppBar( title: const Text('工具箱'), @@ -27,10 +24,7 @@ class ToolboxMenuPage extends StatelessWidget { child: ValueListenableBuilder( valueListenable: conf.listenable(keys: ['toolbox']), builder: (_, __, ___) => ReorderableListView( - children: range(tools_.length) - .cast() - .map((i) => tools_[toolbox.order[i]].toTile()) - .toList(), + children: tools.map((e) => e.toTile()).toList(), onReorder: toolbox.reorder, ), ), diff --git a/lib/toolbox/toolbox_view.dart b/lib/toolbox/toolbox_view.dart index 4878affc..028f3476 100644 --- a/lib/toolbox/toolbox_view.dart +++ b/lib/toolbox/toolbox_view.dart @@ -20,8 +20,6 @@ class ToolboxPage extends StatelessWidget { @override Widget build(BuildContext context) { - final tools_ = tools(logic); - return ValueListenableBuilder( valueListenable: conf.listenable(keys: ['toolbox']), builder: (_, __, ___) => GridView.extent( @@ -29,95 +27,95 @@ class ToolboxPage extends StatelessWidget { childAspectRatio: pi, crossAxisSpacing: 16, mainAxisSpacing: 16, - children: range(tools_.length) - .cast() - .map((i) => tools_[toolbox.order[i]].toCard()) - .toList(), + children: tools.map((e) => e.toCard()).toList(), ), ); } } -List tools([ToolboxLogic? logic]) => [ - Tool( - FontAwesomeIcons.dog, - '/sucker', - '', - subtitleWidget: logic == null - ? null - : Obx( - () => logic.sucker.isEmpty - ? Loading() - : Text( - logic.sucker.value.length < 16 - ? logic.sucker.value - : logic.sucker.value.substring(0, 16) + '……', - ), +List get tools { + final logic = Get.find(); + + final tools_ = [ + Tool( + FontAwesomeIcons.dog, + '/sucker', + Obx( + () => logic.sucker.isEmpty + ? Loading() + : Text( + logic.sucker.value.length < 16 + ? logic.sucker.value + : logic.sucker.value.substring(0, 16) + '……', ), - onTap: logic?.suckerOnTap, - enabled: !GetPlatform.isWeb, ), - Tool( - FontAwesomeIcons.cat, - '/cheater', - '', - subtitleWidget: logic == null - ? null - : Obx( - () => logic.cheater.isEmpty - ? Loading() - : Text( - logic.cheater.value.length < 16 - ? logic.cheater.value - : logic.cheater.value.substring(0, 16) + '……', - ), + onTap: logic.suckerOnTap, + enabled: !GetPlatform.isWeb, + ), + Tool( + FontAwesomeIcons.cat, + '/cheater', + Obx( + () => logic.cheater.isEmpty + ? Loading() + : Text( + logic.cheater.value.length < 16 + ? logic.cheater.value + : logic.cheater.value.substring(0, 16) + '……', ), - onTap: logic?.cheaterOnTap, - ), - Tool( - FontAwesomeIcons.graduationCap, - '卷课意愿值估算', - '仅供参考', - onTap: logic?.juanOnTap, - ), - Tool( - FontAwesomeIcons.calendarAlt, - '校历', - '长按打开网页版', - onTap: logic?.calendarOnTap, - onLongPress: Url.calendar.launch, - ), - Tool( - FontAwesomeIcons.scroll, - '公告', - '善用搜索', - onTap: Url.announcements.launch, - ), - Tool( - FontAwesomeIcons.mapMarked, - '校内地图', - '2D/3D', - onTap: Url.map.launch, ), - Tool( - FontAwesomeIcons.bus, - '校车时刻表', - '需要连学校Wifi/VPN'.s, - onTap: Url.bus.launch, - ), - Tool( - FontAwesomeIcons.cube, - 'ECNU软件镜像站'.s, - '内容很少', - onTap: Url.mirrors.launch, - ), - Tool( - FontAwesomeIcons.key, - '学校VPN'.s, - '对校外网站有减速作用', - onTap: Url.vpn.launch, - ), - ]; + onTap: logic.cheaterOnTap, + ), + Tool( + FontAwesomeIcons.graduationCap, + '卷课意愿值估算', + const Text('仅供参考'), + onTap: logic.juanOnTap, + ), + Tool( + FontAwesomeIcons.calendarAlt, + '校历', + const Text('长按打开网页版'), + onTap: logic.calendarOnTap, + onLongPress: Url.calendar.launch, + ), + Tool( + FontAwesomeIcons.scroll, + '公告', + const Text('善用搜索'), + onTap: Url.announcements.launch, + ), + Tool( + FontAwesomeIcons.mapMarked, + '校内地图', + const Text('2D/3D'), + onTap: Url.map.launch, + ), + Tool( + FontAwesomeIcons.bus, + '校车时刻表', + Text('需要连学校Wifi/VPN'.s), + onTap: Url.bus.launch, + ), + Tool( + FontAwesomeIcons.cube, + 'ECNU软件镜像站'.s, + const Text('内容很少'), + onTap: Url.mirrors.launch, + ), + Tool( + FontAwesomeIcons.key, + '学校VPN'.s, + const Text('对校外网站有减速作用'), + onTap: Url.vpn.launch, + ), + ]; + + return range(tools_.length) + .cast() + .map((i) => tools_[toolbox.order[i]]) + .toList(growable: false); +} class Tool { const Tool( @@ -127,13 +125,11 @@ class Tool { this.onTap, this.onLongPress, this.enabled = true, - this.subtitleWidget, }); final IconData leading; final String title; - final String subtitle; - final Widget? subtitleWidget; + final Widget subtitle; final void Function()? onTap; final void Function()? onLongPress; final bool enabled; @@ -145,7 +141,6 @@ class Tool { onTap: onTap, onLongPress: onLongPress, enabled: enabled, - subtitleWidget: subtitleWidget, ); ToolTile toTile() => ToolTile( @@ -164,13 +159,11 @@ class ToolCard extends StatelessWidget { this.onTap, this.onLongPress, this.enabled = true, - this.subtitleWidget, }) : super(key: key); final IconData leading; final String title; - final String subtitle; - final Widget? subtitleWidget; + final Widget subtitle; final void Function()? onTap; final void Function()? onLongPress; final bool enabled; @@ -191,7 +184,7 @@ class ToolCard extends StatelessWidget { child: ListTile( leading: FaIcon(leading), title: Text(title), - subtitle: subtitleWidget ?? Text(subtitle), + subtitle: subtitle, mouseCursor: MouseCursor.uncontrolled, ), ), diff --git a/lib/utils/string.dart b/lib/utils/string.dart index c9c16aa2..89a4288e 100644 --- a/lib/utils/string.dart +++ b/lib/utils/string.dart @@ -7,8 +7,8 @@ import 'pangu.dart'; // record them manually const appName = 'ECNU Timetable'; const packageName = 'io.github.ccxxxi.ecnu_timetable'; -const version = '1.2.0'; -const buildNumber = '21'; +const version = '1.3.0'; +const buildNumber = '22'; const release = '$packageName@$version+$buildNumber'; diff --git a/lib/utils/web.dart b/lib/utils/web.dart index a1c8588f..0f3a2d5b 100644 --- a/lib/utils/web.dart +++ b/lib/utils/web.dart @@ -77,5 +77,10 @@ class Url { static const captcha = '$_cas/code'; static const ids = '$_eams/courseTableForStd!index.action'; static const table = '$_eams/courseTableForStd!courseTable.action'; + // endregion + + static String ics(String id, String password, int year, int semester) => + 'http://application.jjaychen.me/ecnu-service/course-calendar' + '?username=$id&password=$password&year=$year&semesterIndex=${semester + 1}'; } diff --git a/pubspec.yaml b/pubspec.yaml index fb986222..1cd65dd3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ description: 更美观更智能的ECNU课程表。 # Prevent the package from being accidentally published to pub.dev. publish_to: "none" -version: 1.2.0+21 +version: 1.3.0+22 environment: sdk: ">=2.15.0-172.0.dev" @@ -60,7 +60,7 @@ msix_config: display_name: "ECNU Timetable" publisher_display_name: "CCXXXI" identity_name: "io.github.ccxxxi.ecnu-timetable" - msix_version: 1.2.0.21 + msix_version: 1.3.0.22 logo_path: "assets/images/app_icon.png" flutter: diff --git a/test/utils/web_test.dart b/test/utils/web_test.dart index f6eb8910..7d2a0c31 100644 --- a/test/utils/web_test.dart +++ b/test/utils/web_test.dart @@ -25,4 +25,13 @@ void main() { 'https://github.com/CCXXXI/ecnu_timetable/releases/tag/v0.1.0', ), ); + + test( + 'ics', + () => expect( + Url.ics('10101001000', 'abc', 2021, 0), + 'http://application.jjaychen.me/ecnu-service/course-calendar' + '?username=10101001000&password=abc&year=2021&semesterIndex=1', + ), + ); }