1
0
forked from firka/firka

Show active break in timetable widget

Add break detection and UI for active breaks in timetable widgets. Introduces a new localization key "break_between" and a BreakIndicatorRow view that displays a break marker and minutes left. TimetableMediumView and TimetableLargeView now detect when the current time falls between consecutive lessons, reduce the number of visible lessons accordingly, and inject a BreakIndicatorRow between lessons when in a break (minutes are calculated using ceil of time interval). Also clean up commented/debug lines in the iOS widget cache helper (widget.dart) for locale/theme and removed a TODO comment.
This commit is contained in:
Horváth Gergely
2026-01-30 20:44:48 +01:00
committed by 4831c0
parent 16b7d2f70a
commit 71ef509021
3 changed files with 93 additions and 9 deletions

View File

@@ -149,6 +149,11 @@ struct WidgetLocalization {
"en": "lesson",
"de": "Std"
],
"break_between": [
"hu": "Szünet",
"en": "Break",
"de": "Pause"
],
"in_minutes": [
"hu": "%d perc múlva",
"en": "in %d min",

View File

@@ -116,9 +116,19 @@ struct TimetableMediumView: View {
return max(0, entry.lessons.count - 1)
}
var hasActiveBreak: Bool {
let checkDate = entry.date
for i in 0..<entry.lessons.count - 1 {
if checkDate > entry.lessons[i].end && checkDate < entry.lessons[i + 1].start {
return true
}
}
return false
}
var visibleLessons: [WidgetLesson] {
let totalLessons = entry.lessons.count
let maxVisible = 4
let maxVisible = hasActiveBreak ? 3 : 4
if totalLessons <= maxVisible {
return Array(entry.lessons)
@@ -154,8 +164,17 @@ struct TimetableMediumView: View {
.widgetTextStyle(style, colors: nil, isPrimary: false)
Spacer(minLength: 0)
ForEach(visibleLessons) { lesson in
ForEach(Array(visibleLessons.enumerated()), id: \.element.id) { index, lesson in
LessonRow(lesson: lesson, isActive: isLessonActive(lesson), style: style, compact: true)
if index < visibleLessons.count - 1 {
let nextLesson = visibleLessons[index + 1]
let isInBreak = entry.date > lesson.end && entry.date < nextLesson.start
if isInBreak {
let breakMinutes = Int(ceil(nextLesson.start.timeIntervalSince(entry.date) / 60))
BreakIndicatorRow(minutesLeft: breakMinutes, localization: localization, style: style, compact: true)
}
}
}
Spacer(minLength: 0)
}
@@ -178,6 +197,16 @@ struct TimetableLargeView: View {
return checkDate >= lesson.start && checkDate <= lesson.end
}
var hasActiveBreak: Bool {
let checkDate = entry.date
for i in 0..<entry.lessons.count - 1 {
if checkDate > entry.lessons[i].end && checkDate < entry.lessons[i + 1].start {
return true
}
}
return false
}
var headerText: String {
if entry.isNextSchoolDay {
let dateStr = WidgetLocalization.formatShortDate(entry.nextSchoolDayDateString, locale: localization.locale)
@@ -199,8 +228,19 @@ struct TimetableLargeView: View {
.fontWeight(.semibold)
.widgetTextStyle(style, colors: nil)
ForEach(entry.lessons.prefix(7)) { lesson in
let maxLessons = hasActiveBreak ? 6 : 7
let lessonsToShow = Array(entry.lessons.prefix(maxLessons))
ForEach(Array(lessonsToShow.enumerated()), id: \.element.id) { index, lesson in
LessonRow(lesson: lesson, isActive: isLessonActive(lesson), style: style, showRoom: true)
if index < lessonsToShow.count - 1 {
let nextLesson = lessonsToShow[index + 1]
let isInBreak = entry.date > lesson.end && entry.date < nextLesson.start
if isInBreak {
let breakMinutes = Int(ceil(nextLesson.start.timeIntervalSince(entry.date) / 60))
BreakIndicatorRow(minutesLeft: breakMinutes, localization: localization, style: style)
}
}
}
}
.padding()
@@ -209,6 +249,50 @@ struct TimetableLargeView: View {
}
}
struct BreakIndicatorRow: View {
let minutesLeft: Int
let localization: WidgetLocalization
let style: WidgetStyleType
var compact: Bool = false
@Environment(\.colorScheme) var colorScheme
var liquidGlassPrimary: Color {
colorScheme == .dark ? .white : .black
}
var liquidGlassSecondary: Color {
colorScheme == .dark ? .white.opacity(0.7) : .black.opacity(0.6)
}
var body: some View {
HStack(spacing: 12) {
Text("")
.font(.caption)
.fontWeight(.bold)
.frame(width: 24, height: 24)
.background(
Circle()
.fill(Color.green.opacity(0.3))
)
.foregroundColor(style == .liquidGlass ? liquidGlassPrimary : .primary)
Text(localization.string("break_between"))
.font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(style == .liquidGlass ? liquidGlassPrimary : .primary)
Spacer()
Text("\(minutesLeft) \(localization.string("minutes_abbrev"))")
.font(.caption)
.foregroundColor(style == .liquidGlass ? liquidGlassSecondary : .secondary)
}
.padding(.vertical, compact ? 2 : 4)
.padding(.horizontal, 8)
.currentLessonGlow(isActive: true)
}
}
struct LessonRow: View {
let lesson: WidgetLesson
let isActive: Bool

View File

@@ -109,7 +109,6 @@ class WidgetCacheHelper {
if (!Platform.isIOS) return;
try {
// Get locale
final langIndex = (settings.group("settings").subGroup("application")["language"]
as SettingsItemsRadio)
.activeIndex;
@@ -125,10 +124,9 @@ class WidgetCacheHelper {
locale = 'de';
break;
default:
locale = 'hu'; // Default to Hungarian
locale = 'hu';
}
// Get theme
final themeIndex = (settings.group("settings").subGroup("customization")["theme"]
as SettingsItemsRadio)
.activeIndex;
@@ -189,7 +187,6 @@ class WidgetCacheHelper {
debugPrint('iOS widget refresh: ${grades.length} grades fetched (cached: ${gradesResponse.cached})');
// Calculate subject averages
final Map<String, double> subjectAverages = {};
final Set<String> subjectUids = {};
@@ -214,9 +211,7 @@ class WidgetCacheHelper {
? overallSum / validSubjectCount
: null;
// Check for break (simplified - you might want to enhance this)
WidgetBreakInfo? currentBreak;
// TODO: Add break detection if needed
await updateIOSWidgets(
locale: locale,