feat: select school terms and smooth refractive Dock motion

This commit is contained in:
Codex
2026-10-13 19:08:36 +08:00
parent 3e2d8864af
commit 50fcc70e8e
33 changed files with 1245 additions and 94 deletions
+28 -1
View File
@@ -9,6 +9,7 @@ import '../services/calendar_service.dart';
import '../domain/schedule_engine.dart';
import '../data/demo_schedule.dart';
import '../domain/exam_schedule.dart';
import '../domain/school_term.dart';
class AppController extends ChangeNotifier {
AppController(this.store, this.notifications, this.calendar, this.clock);
@@ -145,6 +146,11 @@ class AppController extends ChangeNotifier {
await _save(
snapshot.copyWith(
courses: draft.courses,
schoolTerm: draft.schoolTerm,
clearExams: _differentTerm(draft.schoolTerm),
settings: _differentTerm(draft.schoolTerm)
? snapshot.settings.copyWith(clearSemester: true)
: snapshot.settings,
importMetadata: ImportMetadata(
source: draft.source,
importedAt: clock(),
@@ -177,8 +183,29 @@ class AppController extends ChangeNotifier {
});
Future<void> applyExams(ExamSchedule value) => _run(() async {
final validated = ExamSchedule.fromJson(value.toJson());
await _save(snapshot.copyWith(examSchedule: validated));
final term = SchoolTerm(int.parse(validated.termId));
final changed = _differentTerm(term);
await _save(
snapshot.copyWith(
examSchedule: validated,
schoolTerm: term,
courses: changed ? [] : null,
clearMetadata: changed,
settings: changed
? snapshot.settings.copyWith(clearSemester: true)
: null,
),
);
if (changed) {
isDemo = false;
await _reconcile();
}
});
bool _differentTerm(SchoolTerm? next) =>
next != null &&
((snapshot.schoolTerm != null && snapshot.schoolTerm != next) ||
(snapshot.schoolTerm == null &&
(snapshot.courses.isNotEmpty || snapshot.examSchedule != null)));
Future<void> clearExams() => _run(() async {
await _save(snapshot.copyWith(clearExams: true));
});
+15 -4
View File
@@ -136,12 +136,19 @@ class _ShellState extends State<_Shell> with WidgetsBindingObserver {
}
}
void _school({bool exams = false}) {
void _school({bool exams = false, bool chooseTerm = false}) {
if (widget.schoolBuilder != null) {
final term = widget.controller.snapshot.schoolTerm;
if (exams) {
widget.school?.openExams();
widget.school?.openExams(
term: term,
chooseTerm: chooseTerm || term == null,
);
} else {
widget.school?.openTimetable();
widget.school?.openTimetable(
term: term,
chooseTerm: chooseTerm || term == null,
);
}
Navigator.push(
context,
@@ -272,7 +279,10 @@ class _ShellState extends State<_Shell> with WidgetsBindingObserver {
child: TabDeck(
index: _tab,
children: [
TimetablePage(controller: widget.controller),
TimetablePage(
controller: widget.controller,
onSync: _school,
),
ExamsPage(
controller: widget.controller,
onSync: () {
@@ -282,6 +292,7 @@ class _ShellState extends State<_Shell> with WidgetsBindingObserver {
ProfilePage(
controller: widget.controller,
onLogin: _school,
onUpdateTerm: () => _school(chooseTerm: true),
authStatus: widget.school?.statusLabel ?? '未登录',
logoutLabel: widget.school?.logoutFailed == true
? '重试退出学校登录'