diff --git a/firka/ios/HomeWidgetsExtension/Models/Grade.swift b/firka/ios/HomeWidgetsExtension/Models/Grade.swift index d44a9c95..8bb36401 100644 --- a/firka/ios/HomeWidgetsExtension/Models/Grade.swift +++ b/firka/ios/HomeWidgetsExtension/Models/Grade.swift @@ -37,4 +37,15 @@ struct WidgetGrade: Codable, Identifiable { default: return .gray } } + + var subjectNameWithWeight: String { + if let weight = weightPercentage, weight != 100 { + return "\(subject.name) (\(weight)%)" + } + return subject.name + } + + var teacherName: String? { + subject.teacherName + } } diff --git a/firka/ios/HomeWidgetsExtension/Providers/AveragesProvider.swift b/firka/ios/HomeWidgetsExtension/Providers/AveragesProvider.swift index f4db0270..5f92eedd 100644 --- a/firka/ios/HomeWidgetsExtension/Providers/AveragesProvider.swift +++ b/firka/ios/HomeWidgetsExtension/Providers/AveragesProvider.swift @@ -7,6 +7,7 @@ struct AveragesEntry: TimelineEntry { let overallAverage: Double? let subjectAverages: [SubjectAverage] let locale: String + let isFiltered: Bool } struct AveragesProvider: AppIntentTimelineProvider { @@ -19,7 +20,8 @@ struct AveragesProvider: AppIntentTimelineProvider { configuration: AveragesWidgetIntent(), overallAverage: nil, subjectAverages: [], - locale: "hu" + locale: "hu", + isFiltered: false ) } @@ -39,9 +41,10 @@ struct AveragesProvider: AppIntentTimelineProvider { let data = WidgetData.load() var subjectAverages = data?.averages.subjects ?? [] + let isFiltered = configuration.selectedSubjects?.isEmpty == false - if let selectedSubjects = configuration.selectedSubjects, !selectedSubjects.isEmpty { - let selectedIds = Set(selectedSubjects.map { $0.id }) + if isFiltered { + let selectedIds = Set(configuration.selectedSubjects!.map { $0.id }) subjectAverages = subjectAverages.filter { selectedIds.contains($0.uid) } } @@ -50,7 +53,8 @@ struct AveragesProvider: AppIntentTimelineProvider { configuration: configuration, overallAverage: data?.averages.overall, subjectAverages: subjectAverages, - locale: data?.locale ?? "hu" + locale: data?.locale ?? "hu", + isFiltered: isFiltered ) } } diff --git a/firka/ios/HomeWidgetsExtension/Providers/TimetableProvider.swift b/firka/ios/HomeWidgetsExtension/Providers/TimetableProvider.swift index b58a50fa..a0af11ef 100644 --- a/firka/ios/HomeWidgetsExtension/Providers/TimetableProvider.swift +++ b/firka/ios/HomeWidgetsExtension/Providers/TimetableProvider.swift @@ -73,17 +73,25 @@ struct TimetableProvider: AppIntentTimelineProvider { entries.append(createEntry(for: configuration, date: now)) + var transitionTimes: Set = [] + for lesson in todayLessons { if lesson.start > now { - entries.append(createEntry(for: configuration, date: lesson.start)) + transitionTimes.insert(lesson.start) } if lesson.end > now { - entries.append(createEntry(for: configuration, date: lesson.end.addingTimeInterval(1))) + transitionTimes.insert(lesson.end.addingTimeInterval(1)) } } let midnight = Calendar.current.startOfDay(for: now.addingTimeInterval(86400)) - entries.append(createEntry(for: configuration, date: midnight)) + transitionTimes.insert(midnight) + + for time in transitionTimes { + entries.append(createEntry(for: configuration, date: time)) + } + + entries.sort { $0.date < $1.date } return Timeline(entries: entries, policy: .atEnd) } @@ -166,8 +174,7 @@ struct TimetableProvider: AppIntentTimelineProvider { } let currentLesson = lessons.first { lesson in - let now = Date() - return now >= lesson.start && now <= lesson.end + return date >= lesson.start && date <= lesson.end } let nextLesson = lessons.first { $0.start > date } diff --git a/firka/ios/HomeWidgetsExtension/Views/AveragesViews.swift b/firka/ios/HomeWidgetsExtension/Views/AveragesViews.swift index cf23934d..3ac01f2b 100644 --- a/firka/ios/HomeWidgetsExtension/Views/AveragesViews.swift +++ b/firka/ios/HomeWidgetsExtension/Views/AveragesViews.swift @@ -47,20 +47,46 @@ struct AveragesSmallView: View { struct AveragesMediumView: View { let entry: AveragesEntry let localization: WidgetLocalization + private let maxVisible = 4 var style: WidgetStyleType { (entry.configuration.style ?? .appTheme) == .liquidGlass ? .liquidGlass : .appTheme } + var totalCount: Int { + entry.subjectAverages.count + } + + var showingCount: Int { + min(totalCount, maxVisible) + } + var body: some View { ZStack { WidgetBackground(style: style, colors: nil) VStack(alignment: .leading, spacing: 8) { - Text(localization.string("subject_averages")) - .font(.caption) - .fontWeight(.medium) - .widgetTextStyle(style, colors: nil, isPrimary: false) + HStack { + Text(localization.string("subject_averages")) + .font(.caption) + .fontWeight(.medium) + .widgetTextStyle(style, colors: nil, isPrimary: false) + + Spacer() + + if entry.isFiltered && totalCount > maxVisible { + Text("\(showingCount)/\(totalCount)") + .font(.caption2) + .fontWeight(.medium) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background( + Capsule() + .fill(Color.secondary.opacity(0.2)) + ) + .widgetTextStyle(style, colors: nil, isPrimary: false) + } + } if entry.subjectAverages.isEmpty { Spacer() @@ -69,7 +95,7 @@ struct AveragesMediumView: View { .widgetTextStyle(style, colors: nil, isPrimary: false) Spacer() } else { - ForEach(entry.subjectAverages.prefix(4)) { subject in + ForEach(entry.subjectAverages.prefix(maxVisible)) { subject in AverageRow(subject: subject, style: style) } Spacer() @@ -83,11 +109,20 @@ struct AveragesMediumView: View { struct AveragesLargeView: View { let entry: AveragesEntry let localization: WidgetLocalization + private let maxVisible = 9 var style: WidgetStyleType { (entry.configuration.style ?? .appTheme) == .liquidGlass ? .liquidGlass : .appTheme } + var totalCount: Int { + entry.subjectAverages.count + } + + var showingCount: Int { + min(totalCount, maxVisible) + } + var body: some View { ZStack { WidgetBackground(style: style, colors: nil) @@ -101,6 +136,19 @@ struct AveragesLargeView: View { Spacer() + if entry.isFiltered && totalCount > maxVisible { + Text("\(showingCount)/\(totalCount)") + .font(.caption) + .fontWeight(.medium) + .padding(.horizontal, 6) + .padding(.vertical, 2) + .background( + Capsule() + .fill(Color.secondary.opacity(0.2)) + ) + .widgetTextStyle(style, colors: nil, isPrimary: false) + } + if let overall = entry.overallAverage { Text(String(format: "%.2f", overall)) .font(.headline) @@ -116,7 +164,7 @@ struct AveragesLargeView: View { .widgetTextStyle(style, colors: nil, isPrimary: false) Spacer() } else { - ForEach(entry.subjectAverages.prefix(8)) { subject in + ForEach(entry.subjectAverages.prefix(maxVisible)) { subject in AverageRow(subject: subject, style: style, showGradeCount: true) } Spacer() diff --git a/firka/ios/HomeWidgetsExtension/Views/GradesViews.swift b/firka/ios/HomeWidgetsExtension/Views/GradesViews.swift index 22bb9d0d..ab69ce5c 100644 --- a/firka/ios/HomeWidgetsExtension/Views/GradesViews.swift +++ b/firka/ios/HomeWidgetsExtension/Views/GradesViews.swift @@ -29,7 +29,7 @@ struct GradesSmallView: View { Spacer() } - Text(grade.subject.name) + Text(grade.subjectNameWithWeight) .font(.subheadline) .fontWeight(.medium) .widgetTextStyle(style, colors: nil) @@ -78,7 +78,7 @@ struct GradesMediumView: View { Spacer() } else { ForEach(entry.grades.prefix(3)) { grade in - GradeRow(grade: grade, style: style, showType: true) + GradeRow(grade: grade, style: style, showTeacher: true) } } } @@ -114,7 +114,7 @@ struct GradesLargeView: View { Spacer() } else { ForEach(entry.grades.prefix(6)) { grade in - GradeRow(grade: grade, style: style, showType: true, showTopic: true) + GradeRow(grade: grade, style: style, showTeacher: true, showTopic: true) } } } @@ -127,7 +127,7 @@ struct GradesLargeView: View { struct GradeRow: View { let grade: WidgetGrade let style: WidgetStyleType - var showType: Bool = false + var showTeacher: Bool = false var showTopic: Bool = false var body: some View { @@ -139,23 +139,24 @@ struct GradeRow: View { .frame(width: 32) VStack(alignment: .leading, spacing: 2) { - Text(grade.subject.name) + Text(grade.subjectNameWithWeight) .font(.subheadline) .fontWeight(.medium) .widgetTextStyle(style, colors: nil) .lineLimit(1) HStack(spacing: 4) { - if showType { - Text(grade.type.name) + if showTeacher, let teacher = grade.teacherName { + Text(teacher) + .font(.caption) + .widgetTextStyle(style, colors: nil, isPrimary: false) + .lineLimit(1) + + Text("•") .font(.caption) .widgetTextStyle(style, colors: nil, isPrimary: false) } - Text("•") - .font(.caption) - .widgetTextStyle(style, colors: nil, isPrimary: false) - Text(grade.dateString) .font(.caption) .widgetTextStyle(style, colors: nil, isPrimary: false) diff --git a/firka/ios/HomeWidgetsExtension/Views/TimetableViews.swift b/firka/ios/HomeWidgetsExtension/Views/TimetableViews.swift index 1fcf8e5d..13334db6 100644 --- a/firka/ios/HomeWidgetsExtension/Views/TimetableViews.swift +++ b/firka/ios/HomeWidgetsExtension/Views/TimetableViews.swift @@ -79,9 +79,36 @@ struct TimetableMediumView: View { (entry.configuration.style ?? .appTheme) == .liquidGlass ? .liquidGlass : .appTheme } - var remainingLessons: [WidgetLesson] { - let now = Date() - return entry.lessons.filter { $0.end > now } + func isLessonActive(_ lesson: WidgetLesson) -> Bool { + let checkDate = entry.date + return checkDate >= lesson.start && checkDate <= lesson.end + } + + var currentLessonIndex: Int { + let checkDate = entry.date + if let index = entry.lessons.firstIndex(where: { checkDate >= $0.start && checkDate <= $0.end }) { + return index + } + if let index = entry.lessons.firstIndex(where: { $0.start > checkDate }) { + return index + } + return max(0, entry.lessons.count - 1) + } + + var visibleLessons: [WidgetLesson] { + let totalLessons = entry.lessons.count + let maxVisible = 4 + + if totalLessons <= maxVisible { + return Array(entry.lessons) + } + + var startIndex = max(0, currentLessonIndex - 1) + + startIndex = min(startIndex, totalLessons - maxVisible) + + let endIndex = min(startIndex + maxVisible, totalLessons) + return Array(entry.lessons[startIndex.. now } + func isLessonActive(_ lesson: WidgetLesson) -> Bool { + let checkDate = entry.date + return checkDate >= lesson.start && checkDate <= lesson.end } var body: some View { @@ -127,8 +154,8 @@ struct TimetableLargeView: View { .fontWeight(.semibold) .widgetTextStyle(style, colors: nil) - ForEach(remainingLessons.prefix(7)) { lesson in - LessonRow(lesson: lesson, isActive: lesson.isCurrentlyActive, style: style, showRoom: true) + ForEach(entry.lessons.prefix(7)) { lesson in + LessonRow(lesson: lesson, isActive: isLessonActive(lesson), style: style, showRoom: true) } } .padding() diff --git a/firka/lib/helpers/db/ios_widget_helper.dart b/firka/lib/helpers/db/ios_widget_helper.dart index 325975e9..cecebb86 100644 --- a/firka/lib/helpers/db/ios_widget_helper.dart +++ b/firka/lib/helpers/db/ios_widget_helper.dart @@ -108,10 +108,10 @@ class IOSWidgetHelper { 'subject': subject != null ? { 'uid': subject.uid, 'name': subject.name, - 'category': subject.category != null ? { - 'uid': subject.category!.uid, - 'name': subject.category!.name, - 'description': subject.category!.description, + 'category': subject.category, { + 'uid': subject.category.uid, + 'name': subject.category.name, + 'description': subject.category.description, } : null, 'sortIndex': subject.sortIndex, 'teacherName': subject.teacherName, @@ -132,17 +132,18 @@ class IOSWidgetHelper { static Map _gradeToJson(Grade grade) { return { 'uid': grade.uid, - 'recordDate': _formatDateTimeWithOffset(grade.recordDate), + 'recordDate': _formatDateTimeWithOffset(grade.creationDate), 'subject': { 'uid': grade.subject.uid, 'name': grade.subject.name, - 'category': grade.subject.category != null ? { - 'uid': grade.subject.category!.uid, - 'name': grade.subject.category!.name, - 'description': grade.subject.category!.description, + 'category': grade.subject.category, { + 'uid': grade.subject.category.uid, + 'name': grade.subject.category.name, + 'description': grade.subject.category.description, } : null, 'sortIndex': grade.subject.sortIndex, - 'teacherName': grade.subject.teacherName, + // Use the grade's teacher field, not subject.teacherName (which is usually null for grades) + 'teacherName': grade.teacher, }, 'topic': grade.topic, 'type': {