dart format
This commit is contained in:
@@ -8,9 +8,9 @@ sealed class ArbitraryAppointment {
|
||||
required T Function(GetTimetableResponseObject lesson) webuntis,
|
||||
required T Function(CustomTimetableEvent event) custom,
|
||||
}) => switch (this) {
|
||||
WebuntisAppointment(:final lesson) => webuntis(lesson),
|
||||
CustomAppointment(:final event) => custom(event),
|
||||
};
|
||||
WebuntisAppointment(:final lesson) => webuntis(lesson),
|
||||
CustomAppointment(:final event) => custom(event),
|
||||
};
|
||||
}
|
||||
|
||||
class WebuntisAppointment extends ArbitraryAppointment {
|
||||
|
||||
@@ -43,24 +43,28 @@ List<BoundRegion> expandRegionsForDay(List<TimeRegion> regions, DateTime day) {
|
||||
final result = <BoundRegion>[];
|
||||
final dayStart = DateTime(day.year, day.month, day.day);
|
||||
for (final region in regions) {
|
||||
final isRecurringDaily = region.recurrenceRule != null &&
|
||||
final isRecurringDaily =
|
||||
region.recurrenceRule != null &&
|
||||
region.recurrenceRule!.toUpperCase().contains('FREQ=DAILY');
|
||||
if (isRecurringDaily) {
|
||||
final start = dayStart.add(Duration(
|
||||
hours: region.startTime.hour,
|
||||
minutes: region.startTime.minute,
|
||||
));
|
||||
final end = dayStart.add(Duration(
|
||||
hours: region.endTime.hour,
|
||||
minutes: region.endTime.minute,
|
||||
));
|
||||
final start = dayStart.add(
|
||||
Duration(
|
||||
hours: region.startTime.hour,
|
||||
minutes: region.startTime.minute,
|
||||
),
|
||||
);
|
||||
final end = dayStart.add(
|
||||
Duration(hours: region.endTime.hour, minutes: region.endTime.minute),
|
||||
);
|
||||
result.add(BoundRegion(region: region, start: start, end: end));
|
||||
} else if (region.startTime.isSameDay(day)) {
|
||||
result.add(BoundRegion(
|
||||
region: region,
|
||||
start: region.startTime,
|
||||
end: region.endTime,
|
||||
));
|
||||
result.add(
|
||||
BoundRegion(
|
||||
region: region,
|
||||
start: region.startTime,
|
||||
end: region.endTime,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -73,8 +77,10 @@ List<BoundRegion> expandRegionsForDay(List<TimeRegion> regions, DateTime day) {
|
||||
/// [kCalendarStartHour] or end after [kCalendarEndHour]). The outside bucket
|
||||
/// is rendered as chips above the grid.
|
||||
({List<List<Appointment>> inside, List<List<Appointment>> outside})
|
||||
partitionAppointmentsForWeek(
|
||||
List<Appointment> appointments, DateTime weekStart) {
|
||||
partitionAppointmentsForWeek(
|
||||
List<Appointment> appointments,
|
||||
DateTime weekStart,
|
||||
) {
|
||||
final inside = List<List<Appointment>>.generate(5, (_) => <Appointment>[]);
|
||||
final outside = List<List<Appointment>>.generate(5, (_) => <Appointment>[]);
|
||||
final weekEnd = weekStart.add(const Duration(days: 5));
|
||||
@@ -104,12 +110,19 @@ List<BoundRegion> expandRegionsForDay(List<TimeRegion> regions, DateTime day) {
|
||||
if (!occUtc.isBefore(weekEndUtc)) break;
|
||||
if (occUtc.isBefore(weekStartUtc)) continue;
|
||||
final occLocal = occUtc.toLocal();
|
||||
final idx = DateTime(occLocal.year, occLocal.month, occLocal.day)
|
||||
.difference(weekStart)
|
||||
.inDays;
|
||||
final idx = DateTime(
|
||||
occLocal.year,
|
||||
occLocal.month,
|
||||
occLocal.day,
|
||||
).difference(weekStart).inDays;
|
||||
if (idx < 0 || idx >= 5) continue;
|
||||
final newStart = DateTime(occLocal.year, occLocal.month, occLocal.day,
|
||||
a.startTime.hour, a.startTime.minute);
|
||||
final newStart = DateTime(
|
||||
occLocal.year,
|
||||
occLocal.month,
|
||||
occLocal.day,
|
||||
a.startTime.hour,
|
||||
a.startTime.minute,
|
||||
);
|
||||
place(
|
||||
idx,
|
||||
Appointment(
|
||||
@@ -150,8 +163,7 @@ class PeriodLayout {
|
||||
|
||||
double _h(LessonPeriod p) => p.isBreak ? breakHeight : lessonHeight;
|
||||
|
||||
double get totalHeight =>
|
||||
periods.fold<double>(0, (sum, p) => sum + _h(p));
|
||||
double get totalHeight => periods.fold<double>(0, (sum, p) => sum + _h(p));
|
||||
|
||||
double topOf(LessonPeriod period) {
|
||||
var y = 0.0;
|
||||
@@ -241,7 +253,13 @@ class LaidOutOverflow extends LaidOutCell {
|
||||
final DateTime startTime;
|
||||
@override
|
||||
final DateTime endTime;
|
||||
LaidOutOverflow(this.appointments, this.lane, this.laneCount, this.startTime, this.endTime);
|
||||
LaidOutOverflow(
|
||||
this.appointments,
|
||||
this.lane,
|
||||
this.laneCount,
|
||||
this.startTime,
|
||||
this.endTime,
|
||||
);
|
||||
}
|
||||
|
||||
/// Horizontal ordering rank for parallel appointments. Lower = further left.
|
||||
@@ -269,17 +287,21 @@ int _appointmentPriority(Appointment a) {
|
||||
/// is free at its `startTime`. When no lane is free, open a new one.
|
||||
/// 3. A cluster ends as soon as every active lane's end is at or before the
|
||||
/// next appointment's start.
|
||||
List<LaidOutCell> assignLanes(List<Appointment> appts, {required int maxLanes}) {
|
||||
List<LaidOutCell> assignLanes(
|
||||
List<Appointment> appts, {
|
||||
required int maxLanes,
|
||||
}) {
|
||||
assert(maxLanes >= 2, 'maxLanes must reserve at least one slot for overflow');
|
||||
if (appts.isEmpty) return const <LaidOutCell>[];
|
||||
|
||||
final sorted = [...appts]..sort((a, b) {
|
||||
final c = a.startTime.compareTo(b.startTime);
|
||||
if (c != 0) return c;
|
||||
final p = _appointmentPriority(a).compareTo(_appointmentPriority(b));
|
||||
if (p != 0) return p;
|
||||
return b.endTime.compareTo(a.endTime);
|
||||
});
|
||||
final sorted = [...appts]
|
||||
..sort((a, b) {
|
||||
final c = a.startTime.compareTo(b.startTime);
|
||||
if (c != 0) return c;
|
||||
final p = _appointmentPriority(a).compareTo(_appointmentPriority(b));
|
||||
if (p != 0) return p;
|
||||
return b.endTime.compareTo(a.endTime);
|
||||
});
|
||||
|
||||
// Phase 1: greedy lane assignment, grouped by cluster.
|
||||
final clusters = <List<({Appointment apt, int lane})>>[];
|
||||
@@ -288,7 +310,8 @@ List<LaidOutCell> assignLanes(List<Appointment> appts, {required int maxLanes})
|
||||
|
||||
for (final apt in sorted) {
|
||||
final allFree =
|
||||
laneEnds.isNotEmpty && laneEnds.every((end) => !end.isAfter(apt.startTime));
|
||||
laneEnds.isNotEmpty &&
|
||||
laneEnds.every((end) => !end.isAfter(apt.startTime));
|
||||
if (allFree) {
|
||||
clusters.add(current);
|
||||
current = <({Appointment apt, int lane})>[];
|
||||
@@ -316,8 +339,10 @@ List<LaidOutCell> assignLanes(List<Appointment> appts, {required int maxLanes})
|
||||
// Phase 2: emit cells per cluster, collapsing if too wide.
|
||||
final result = <LaidOutCell>[];
|
||||
for (final cluster in clusters) {
|
||||
final laneCount =
|
||||
cluster.fold<int>(0, (m, e) => e.lane + 1 > m ? e.lane + 1 : m);
|
||||
final laneCount = cluster.fold<int>(
|
||||
0,
|
||||
(m, e) => e.lane + 1 > m ? e.lane + 1 : m,
|
||||
);
|
||||
|
||||
if (laneCount <= maxLanes) {
|
||||
for (final entry in cluster) {
|
||||
@@ -348,8 +373,9 @@ List<LaidOutCell> assignLanes(List<Appointment> appts, {required int maxLanes})
|
||||
if (a.startTime.isBefore(earliest)) earliest = a.startTime;
|
||||
if (a.endTime.isAfter(latest)) latest = a.endTime;
|
||||
}
|
||||
result.add(LaidOutOverflow(
|
||||
overflow, maxLanes - 1, maxLanes, earliest, latest));
|
||||
result.add(
|
||||
LaidOutOverflow(overflow, maxLanes - 1, maxLanes, earliest, latest),
|
||||
);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -17,8 +17,8 @@ class LessonPeriod {
|
||||
});
|
||||
|
||||
Duration get duration => Duration(
|
||||
minutes: (end.hour * 60 + end.minute) - (start.hour * 60 + start.minute),
|
||||
);
|
||||
minutes: (end.hour * 60 + end.minute) - (start.hour * 60 + start.minute),
|
||||
);
|
||||
|
||||
int get _startMinutes => start.hour * 60 + start.minute;
|
||||
}
|
||||
@@ -31,39 +31,94 @@ class LessonPeriodSchedule {
|
||||
static LessonPeriodSchedule? fromApi(GetTimegridUnitsResponse response) {
|
||||
final canonical = response.result.firstWhere(
|
||||
(d) => d.day == 1,
|
||||
orElse: () => response.result.isNotEmpty ? response.result.first : GetTimegridUnitsResponseDay(0, []),
|
||||
orElse: () => response.result.isNotEmpty
|
||||
? response.result.first
|
||||
: GetTimegridUnitsResponseDay(0, []),
|
||||
);
|
||||
if (canonical.timeUnits.isEmpty) return null;
|
||||
|
||||
final periods = canonical.timeUnits
|
||||
.map((u) => LessonPeriod(
|
||||
name: u.name,
|
||||
start: _fromHHMM(u.startTime),
|
||||
end: _fromHHMM(u.endTime),
|
||||
))
|
||||
.toList()
|
||||
..sort((a, b) => a._startMinutes.compareTo(b._startMinutes));
|
||||
final periods =
|
||||
canonical.timeUnits
|
||||
.map(
|
||||
(u) => LessonPeriod(
|
||||
name: u.name,
|
||||
start: _fromHHMM(u.startTime),
|
||||
end: _fromHHMM(u.endTime),
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
..sort((a, b) => a._startMinutes.compareTo(b._startMinutes));
|
||||
|
||||
return LessonPeriodSchedule(periods);
|
||||
}
|
||||
|
||||
static LessonPeriodSchedule fallback() => const LessonPeriodSchedule([
|
||||
LessonPeriod(name: '0', start: TimeOfDay(hour: 7, minute: 10), end: TimeOfDay(hour: 7, minute: 53)),
|
||||
LessonPeriod(name: '1', start: TimeOfDay(hour: 7, minute: 55), end: TimeOfDay(hour: 8, minute: 40)),
|
||||
LessonPeriod(name: '2', start: TimeOfDay(hour: 8, minute: 40), end: TimeOfDay(hour: 9, minute: 25)),
|
||||
LessonPeriod(name: '3', start: TimeOfDay(hour: 9, minute: 30), end: TimeOfDay(hour: 10, minute: 15)),
|
||||
LessonPeriod(name: '4', start: TimeOfDay(hour: 10, minute: 35), end: TimeOfDay(hour: 11, minute: 20)),
|
||||
LessonPeriod(name: '5', start: TimeOfDay(hour: 11, minute: 25), end: TimeOfDay(hour: 12, minute: 10)),
|
||||
LessonPeriod(name: '6', start: TimeOfDay(hour: 12, minute: 15), end: TimeOfDay(hour: 13, minute: 0)),
|
||||
LessonPeriod(name: '7', start: TimeOfDay(hour: 13, minute: 5), end: TimeOfDay(hour: 13, minute: 50)),
|
||||
LessonPeriod(name: '8', start: TimeOfDay(hour: 14, minute: 5), end: TimeOfDay(hour: 14, minute: 50)),
|
||||
LessonPeriod(name: '9', start: TimeOfDay(hour: 14, minute: 50), end: TimeOfDay(hour: 15, minute: 35)),
|
||||
LessonPeriod(name: '10', start: TimeOfDay(hour: 15, minute: 40), end: TimeOfDay(hour: 16, minute: 25)),
|
||||
LessonPeriod(name: '11', start: TimeOfDay(hour: 16, minute: 25), end: TimeOfDay(hour: 17, minute: 10)),
|
||||
]);
|
||||
LessonPeriod(
|
||||
name: '0',
|
||||
start: TimeOfDay(hour: 7, minute: 10),
|
||||
end: TimeOfDay(hour: 7, minute: 53),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '1',
|
||||
start: TimeOfDay(hour: 7, minute: 55),
|
||||
end: TimeOfDay(hour: 8, minute: 40),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '2',
|
||||
start: TimeOfDay(hour: 8, minute: 40),
|
||||
end: TimeOfDay(hour: 9, minute: 25),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '3',
|
||||
start: TimeOfDay(hour: 9, minute: 30),
|
||||
end: TimeOfDay(hour: 10, minute: 15),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '4',
|
||||
start: TimeOfDay(hour: 10, minute: 35),
|
||||
end: TimeOfDay(hour: 11, minute: 20),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '5',
|
||||
start: TimeOfDay(hour: 11, minute: 25),
|
||||
end: TimeOfDay(hour: 12, minute: 10),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '6',
|
||||
start: TimeOfDay(hour: 12, minute: 15),
|
||||
end: TimeOfDay(hour: 13, minute: 0),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '7',
|
||||
start: TimeOfDay(hour: 13, minute: 5),
|
||||
end: TimeOfDay(hour: 13, minute: 50),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '8',
|
||||
start: TimeOfDay(hour: 14, minute: 5),
|
||||
end: TimeOfDay(hour: 14, minute: 50),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '9',
|
||||
start: TimeOfDay(hour: 14, minute: 50),
|
||||
end: TimeOfDay(hour: 15, minute: 35),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '10',
|
||||
start: TimeOfDay(hour: 15, minute: 40),
|
||||
end: TimeOfDay(hour: 16, minute: 25),
|
||||
),
|
||||
LessonPeriod(
|
||||
name: '11',
|
||||
start: TimeOfDay(hour: 16, minute: 25),
|
||||
end: TimeOfDay(hour: 17, minute: 10),
|
||||
),
|
||||
]);
|
||||
|
||||
static LessonPeriodSchedule fromState(TimetableState state) {
|
||||
final fromApi = state.timegrid != null ? LessonPeriodSchedule.fromApi(state.timegrid!) : null;
|
||||
final fromApi = state.timegrid != null
|
||||
? LessonPeriodSchedule.fromApi(state.timegrid!)
|
||||
: null;
|
||||
return (fromApi ?? fallback()).withSyntheticBreaks();
|
||||
}
|
||||
|
||||
@@ -74,21 +129,22 @@ class LessonPeriodSchedule {
|
||||
result.add(current);
|
||||
if (i + 1 >= periods.length) continue;
|
||||
final next = periods[i + 1];
|
||||
final gapMinutes = next._startMinutes - (current.end.hour * 60 + current.end.minute);
|
||||
final gapMinutes =
|
||||
next._startMinutes - (current.end.hour * 60 + current.end.minute);
|
||||
if (gapMinutes >= 10) {
|
||||
result.add(LessonPeriod(
|
||||
name: 'Pause',
|
||||
start: current.end,
|
||||
end: next.start,
|
||||
isBreak: true,
|
||||
));
|
||||
result.add(
|
||||
LessonPeriod(
|
||||
name: 'Pause',
|
||||
start: current.end,
|
||||
end: next.start,
|
||||
isBreak: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return LessonPeriodSchedule(result);
|
||||
}
|
||||
|
||||
static TimeOfDay _fromHHMM(int hhmm) => TimeOfDay(
|
||||
hour: hhmm ~/ 100,
|
||||
minute: hhmm % 100,
|
||||
);
|
||||
static TimeOfDay _fromHHMM(int hhmm) =>
|
||||
TimeOfDay(hour: hhmm ~/ 100, minute: hhmm % 100);
|
||||
}
|
||||
|
||||
@@ -20,10 +20,17 @@ class LessonStatusClassifier {
|
||||
}) {
|
||||
if (lesson.code == 'cancelled') return LessonStatus.cancelled;
|
||||
if (isEvent) return LessonStatus.event;
|
||||
if (lesson.code == 'irregular' || (lesson.te.isNotEmpty && lesson.te.first.id == 0)) return LessonStatus.irregular;
|
||||
if (lesson.te.any((t) => t.orgname != null)) return LessonStatus.teacherChanged;
|
||||
if (lesson.code == 'irregular' ||
|
||||
(lesson.te.isNotEmpty && lesson.te.first.id == 0)) {
|
||||
return LessonStatus.irregular;
|
||||
}
|
||||
if (lesson.te.any((t) => t.orgname != null)) {
|
||||
return LessonStatus.teacherChanged;
|
||||
}
|
||||
if (endTime.isBefore(now)) return LessonStatus.past;
|
||||
if (startTime.isBefore(now) && endTime.isAfter(now)) return LessonStatus.ongoing;
|
||||
if (startTime.isBefore(now) && endTime.isAfter(now)) {
|
||||
return LessonStatus.ongoing;
|
||||
}
|
||||
return LessonStatus.regular;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ class TimetableAppointmentFactory {
|
||||
});
|
||||
|
||||
List<Appointment> build() {
|
||||
final source = settings.connectDoubleLessons ? _mergeAdjacentLessons(lessons) : lessons;
|
||||
final source = settings.connectDoubleLessons
|
||||
? _mergeAdjacentLessons(lessons)
|
||||
: lessons;
|
||||
return [
|
||||
...source.map(_lessonToAppointment),
|
||||
...customEvents.map(_customEventToAppointment),
|
||||
@@ -42,7 +44,9 @@ class TimetableAppointmentFactory {
|
||||
try {
|
||||
final startTime = WebuntisTime.parse(lesson.date, lesson.startTime);
|
||||
final endTime = WebuntisTime.parse(lesson.date, lesson.endTime);
|
||||
final subject = subjects.result.firstWhereOrNull((s) => s.id == lesson.su.firstOrNull?.id);
|
||||
final subject = subjects.result.firstWhereOrNull(
|
||||
(s) => s.id == lesson.su.firstOrNull?.id,
|
||||
);
|
||||
final status = LessonStatusClassifier.classify(
|
||||
lesson,
|
||||
startTime,
|
||||
@@ -81,16 +85,26 @@ class TimetableAppointmentFactory {
|
||||
id: CustomAppointment(event),
|
||||
startTime: event.startDate,
|
||||
endTime: allDay
|
||||
? DateTime(event.startDate.year, event.startDate.month, event.startDate.day, 23, 59)
|
||||
? DateTime(
|
||||
event.startDate.year,
|
||||
event.startDate.month,
|
||||
event.startDate.day,
|
||||
23,
|
||||
59,
|
||||
)
|
||||
: event.endDate,
|
||||
isAllDay: allDay,
|
||||
// Preserve user-entered newlines in descriptions; the tile soft-wraps to
|
||||
// fill the available height. For lessons we still collapse whitespace
|
||||
// so room/teacher stay on one line each.
|
||||
location: event.description.trim().isEmpty ? null : event.description.trim(),
|
||||
location: event.description.trim().isEmpty
|
||||
? null
|
||||
: event.description.trim(),
|
||||
subject: _collapseWhitespace(event.title) ?? event.title,
|
||||
recurrenceRule: event.rrule,
|
||||
color: TimetableColors.getColorFromString(event.color ?? TimetableColors.defaultColor.name),
|
||||
color: TimetableColors.getColorFromString(
|
||||
event.color ?? TimetableColors.defaultColor.name,
|
||||
),
|
||||
startTimeZone: '',
|
||||
endTimeZone: '',
|
||||
);
|
||||
@@ -114,7 +128,10 @@ class TimetableAppointmentFactory {
|
||||
e.second == 0;
|
||||
}
|
||||
|
||||
String _subjectName(GetTimetableResponseObject lesson, GetSubjectsResponseObject? subject) {
|
||||
String _subjectName(
|
||||
GetTimetableResponseObject lesson,
|
||||
GetSubjectsResponseObject? subject,
|
||||
) {
|
||||
if (subject == null) return 'Event';
|
||||
final name = switch (settings.timetableNameMode) {
|
||||
TimetableNameMode.name => subject.name,
|
||||
@@ -125,10 +142,15 @@ class TimetableAppointmentFactory {
|
||||
}
|
||||
|
||||
String _locationLabel(GetTimetableResponseObject lesson) {
|
||||
final roomName = _collapseWhitespace(
|
||||
rooms.result.firstWhereOrNull((r) => r.id == lesson.ro.firstOrNull?.id)?.name) ??
|
||||
final roomName =
|
||||
_collapseWhitespace(
|
||||
rooms.result
|
||||
.firstWhereOrNull((r) => r.id == lesson.ro.firstOrNull?.id)
|
||||
?.name,
|
||||
) ??
|
||||
'Unbekannt';
|
||||
final teacherName = _collapseWhitespace(lesson.te.firstOrNull?.longname) ?? 'Unbekannt';
|
||||
final teacherName =
|
||||
_collapseWhitespace(lesson.te.firstOrNull?.longname) ?? 'Unbekannt';
|
||||
return '$roomName\n$teacherName';
|
||||
}
|
||||
|
||||
@@ -161,8 +183,13 @@ class TimetableAppointmentFactory {
|
||||
}) {
|
||||
if (input.isEmpty) return const [];
|
||||
|
||||
final sorted = [...input]..sort((a, b) =>
|
||||
WebuntisTime.parse(a.date, a.startTime).compareTo(WebuntisTime.parse(b.date, b.startTime)));
|
||||
final sorted = [...input]
|
||||
..sort(
|
||||
(a, b) => WebuntisTime.parse(
|
||||
a.date,
|
||||
a.startTime,
|
||||
).compareTo(WebuntisTime.parse(b.date, b.startTime)),
|
||||
);
|
||||
|
||||
final merged = <GetTimetableResponseObject>[];
|
||||
for (final current in sorted) {
|
||||
@@ -180,10 +207,16 @@ class TimetableAppointmentFactory {
|
||||
static GetTimetableResponseObject _copyLesson(GetTimetableResponseObject l) =>
|
||||
GetTimetableResponseObject.fromJson(l.toJson());
|
||||
|
||||
static bool _canMerge(GetTimetableResponseObject a, GetTimetableResponseObject b, Duration maxGap) {
|
||||
static bool _canMerge(
|
||||
GetTimetableResponseObject a,
|
||||
GetTimetableResponseObject b,
|
||||
Duration maxGap,
|
||||
) {
|
||||
final aSubject = a.su.firstOrNull?.id;
|
||||
final bSubject = b.su.firstOrNull?.id;
|
||||
if (aSubject == null || bSubject == null || aSubject != bSubject) return false;
|
||||
if (aSubject == null || bSubject == null || aSubject != bSubject) {
|
||||
return false;
|
||||
}
|
||||
if (a.ro.firstOrNull?.id != b.ro.firstOrNull?.id) return false;
|
||||
if (a.te.firstOrNull?.id != b.te.firstOrNull?.id) return false;
|
||||
if (a.code != b.code) return false;
|
||||
@@ -193,7 +226,10 @@ class TimetableAppointmentFactory {
|
||||
// overlap in time would silently collapse into one — and because the
|
||||
// merge sets `previous.endTime = current.endTime`, an overlapping merge
|
||||
// can even truncate the earlier lesson.
|
||||
final gap = WebuntisTime.parse(b.date, b.startTime).difference(WebuntisTime.parse(a.date, a.endTime));
|
||||
final gap = WebuntisTime.parse(
|
||||
b.date,
|
||||
b.startTime,
|
||||
).difference(WebuntisTime.parse(a.date, a.endTime));
|
||||
return !gap.isNegative && gap <= maxGap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,20 @@ class TimetableNameModes {
|
||||
static DropdownDisplay getDisplayOptions(TimetableNameMode mode) {
|
||||
switch (mode) {
|
||||
case TimetableNameMode.name:
|
||||
return DropdownDisplay(icon: Icons.device_unknown_outlined, displayName: 'Name');
|
||||
return DropdownDisplay(
|
||||
icon: Icons.device_unknown_outlined,
|
||||
displayName: 'Name',
|
||||
);
|
||||
case TimetableNameMode.longName:
|
||||
return DropdownDisplay(icon: Icons.perm_device_info_outlined, displayName: 'Langname');
|
||||
return DropdownDisplay(
|
||||
icon: Icons.perm_device_info_outlined,
|
||||
displayName: 'Langname',
|
||||
);
|
||||
case TimetableNameMode.alternateName:
|
||||
return DropdownDisplay(icon: Icons.on_device_training_outlined, displayName: 'Kurzform');
|
||||
return DropdownDisplay(
|
||||
icon: Icons.on_device_training_outlined,
|
||||
displayName: 'Kurzform',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ class WebuntisTime {
|
||||
|
||||
static DateTime parse(int date, int time) {
|
||||
final timeString = time.toString().padLeft(4, '0');
|
||||
return DateTime.parse('$date ${timeString.substring(0, 2)}:${timeString.substring(2, 4)}');
|
||||
return DateTime.parse(
|
||||
'$date ${timeString.substring(0, 2)}:${timeString.substring(2, 4)}',
|
||||
);
|
||||
}
|
||||
|
||||
static int formatDate(DateTime date) => int.parse(_dateFormat.format(date));
|
||||
|
||||
Reference in New Issue
Block a user