home: add info board list

This commit is contained in:
2025-09-01 17:30:27 +02:00
parent 01dcec0fc5
commit f3f6e2f3ee
8 changed files with 227 additions and 54 deletions

View File

@@ -214,6 +214,34 @@ class KretaClient {
return ApiResponse(items, status, err, cached);
}
ApiResponse<List<InfoBoardItem>>? infoBoardCache;
Future<ApiResponse<List<InfoBoardItem>>> getInfoBoard(
{bool forceCache = true}) async {
if (forceCache && infoBoardCache != null) return infoBoardCache!;
var (resp, status, ex, cached) = await _cachingGet(CacheId.getInfoBoard,
KretaEndpoints.getInfoBoard(model.iss!), forceCache, 0);
var items = List<InfoBoardItem>.empty(growable: true);
String? err;
try {
List<dynamic> rawItems = resp;
for (var item in rawItems) {
items.add(InfoBoardItem.fromJson(item));
}
} catch (ex) {
err = ex.toString();
}
if (ex != null) {
err = ex.toString();
}
if (err == null) infoBoardCache = ApiResponse(items, 200, null, true);
return ApiResponse(items, status, err, cached);
}
ApiResponse<List<Grade>>? gradeCache;
Future<ApiResponse<List<Grade>>> getGrades({bool forceCache = true}) async {

View File

@@ -67,6 +67,12 @@ class KretaEndpoints {
static String getNoticeBoard(String iss) =>
"${kreta(iss)}/ellenorzo/v3/sajat/FaliujsagElemek";
// for some reason the [redacted] devs decided to make
// two different apis to get items for the notice board
// that appears on the home screen, like wtf
static String getInfoBoard(String iss) =>
"${kreta(iss)}/ellenorzo/v3/sajat/Feljegyzesek";
static String getGrades(String iss) =>
"${kreta(iss)}/ellenorzo/v3/sajat/Ertekelesek";

View File

@@ -1,3 +1,5 @@
import 'package:firka/helpers/api/model/generic.dart';
class NoticeBoardItem {
final String uid;
final String author;
@@ -40,3 +42,50 @@ class NoticeBoardItem {
')';
}
}
class InfoBoardItem {
final String uid;
final String title;
final DateTime date;
final String author;
final DateTime createdAt;
final String contentHTML;
final String contentText;
final NameUidDesc type;
InfoBoardItem(
{required this.uid,
required this.title,
required this.date,
required this.author,
required this.createdAt,
required this.contentHTML,
required this.contentText,
required this.type});
factory InfoBoardItem.fromJson(Map<String, dynamic> json) {
return InfoBoardItem(
uid: json['Uid'],
title: json['Cim'],
date: DateTime.parse(json['Datum']),
author: json['KeszitoTanarNeve'],
createdAt: DateTime.parse(json['KeszitesDatuma']),
contentText: json['Tartalom'],
contentHTML: json['TartalomFormazott'],
type: NameUidDesc.fromJson(json['Tipus']));
}
@override
String toString() {
return 'InfoBoard('
'uid: "$uid", '
'title: "$title", '
'date: "$date", '
'author: "$author", '
'createdAt: "$createdAt", '
'contentText: "$contentText", '
'contentHTML: "$contentHTML", '
'type: $type'
')';
}
}

View File

@@ -2,7 +2,14 @@ import 'package:isar/isar.dart';
part 'generic_cache_model.g.dart';
enum CacheId { getStudent, getNoticeBoard, getGrades, getOmissions, getTests }
enum CacheId {
getStudent,
getNoticeBoard,
getInfoBoard,
getGrades,
getOmissions,
getTests
}
@collection
class GenericCacheModel {

View File

@@ -2,10 +2,12 @@ import 'dart:async';
import 'package:firka/helpers/extensions.dart';
import 'package:firka/ui/phone/widgets/home_main_starting_soon.dart';
import 'package:firka/ui/phone/widgets/info_board_item.dart';
import 'package:firka/ui/phone/widgets/lesson_small.dart';
import 'package:firka/ui/widget/delayed_spinner.dart';
import 'package:flutter/material.dart';
import '../../../../helpers/api/model/notice_board.dart';
import '../../../../helpers/api/model/student.dart';
import '../../../../helpers/api/model/timetable.dart';
import '../../../../helpers/debug_helper.dart';
@@ -31,6 +33,8 @@ class _HomeMainScreen extends State<HomeMainScreen> {
DateTime now = timeNow();
List<Lesson>? lessons;
List<NoticeBoardItem>? noticeBoard;
List<InfoBoardItem>? infoBoard;
Student? student;
Timer? timer;
@@ -48,24 +52,36 @@ class _HomeMainScreen extends State<HomeMainScreen> {
if (mounted) {
setState(() {
lessons = newData.$1;
student = newData.$2;
noticeBoard = newData.$2;
infoBoard = newData.$3;
student = newData.$4;
});
}
widget.finishNotifier.update();
}
Future<(List<Lesson>, Student)> loadData(DateTime now,
{bool forceCache = true}) async {
Future<(List<Lesson>, List<NoticeBoardItem>, List<InfoBoardItem>, Student)>
loadData(DateTime now, {bool forceCache = true}) async {
var midnight = now.getMidnight();
var respTT = await widget.data.client.getTimeTable(
midnight, midnight.add(Duration(hours: 23, minutes: 59)),
forceCache: forceCache);
var respNB =
await widget.data.client.getNoticeBoard(forceCache: forceCache);
var respIB = await widget.data.client.getInfoBoard(forceCache: forceCache);
var respStudent =
await widget.data.client.getStudent(forceCache: forceCache);
return Future.value((respTT.response!, respStudent.response!));
return Future.value((
respTT.response!,
respNB.response!,
respIB.response!,
respStudent.response!
));
}
@override
@@ -83,7 +99,9 @@ class _HomeMainScreen extends State<HomeMainScreen> {
if (mounted) {
setState(() {
lessons = newData.$1;
student = newData.$2;
noticeBoard = newData.$2;
infoBoard = newData.$3;
student = newData.$4;
});
}
})();
@@ -111,7 +129,7 @@ class _HomeMainScreen extends State<HomeMainScreen> {
Widget nextClass = SizedBox();
bool lessonActive = false;
if (lessons != null && lessons!.isNotEmpty) {
if (lessons != null && noticeBoard != null && lessons!.isNotEmpty) {
if (now.isBefore(lessons!.first.start)) {
welcomeWidget = StartingSoonWidget(widget.data.l10n, now, lessons!);
} else {
@@ -141,24 +159,37 @@ class _HomeMainScreen extends State<HomeMainScreen> {
}
}
if (student != null && lessons != null) {
if (student != null && noticeBoard != null && lessons != null) {
List<Widget> noticeBoardWidgets = List.empty(growable: true);
// TODO: Add notice board items once we actually have those
for (var item in infoBoard!) {
noticeBoardWidgets.add(InfoBoardItemWidget(item));
}
return Padding(
padding: const EdgeInsets.only(
left: 20.0,
top: 24.0,
right: 20.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
WelcomeWidget(widget.data.l10n, now, student!, lessons!),
SizedBox(height: 48),
welcomeWidget,
lessonActive ? SizedBox(height: 5) : SizedBox(height: 0),
nextClass
],
),
);
padding: const EdgeInsets.only(
left: 20.0,
top: 24.0,
right: 20.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
WelcomeWidget(widget.data.l10n, now, student!, lessons!),
SizedBox(height: 48),
welcomeWidget,
lessonActive ? SizedBox(height: 5) : SizedBox(height: 0),
nextClass,
SizedBox(
height: MediaQuery.of(context).size.height / 1.6,
child: ListView(
children: noticeBoardWidgets,
),
)
],
),
);
} else {
return DelayedSpinnerWidget();
}

View File

@@ -0,0 +1,66 @@
import 'package:firka/helpers/ui/firka_card.dart';
import 'package:firka/ui/model/style.dart';
import 'package:flutter/material.dart';
import '../../../helpers/api/model/notice_board.dart';
// TODO: Finish
class InfoBoardItemWidget extends StatelessWidget {
final InfoBoardItem item;
const InfoBoardItemWidget(this.item, {super.key});
@override
Widget build(BuildContext context) {
return FirkaCard(left: [
Row(
children: [
Container(
decoration: ShapeDecoration(
color: appStyle.colors.accent,
shape: CircleBorder(
eccentricity: 1,
// borderRadius: BorderRadius.circular(6)),
)),
child: SizedBox(
width: 28,
height: 28,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 6),
child: Text(
item.author[0],
style: appStyle.fonts.H_18px.copyWith(
fontSize: 20, color: appStyle.colors.textPrimary),
),
)
],
),
),
),
SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.of(context).size.width / 1.4,
child: Text(
item.title,
style: appStyle.fonts.B_14SB
.apply(color: appStyle.colors.textPrimary),
),
),
Text(
item.author,
style: appStyle.fonts.B_14R
.apply(color: appStyle.colors.textSecondary),
)
],
)
],
)
]);
}
}

View File

@@ -0,0 +1,16 @@
import 'package:firka/helpers/ui/firka_card.dart';
import 'package:flutter/material.dart';
import '../../../helpers/api/model/notice_board.dart';
// TODO: Finish
class NoticeBoardItemWidget extends StatelessWidget {
final NoticeBoardItem item;
const NoticeBoardItemWidget(this.item, {super.key});
@override
Widget build(BuildContext context) {
return FirkaCard(left: [Text(item.title)]);
}
}

View File

@@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:firka/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}