forked from firka/firka
- Fixed lesson language data; backend now passes the correct values.
- Updated handling of cancelled lessons: removed timer and now displaying the localized “Cancelled” text. - Aligned the icon and lesson number in the Dynamic Island so they are now level with the label text.
This commit is contained in:
@@ -11,21 +11,36 @@ struct TimetableActivityAttributes: ActivityAttributes {
|
||||
var startTime: Date
|
||||
var endTime: Date
|
||||
var lessonNumber: Int?
|
||||
|
||||
|
||||
var mode: String? // "lesson" | "break" | "seasonalBreak" | "xmas" | "newYear"
|
||||
var message: String?
|
||||
var season: String?
|
||||
|
||||
|
||||
var nextLessonName: String?
|
||||
var nextRoomName: String?
|
||||
var nextStartTime: Date?
|
||||
|
||||
|
||||
var isSubstitution: Bool
|
||||
var isCancelled: Bool
|
||||
var substituteTeacher: String?
|
||||
|
||||
|
||||
var currentTime: Date
|
||||
|
||||
var labels: Labels?
|
||||
|
||||
struct Labels: Codable, Hashable {
|
||||
var title: String?
|
||||
var timerLabel: String?
|
||||
var cancelledText: String?
|
||||
var substitutionText: String?
|
||||
var roomLabel: String?
|
||||
var teacherLabel: String?
|
||||
var themeLabel: String?
|
||||
var nextLabel: String?
|
||||
var firstLessonLabel: String?
|
||||
var startTimeLabel: String?
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case isBreak
|
||||
case lessonName
|
||||
@@ -45,9 +60,10 @@ struct TimetableActivityAttributes: ActivityAttributes {
|
||||
case isCancelled
|
||||
case substituteTeacher
|
||||
case currentTime
|
||||
case labels
|
||||
}
|
||||
|
||||
init(isBreak: Bool, lessonName: String, lessonTheme: String?, roomName: String?, teacherName: String?, startTime: Date, endTime: Date, lessonNumber: Int?, mode: String?, message: String?, season: String?, nextLessonName: String?, nextRoomName: String?, nextStartTime: Date?, isSubstitution: Bool, isCancelled: Bool, substituteTeacher: String?, currentTime: Date) {
|
||||
init(isBreak: Bool, lessonName: String, lessonTheme: String?, roomName: String?, teacherName: String?, startTime: Date, endTime: Date, lessonNumber: Int?, mode: String?, message: String?, season: String?, nextLessonName: String?, nextRoomName: String?, nextStartTime: Date?, isSubstitution: Bool, isCancelled: Bool, substituteTeacher: String?, currentTime: Date, labels: Labels? = nil) {
|
||||
self.isBreak = isBreak
|
||||
self.lessonName = lessonName
|
||||
self.lessonTheme = lessonTheme
|
||||
@@ -66,6 +82,7 @@ struct TimetableActivityAttributes: ActivityAttributes {
|
||||
self.isCancelled = isCancelled
|
||||
self.substituteTeacher = substituteTeacher
|
||||
self.currentTime = currentTime
|
||||
self.labels = labels
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
@@ -108,7 +125,8 @@ struct TimetableActivityAttributes: ActivityAttributes {
|
||||
isSubstitution = try container.decode(Bool.self, forKey: .isSubstitution)
|
||||
isCancelled = try container.decode(Bool.self, forKey: .isCancelled)
|
||||
substituteTeacher = try container.decodeIfPresent(String.self, forKey: .substituteTeacher)
|
||||
|
||||
labels = try container.decodeIfPresent(Labels.self, forKey: .labels)
|
||||
|
||||
let currentTimeStr = try container.decode(String.self, forKey: .currentTime)
|
||||
guard let currentTimeDate = isoFormatter.date(from: currentTimeStr) else {
|
||||
throw DecodingError.dataCorruptedError(forKey: .currentTime, in: container, debugDescription: "Invalid currentTime format: \(currentTimeStr)")
|
||||
@@ -145,7 +163,8 @@ struct TimetableActivityAttributes: ActivityAttributes {
|
||||
try container.encode(isSubstitution, forKey: .isSubstitution)
|
||||
try container.encode(isCancelled, forKey: .isCancelled)
|
||||
try container.encodeIfPresent(substituteTeacher, forKey: .substituteTeacher)
|
||||
|
||||
try container.encodeIfPresent(labels, forKey: .labels)
|
||||
|
||||
try container.encode(isoFormatter.string(from: currentTime), forKey: .currentTime)
|
||||
}
|
||||
}
|
||||
@@ -302,7 +321,25 @@ extension TimetableActivityAttributes.ContentState {
|
||||
} else {
|
||||
nextStartTime = nil
|
||||
}
|
||||
|
||||
|
||||
let labels: Labels?
|
||||
if let labelsDict = json["labels"] as? [String: Any] {
|
||||
labels = Labels(
|
||||
title: labelsDict["title"] as? String,
|
||||
timerLabel: labelsDict["timerLabel"] as? String,
|
||||
cancelledText: labelsDict["cancelledText"] as? String,
|
||||
substitutionText: labelsDict["substitutionText"] as? String,
|
||||
roomLabel: labelsDict["roomLabel"] as? String,
|
||||
teacherLabel: labelsDict["teacherLabel"] as? String,
|
||||
themeLabel: labelsDict["themeLabel"] as? String,
|
||||
nextLabel: labelsDict["nextLabel"] as? String,
|
||||
firstLessonLabel: labelsDict["firstLessonLabel"] as? String,
|
||||
startTimeLabel: labelsDict["startTimeLabel"] as? String
|
||||
)
|
||||
} else {
|
||||
labels = nil
|
||||
}
|
||||
|
||||
return TimetableActivityAttributes.ContentState(
|
||||
isBreak: isBreak,
|
||||
lessonName: lessonName,
|
||||
@@ -321,7 +358,8 @@ extension TimetableActivityAttributes.ContentState {
|
||||
isSubstitution: isSubstitution,
|
||||
isCancelled: isCancelled,
|
||||
substituteTeacher: json["substituteTeacher"] as? String,
|
||||
currentTime: currentTime
|
||||
currentTime: currentTime,
|
||||
labels: labels
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,24 +49,7 @@ struct TimetableLiveActivity: Widget {
|
||||
return DynamicIsland {
|
||||
// Expanded UI
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
let season = context.state.season ?? ""
|
||||
HStack(alignment: .center, spacing: 4) {
|
||||
if mode == "beforeSchool" {
|
||||
Image(systemName: SeasonalIconHelper.iconName(for: mode, season: season))
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(SeasonalIconHelper.iconColor(for: mode))
|
||||
} else if !SeasonalIconHelper.isSeasonalMode(mode) && !context.state.isBreak {
|
||||
if let lessonNumber = context.state.lessonNumber {
|
||||
Text("\(lessonNumber).")
|
||||
.font(.system(size: 16, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
EmptyView()
|
||||
}
|
||||
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
@@ -78,7 +61,7 @@ struct TimetableLiveActivity: Widget {
|
||||
if SeasonalIconHelper.isSeasonalMode(mode) {
|
||||
EmptyView()
|
||||
} else if mode == "beforeSchool", let timeString = beforeSchoolTime {
|
||||
Text("Kezdés: \(timeString)")
|
||||
Text("\(context.state.labels?.startTimeLabel ?? "Kezdés:") \(timeString)")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
@@ -92,23 +75,28 @@ struct TimetableLiveActivity: Widget {
|
||||
let season = context.state.season ?? ""
|
||||
VStack(spacing: 4) {
|
||||
if mode == "beforeSchool" {
|
||||
Text("Hamarosan suli")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
Image(systemName: SeasonalIconHelper.iconName(for: mode, season: season))
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(SeasonalIconHelper.iconColor(for: mode))
|
||||
Text(context.state.labels?.title ?? "Hamarosan suli")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(spacing: 4) {
|
||||
Text("Első órád:")
|
||||
Text(context.state.labels?.firstLessonLabel ?? "Első órád:")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
Text(context.state.lessonName)
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
|
||||
if let roomName = context.state.roomName {
|
||||
HStack(spacing: 4) {
|
||||
Text("Terem:")
|
||||
Text(context.state.labels?.roomLabel ?? "Terem:")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
Text(roomName)
|
||||
@@ -119,7 +107,7 @@ struct TimetableLiveActivity: Widget {
|
||||
|
||||
if let teacherName = context.state.teacherName {
|
||||
HStack(spacing: 4) {
|
||||
Text("Tanár:")
|
||||
Text(context.state.labels?.teacherLabel ?? "Tanár:")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
Text(teacherName)
|
||||
@@ -130,7 +118,6 @@ struct TimetableLiveActivity: Widget {
|
||||
}
|
||||
} else if SeasonalIconHelper.isSeasonalMode(mode) {
|
||||
if mode == "xmas" || mode == "newYearEve" || mode == "newYearDay" {
|
||||
// Global holidays: show message prominently
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
Image(systemName: SeasonalIconHelper.iconName(for: mode, season: season))
|
||||
.font(.system(size: 18))
|
||||
@@ -141,7 +128,6 @@ struct TimetableLiveActivity: Widget {
|
||||
.lineLimit(2)
|
||||
}
|
||||
} else {
|
||||
// Seasonal breaks: show holiday title
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
Image(systemName: SeasonalIconHelper.iconName(for: mode, season: season))
|
||||
.font(.system(size: 18))
|
||||
@@ -153,13 +139,18 @@ struct TimetableLiveActivity: Widget {
|
||||
}
|
||||
}
|
||||
} else if context.state.isBreak {
|
||||
Text("Szünet")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
Image(systemName: "cup.and.saucer.fill")
|
||||
.font(.system(size: 18))
|
||||
.foregroundColor(SeasonalIconHelper.iconColor(for: mode))
|
||||
Text(context.state.labels?.title ?? "Szünet")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
if let nextLessonName = context.state.nextLessonName {
|
||||
HStack(spacing: 4) {
|
||||
Text("Következő:")
|
||||
Text(context.state.labels?.nextLabel ?? "Következő:")
|
||||
.font(.system(size: 14))
|
||||
.foregroundColor(.gray)
|
||||
Text(nextLessonName)
|
||||
@@ -167,43 +158,46 @@ struct TimetableLiveActivity: Widget {
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let nextRoomName = context.state.nextRoomName {
|
||||
Text("Terem: \(nextRoomName)")
|
||||
Text("\(context.state.labels?.roomLabel ?? "Terem:") \(nextRoomName)")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
} else {
|
||||
Text(context.state.lessonName)
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
|
||||
if let lessonTheme = context.state.lessonTheme, !lessonTheme.isEmpty {
|
||||
Text(lessonTheme)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
HStack(alignment: .center, spacing: 6) {
|
||||
if let lessonNumber = context.state.lessonNumber {
|
||||
Text("\(lessonNumber).")
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
Text(context.state.lessonName)
|
||||
.font(.system(size: 18, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
|
||||
if !(context.state.isCancelled ?? false) {
|
||||
if let lessonTheme = context.state.lessonTheme, !lessonTheme.isEmpty {
|
||||
Text(lessonTheme)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 8) {
|
||||
if let roomName = context.state.roomName {
|
||||
Label(roomName, systemImage: "door.left.hand.closed")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
|
||||
if context.state.isSubstitution ?? false {
|
||||
Label("Helyettesítés", systemImage: "arrow.triangle.2.circlepath")
|
||||
Label(context.state.labels?.substitutionText ?? "Helyettesítés", systemImage: "arrow.triangle.2.circlepath")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
|
||||
if context.state.isCancelled ?? false {
|
||||
Label("Elmaradt", systemImage: "xmark.circle")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,6 +228,11 @@ struct TimetableLiveActivity: Widget {
|
||||
.foregroundColor(.green)
|
||||
.multilineTextAlignment(.center)
|
||||
.monospacedDigit()
|
||||
} else if context.state.isCancelled ?? false {
|
||||
Text(context.state.labels?.cancelledText ?? "Elmaradt")
|
||||
.font(.system(size: 20, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.red)
|
||||
.multilineTextAlignment(.center)
|
||||
} else {
|
||||
Text(timerInterval: context.state.currentTime...context.state.endTime, countsDown: true)
|
||||
.font(.system(size: 24, weight: .bold, design: .rounded))
|
||||
@@ -258,7 +257,6 @@ struct TimetableLiveActivity: Widget {
|
||||
.foregroundColor(SeasonalIconHelper.iconColor(for: mode))
|
||||
} compactTrailing: {
|
||||
if SeasonalIconHelper.isSeasonalMode(mode) {
|
||||
// Show timer for New Year's Eve countdown and seasonal breaks
|
||||
if mode == "newYearEve" || mode == "seasonalBreak" {
|
||||
Text(timerInterval: context.state.currentTime...context.state.endTime, countsDown: true)
|
||||
.font(.system(size: 12, weight: .semibold, design: .rounded))
|
||||
@@ -266,10 +264,13 @@ struct TimetableLiveActivity: Widget {
|
||||
.monospacedDigit()
|
||||
.frame(width: 50)
|
||||
} else {
|
||||
// No timer for xmas and newYearDay
|
||||
Text("")
|
||||
.frame(width: 50)
|
||||
}
|
||||
} else if context.state.isCancelled ?? false {
|
||||
Text("❌")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.frame(width: 50)
|
||||
} else {
|
||||
Text(timerInterval: context.state.currentTime...context.state.endTime, countsDown: true)
|
||||
.font(.system(size: 12, weight: .semibold, design: .rounded))
|
||||
@@ -346,30 +347,27 @@ struct TimetableLiveActivityView: View {
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
if mode == "beforeSchool" {
|
||||
Text("Hamarosan suli")
|
||||
Text(context.state.labels?.title ?? "Hamarosan suli")
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
} else if SeasonalIconHelper.isSeasonalMode(mode) {
|
||||
// Check if it's a special holiday with a prominent message
|
||||
if mode == "xmas" || mode == "newYearEve" || mode == "newYearDay" {
|
||||
// Global holidays: show message prominently
|
||||
Text(context.state.message ?? context.state.lessonName)
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(2)
|
||||
} else {
|
||||
// Seasonal breaks: show holiday title
|
||||
Text(SeasonalIconHelper.holidayTitle(for: context.state.season))
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
}
|
||||
} else if context.state.isBreak {
|
||||
Text("Szünet")
|
||||
Text(context.state.labels?.title ?? "Szünet")
|
||||
.font(.system(size: 20, weight: .bold))
|
||||
.foregroundColor(.white)
|
||||
if let nextLessonName = context.state.nextLessonName {
|
||||
Text("Következő: \(nextLessonName)")
|
||||
Text("\(context.state.labels?.nextLabel ?? "Következő:") \(nextLessonName)")
|
||||
.font(.system(size: 14))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
@@ -392,7 +390,7 @@ struct TimetableLiveActivityView: View {
|
||||
if SeasonalIconHelper.isSeasonalMode(mode) {
|
||||
EmptyView()
|
||||
} else if mode == "beforeSchool", let timeString = beforeSchoolTime {
|
||||
Text("Kezdés: \(timeString)")
|
||||
Text("\(context.state.labels?.startTimeLabel ?? "Kezdés:") \(timeString)")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
} else {
|
||||
@@ -407,7 +405,7 @@ struct TimetableLiveActivityView: View {
|
||||
EmptyView()
|
||||
} else if context.state.isBreak {
|
||||
if let _ = context.state.nextStartTime {
|
||||
Text("Kezdés: \(context.state.formattedNextStartTime)")
|
||||
Text("\(context.state.labels?.startTimeLabel ?? "Kezdés:") \(context.state.formattedNextStartTime)")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
@@ -434,17 +432,17 @@ struct TimetableLiveActivityView: View {
|
||||
if mode2 == "beforeSchool" {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 4) {
|
||||
Text("Első órád:")
|
||||
Text(context.state.labels?.firstLessonLabel ?? "Első órád:")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundColor(.gray)
|
||||
Text(context.state.lessonName)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
|
||||
if let roomName = context.state.roomName {
|
||||
HStack(spacing: 4) {
|
||||
Text("Terem:")
|
||||
Text(context.state.labels?.roomLabel ?? "Terem:")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundColor(.gray)
|
||||
Text(roomName)
|
||||
@@ -452,10 +450,10 @@ struct TimetableLiveActivityView: View {
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let teacherName = context.state.teacherName {
|
||||
HStack(spacing: 4) {
|
||||
Text("Tanár:")
|
||||
Text(context.state.labels?.teacherLabel ?? "Tanár:")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundColor(.gray)
|
||||
Text(teacherName)
|
||||
@@ -469,15 +467,17 @@ struct TimetableLiveActivityView: View {
|
||||
EmptyView()
|
||||
} else if !context.state.isBreak {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
if let lessonTheme = context.state.lessonTheme, !lessonTheme.isEmpty {
|
||||
HStack {
|
||||
Text("Téma:")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundColor(.gray)
|
||||
Text(lessonTheme)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
if !(context.state.isCancelled ?? false) {
|
||||
if let lessonTheme = context.state.lessonTheme, !lessonTheme.isEmpty {
|
||||
HStack {
|
||||
Text(context.state.labels?.themeLabel ?? "Téma:")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundColor(.gray)
|
||||
Text(lessonTheme)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.white)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +485,7 @@ struct TimetableLiveActivityView: View {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "arrow.triangle.2.circlepath")
|
||||
.font(.system(size: 12))
|
||||
Text("Helyettesítés")
|
||||
Text(context.state.labels?.substitutionText ?? "Helyettesítés")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
if let substituteTeacher = context.state.substituteTeacher {
|
||||
Text("(\(substituteTeacher))")
|
||||
@@ -494,16 +494,6 @@ struct TimetableLiveActivityView: View {
|
||||
}
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
|
||||
if context.state.isCancelled ?? false {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "xmark.circle")
|
||||
.font(.system(size: 12))
|
||||
Text("Elmaradt óra")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
}
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
} else if let nextRoomName = context.state.nextRoomName {
|
||||
@@ -553,25 +543,32 @@ struct TimetableLiveActivityView: View {
|
||||
.multilineTextAlignment(.center)
|
||||
.monospacedDigit()
|
||||
} else {
|
||||
let labelText: String = {
|
||||
if mode3 == "newYearEve" {
|
||||
return "Új év"
|
||||
} else if mode3 == "beforeSchool" {
|
||||
return "Első óra kezdése"
|
||||
} else if context.state.isBreak {
|
||||
return "Szünet vége"
|
||||
} else {
|
||||
return "Óra vége"
|
||||
}
|
||||
}()
|
||||
Text(labelText)
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.gray)
|
||||
Text(timerInterval: context.state.currentTime...context.state.endTime, countsDown: true)
|
||||
.font(.system(size: 32, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.green)
|
||||
.multilineTextAlignment(.center)
|
||||
.monospacedDigit()
|
||||
if context.state.isCancelled ?? false {
|
||||
Text(context.state.labels?.cancelledText ?? "Elmaradt")
|
||||
.font(.system(size: 32, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.red)
|
||||
.multilineTextAlignment(.center)
|
||||
} else {
|
||||
let labelText: String = {
|
||||
if mode3 == "newYearEve" {
|
||||
return "Új év"
|
||||
} else if mode3 == "beforeSchool" {
|
||||
return context.state.labels?.timerLabel ?? "Első óra kezdése"
|
||||
} else if context.state.isBreak {
|
||||
return context.state.labels?.timerLabel ?? "Szünet vége"
|
||||
} else {
|
||||
return context.state.labels?.timerLabel ?? "Óra vége"
|
||||
}
|
||||
}()
|
||||
Text(labelText)
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.gray)
|
||||
Text(timerInterval: context.state.currentTime...context.state.endTime, countsDown: true)
|
||||
.font(.system(size: 32, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.green)
|
||||
.multilineTextAlignment(.center)
|
||||
.monospacedDigit()
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
|
||||
@@ -434,7 +434,46 @@ class LiveActivityService {
|
||||
final endOfWeek = startOfWeek.add(const Duration(days: 6));
|
||||
|
||||
final timetableResponse = await client.getTimeTable(startOfWeek, endOfWeek);
|
||||
final allLessons = timetableResponse.response ?? [];
|
||||
List<Lesson> allLessons = timetableResponse.response ?? [];
|
||||
|
||||
final nextMonday = endOfWeek.add(const Duration(days: 1));
|
||||
final nextMondayEnd = nextMonday.add(const Duration(days: 1));
|
||||
|
||||
try {
|
||||
final nextMondayResponse = await client.getTimeTable(nextMonday, nextMondayEnd);
|
||||
if (nextMondayResponse.response != null && nextMondayResponse.response!.isNotEmpty) {
|
||||
final mondayLessons = nextMondayResponse.response!;
|
||||
mondayLessons.sort((a, b) => a.start.compareTo(b.start));
|
||||
final firstLesson = mondayLessons.first;
|
||||
|
||||
final markedLesson = Lesson(
|
||||
uid: '${firstLesson.uid}__FOR_NOTIFICATION_ONLY',
|
||||
date: firstLesson.date,
|
||||
start: firstLesson.start,
|
||||
end: firstLesson.end,
|
||||
name: firstLesson.name,
|
||||
lessonNumber: firstLesson.lessonNumber,
|
||||
teacher: firstLesson.teacher,
|
||||
theme: firstLesson.theme,
|
||||
roomName: firstLesson.roomName,
|
||||
substituteTeacher: firstLesson.substituteTeacher,
|
||||
type: firstLesson.type,
|
||||
state: firstLesson.state,
|
||||
canStudentEditHomework: firstLesson.canStudentEditHomework,
|
||||
isHomeworkComplete: firstLesson.isHomeworkComplete,
|
||||
attachments: firstLesson.attachments,
|
||||
isDigitalLesson: firstLesson.isDigitalLesson,
|
||||
digitalSupportDeviceTypeList: firstLesson.digitalSupportDeviceTypeList,
|
||||
createdAt: firstLesson.createdAt ?? firstLesson.lastModifiedAt ?? DateTime.now(),
|
||||
lastModifiedAt: firstLesson.lastModifiedAt,
|
||||
);
|
||||
|
||||
allLessons.add(markedLesson);
|
||||
_logger.info('Added first lesson from next Monday (${firstLesson.name}) marked for notification scheduling only');
|
||||
}
|
||||
} catch (e) {
|
||||
_logger.warning('Could not fetch next Monday first lesson: $e');
|
||||
}
|
||||
|
||||
if (allLessons.isEmpty) {
|
||||
await LiveActivityManager.endAllActivities();
|
||||
|
||||
@@ -96,6 +96,30 @@ class _HomeScreenState extends FirkaState<HomeScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void _setupNotificationListener() {
|
||||
final notificationChannel = MethodChannel('firka.app/notifications');
|
||||
|
||||
notificationChannel.setMethodCallHandler((call) async {
|
||||
if (call.method == 'onNotificationTapped') {
|
||||
logger.info('Notification tapped: ${call.arguments}');
|
||||
|
||||
final args = call.arguments as Map<Object?, Object?>?;
|
||||
if (args == null) return;
|
||||
|
||||
final action = args['action'] as String?;
|
||||
final route = args['route'] as String?;
|
||||
|
||||
if (action != null || route != null) {
|
||||
logger.info('Navigating to timetable from notification');
|
||||
setState(() {
|
||||
homeScreenPage = HomePage.timetable;
|
||||
_pageController.jumpToPage(HomePage.timetable.index);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void prefetch() async {
|
||||
if (_prefetched) return;
|
||||
|
||||
@@ -245,6 +269,8 @@ class _HomeScreenState extends FirkaState<HomeScreen> {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
|
||||
_setupNotificationListener();
|
||||
|
||||
prefetch();
|
||||
_preloadImages();
|
||||
|
||||
|
||||
@@ -719,6 +719,31 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
|
||||
));
|
||||
continue;
|
||||
}
|
||||
if (item is SettingsButton) {
|
||||
widgets.add(GestureDetector(
|
||||
child: FirkaCard(
|
||||
left: [
|
||||
item.iconType != null
|
||||
? Row(
|
||||
children: [
|
||||
FirkaIconWidget(item.iconType!, item.iconData!,
|
||||
color: appStyle.colors.accent),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
)
|
||||
: SizedBox(),
|
||||
Text(item.title,
|
||||
style: appStyle.fonts.B_16SB
|
||||
.apply(color: appStyle.colors.textPrimary))
|
||||
],
|
||||
),
|
||||
onTap: () async {
|
||||
await item.onTap();
|
||||
},
|
||||
));
|
||||
|
||||
continue;
|
||||
}
|
||||
if (item is SettingsLogs) {
|
||||
final logFileRegex = RegExp(r'^(\d{4})_(\d{2})_(\d{2})\.log$');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user