diff --git a/lib/common/pubspec.yaml.g.dart b/lib/common/pubspec.yaml.g.dart index 52e45542..bf080c14 100644 --- a/lib/common/pubspec.yaml.g.dart +++ b/lib/common/pubspec.yaml.g.dart @@ -58,7 +58,7 @@ const List pre = []; const List build = [r'336']; /// Build date in Unix Time (in seconds) -const int timestamp = 1689749046; +const int timestamp = 1691072369; /// Name [name] const String name = r'dan_xi'; @@ -83,7 +83,7 @@ const String publishTo = r'none'; /// Environment const Map environment = { - 'sdk': '>=2.15.0 <3.0.0', + 'sdk': '>=3.0.0', }; /// Dependencies @@ -182,7 +182,7 @@ const Map dependencies = { 'screen_capture_event': r'^1.0.0+1', 'otp': r'^3.0.2', 'lunar': r'^1.2.20', - 'flutter_fgbg': r'^0.2.0', + 'flutter_fgbg': r'^0.3.0', 'lazy_load_indexed_stack': r'^1.0.0', 'js': r'^0.6.5', 'nil': r'^1.1.1', @@ -209,6 +209,7 @@ const Map devDependencies = { 'sdk': r'flutter', }, 'flutter_lints': r'^2.0.1', + 'intl_utils': r'^2.8.3', }; /// Dependency overrides diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 6f46d823..7594558d 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -495,5 +495,11 @@ "copy_floor_id": "Copy Floor ID", "copy_floor_id_success": "Copy Floor ID success! Please check your clipboard.", "copy_hole_id": "Copy Hole ID", - "copy_hole_id_success": "Copy Hole ID success! Please check your clipboard." + "copy_hole_id_success": "Copy Hole ID success! Please check your clipboard.", + "choose_semester": "Choose semester:", + "choose_semester_message": "Select the semester you want to view.", + "manually_add_course":"Add lesson manually:", + "manually_add_course_message":"If there is a class that is not included in the class schedule, but you want to check it, add it manually yourself.", + "start_date_select": "Semester start date query", + "start_date_select_message": "You must select the start date of the semester to view your class schedule. Not selecting will result in a date error. \nTip: Start date is the Monday of the first week." } \ No newline at end of file diff --git a/lib/l10n/intl_zh_CN.arb b/lib/l10n/intl_zh_CN.arb index 8dfe7311..adb12000 100644 --- a/lib/l10n/intl_zh_CN.arb +++ b/lib/l10n/intl_zh_CN.arb @@ -487,5 +487,11 @@ "copy_floor_id": "拷贝楼层号", "copy_floor_id_success": "楼层号已保存至剪贴板。", "copy_hole_id": "拷贝洞号", - "copy_hole_id_success": "洞号已保存至剪贴板。" + "copy_hole_id_success": "洞号已保存至剪贴板。", + "choose_semester": "学期选择:", + "choose_semester_message": "选择你想查看的学期", + "manually_add_course":"手动添加课程:", + "manually_add_course_message":"是否有课未收录在课表上,但你又想查看的呢,自己手动添加它吧。", + "start_date_select": "学期开始日期查询:", + "start_date_select_message": "必须选择学期的开始日期才能查看你的课表。不选择将导致日期错误。\n提示:开始日期是第一周的周一。" } diff --git a/lib/page/subpage_timetable.dart b/lib/page/subpage_timetable.dart index 2a6468a4..8560ceb5 100644 --- a/lib/page/subpage_timetable.dart +++ b/lib/page/subpage_timetable.dart @@ -16,6 +16,7 @@ */ import 'dart:io'; +import 'dart:ui'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:dan_xi/common/constant.dart'; @@ -52,9 +53,14 @@ import 'package:open_file/open_file.dart'; import 'package:path_provider/path_provider.dart'; import 'package:provider/provider.dart'; import 'package:share_plus/share_plus.dart'; +import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; import '../widget/dialogs/manually_add_course_dialog.dart'; +GlobalKey keyButton = GlobalKey(); +GlobalKey keyButton1 = GlobalKey(); +GlobalKey keyButton2 = GlobalKey(); + const kCompatibleUserGroup = [ UserGroup.FUDAN_UNDERGRADUATE_STUDENT, UserGroup.FUDAN_POSTGRADUATE_STUDENT @@ -73,9 +79,10 @@ class TimetableSubPage extends PlatformSubpage { Create> get trailing => (cxt) => [ AppBarButtonItem( S.of(cxt).add_courses, - Icon(PlatformX.isMaterial(cxt) - ? Icons.add - : CupertinoIcons.add_circled), + Icon( + PlatformX.isMaterial(cxt) ? Icons.add : CupertinoIcons.add_circled, + key: keyButton, + ), () => ManuallyAddCourseEvent().fire(), ), AppBarButtonItem( @@ -89,11 +96,17 @@ class TimetableSubPage extends PlatformSubpage { @override Create> get leading => (cxt) => [ - AppBarButtonItem(S.of(cxt).select_semester, SemesterSelectionButton( - onSelectionUpdate: () { - timetablePageKey.currentState?.indicatorKey.currentState?.show(); - }, - ), null, useCustomWidget: true), + AppBarButtonItem( + S.of(cxt).select_semester, + SemesterSelectionButton( + key: keyButton1, + onSelectionUpdate: () { + timetablePageKey.currentState?.indicatorKey.currentState + ?.show(); + }, + ), + null, + useCustomWidget: true), ]; } @@ -243,11 +256,14 @@ class TimetableSubPageState extends PlatformSubpageState { @override void initState() { + if (!SettingsProvider.getInstance().hasVisitedTimeTable) { + WidgetsBinding.instance + .addPostFrameCallback((_) => createTutorial().show(context: context)); + SettingsProvider.getInstance().hasVisitedTimeTable = true; + } super.initState(); _setContent(); - converters = { - S.current.share_as_ics: ICSConverter() - }; + converters = {S.current.share_as_ics: ICSConverter()}; _shareSubscription.bindOnlyInvalid( Constant.eventBus.on().listen((_) { if (_table == null) return; @@ -283,6 +299,88 @@ class TimetableSubPageState extends PlatformSubpageState { hashCode); } + TutorialCoachMark createTutorial() => TutorialCoachMark( + targets: _createTargets(), + colorShadow: const Color.fromARGB(255, 9, 110, 192), + textSkip: S.of(context).skip, + paddingFocus: 10, + opacityShadow: 0.5, + pulseAnimationDuration: const Duration(milliseconds: 1000), + imageFilter: ImageFilter.blur(sigmaX: 8, sigmaY: 8), + ); + + List _createTargets() => [ + _createTarget( + identify: "SemesterSelectionButton", + keyTarget: keyButton1, + title: S.of(context).choose_semester, + message: S.of(context).choose_semester_message, + hasActionWidget: false, + ), + _createTarget( + identify: "ManuallyAddCourseButton", + keyTarget: keyButton, + title: S.of(context).manually_add_course, + message: S.of(context).manually_add_course_message, + hasActionWidget: true, + shape: ShapeLightFocus.RRect, + ), + _createTarget( + identify: "StartDateSelectionButton", + keyTarget: keyButton2, + title: S.of(context).start_date_select, + message: S.of(context).start_date_select_message, + hasActionWidget: true, + ), + ]; + + TargetFocus _createTarget({ + required String identify, + required GlobalKey keyTarget, + required String title, + required String message, + ShapeLightFocus shape = ShapeLightFocus.Circle, + double radius = 5, + bool? hasActionWidget, + }) => + TargetFocus( + identify: identify, + keyTarget: keyTarget, + contents: [ + TargetContent( + align: ContentAlign.bottom, + builder: (context, controller) => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 20.0, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Text( + message, + style: const TextStyle(color: Colors.white), + ), + ), + if (hasActionWidget != false) + ElevatedButton( + onPressed: controller.previous, + child: const Icon(Icons.chevron_left), + ), + ], + ), + ), + ], + shape: shape, + radius: radius, + ); + Future refresh( {bool reloadWhenEmptyData = false, bool forceReloadFromRemote = false}) async { @@ -343,7 +441,8 @@ class TimetableSubPageState extends PlatformSubpageState { padding: const EdgeInsets.all(16.0), child: Row( children: [ - Expanded(child: Column( + Expanded( + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( @@ -382,7 +481,8 @@ class TimetableSubPageState extends PlatformSubpageState { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, - children: block.event.map((e) => _buildCourseItem(e,context)).toList(), + children: + block.event.map((e) => _buildCourseItem(e, context)).toList(), ), ), ); @@ -401,8 +501,8 @@ class TimetableSubPageState extends PlatformSubpageState { if (_showingTime!.weekday < 0) _showingTime!.weekday = 0; if (_showingTime!.weekday > 6) _showingTime!.weekday = 6; - final List scheduleData = _table!.toDayEvents(_showingTime!.week, - compact: TableDisplayType.STANDARD); + final List scheduleData = _table! + .toDayEvents(_showingTime!.week, compact: TableDisplayType.STANDARD); return RefreshIndicator( key: indicatorKey, edgeOffset: MediaQuery.of(context).padding.top, @@ -442,6 +542,7 @@ class TimetableSubPageState extends PlatformSubpageState { Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Text(S.of(context).semester_start_date), StartDateSelectionButton( + key: keyButton2, onUpdate: (() => indicatorKey.currentState?.show())), ]), ], diff --git a/lib/provider/settings_provider.dart b/lib/provider/settings_provider.dart index b4b951d0..88133ae0 100644 --- a/lib/provider/settings_provider.dart +++ b/lib/provider/settings_provider.dart @@ -78,6 +78,7 @@ class SettingsProvider with ChangeNotifier { static const String KEY_HIDDEN_NOTIFICATIONS = "hidden_notifications"; static const String KEY_THEME_TYPE = "theme_type"; static const String KEY_MARKDOWN_ENABLED = "markdown_rendering_enabled"; + static const String KEY_VISITED_TIMETABLE = "visited_timetable"; SettingsProvider._(); @@ -176,6 +177,18 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } + bool get hasVisitedTimeTable { + if (preferences!.containsKey(KEY_VISITED_TIMETABLE)) { + return preferences!.getBool(KEY_VISITED_TIMETABLE)!; + } + return false; + } + + set hasVisitedTimeTable(bool value) { + preferences!.setBool(KEY_VISITED_TIMETABLE, value); + notifyListeners(); + } + int get lastECBuildingChoiceRepresentation { if (preferences!.containsKey(KEY_EMPTY_CLASSROOM_LAST_BUILDING_CHOICE)) { return preferences!.getInt(KEY_EMPTY_CLASSROOM_LAST_BUILDING_CHOICE)!; diff --git a/lib/util/opentreehole/human_duration.dart b/lib/util/opentreehole/human_duration.dart index 5f7787a4..d6f1769d 100644 --- a/lib/util/opentreehole/human_duration.dart +++ b/lib/util/opentreehole/human_duration.dart @@ -34,7 +34,7 @@ class HumanDuration { } else if (duration.inDays <= 30) { return S.of(context).day_ago(duration.inDays); } else { - return DateFormat("yyyy/MM/dd").format(dateTime!.toLocal()); + return DateFormat("yyyy/MM/dd").format(dateTime.toLocal()); } } catch (e) { return ""; diff --git a/pubspec.lock b/pubspec.lock index ac90598a..84206811 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1727,6 +1727,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + tutorial_coach_mark: + dependency: "direct main" + description: + name: tutorial_coach_mark + sha256: ba60583d1b500111bf5040eb24330862b25162d926f72923b8e45c16621878b0 + url: "https://pub.dev" + source: hosted + version: "1.2.9" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2ca1ac91..ef941c38 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,7 @@ dependencies: flutter_secure_storage: ^8.0.0 encrypt_shared_preferences: ^0.3.5 device_identity: ^1.0.0 + tutorial_coach_mark: ^1.2.9 dependency_overrides: