Frontend events are being returned from the API (yey!)

This commit is contained in:
rzuasti
2026-02-27 12:28:16 -05:00
parent 874a70969f
commit 2d64efa44b
14 changed files with 232 additions and 126 deletions
+3
View File
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
+52 -80
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:frontend/utils/friendly_date_formatter.dart';
import '../utils/friendly_date_formatter.dart';
import '../model/event.dart';
import '../model/device_type.dart';
import '../utils/oott_api.dart';
class EventsList extends StatefulWidget {
@override
@@ -9,91 +9,63 @@ class EventsList extends StatefulWidget {
}
class _EventListState extends State<EventsList> {
// Dummy events to display
// TODO : Replace with API call
List<Event> events = <Event>[
Event(
id: 5,
title: 'New device found',
description: 'Apple device @ 192.168.0.1 with MAC 56:e7:e3:07:d2:d5',
isRead: false,
deviceType: DeviceType.phone,
dateTime: DateTime.now().subtract(Duration(minutes: 22)),
),
Event(
id: 4,
title: 'New device found',
description: 'Apple device @ 192.168.0.1 with MAC 56:e7:e3:07:d2:d5',
isRead: false,
deviceType: DeviceType.pc,
dateTime: DateTime.now().subtract(Duration(minutes: 87)),
),
Event(
id: 3,
title: 'New device found',
description: 'Apple device @ 192.168.0.1 with MAC 56:e7:e3:07:d2:d5',
isRead: false,
deviceType: DeviceType.tablet,
dateTime: DateTime.now().subtract(Duration(hours: 23)),
),
Event(
id: 2,
title: 'New device found',
description: 'Apple device @ 192.168.0.1 with MAC 56:e7:e3:07:d2:d5',
isRead: false,
deviceType: DeviceType.server,
dateTime: DateTime(2026, 01, 19, 14, 47),
),
Event(
id: 1,
title: 'New device found',
description: 'Apple device @ 192.168.0.1 with MAC 56:e7:e3:07:d2:d5',
isRead: false,
deviceType: DeviceType.phone,
dateTime: DateTime(2026, 01, 17, 14, 47),
),
];
bool _isLoading = true;
List<Event>? _events;
@override
void initState() {
super.initState();
_getData();
}
void _getData() async {
_events = await BackendAPI.instance.listNotifications();
_isLoading = false;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Events')),
body: ListView.separated(
itemCount: events.length,
itemBuilder: (context, index) {
return Dismissible(
key: UniqueKey(),
onDismissed: (direction) {
setState(() {
events.removeAt(index);
});
body: _isLoading
? const Center(child: CircularProgressIndicator())
: ListView.separated(
itemCount: _events!.length,
itemBuilder: (context, index) {
return Dismissible(
key: UniqueKey(),
onDismissed: (direction) {
setState(() {
_events!.removeAt(index);
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Event marked as read'),
behavior: SnackBarBehavior.floating,
showCloseIcon: true,
),
);
},
background: Container(
color: Theme.of(context).colorScheme.primaryContainer,
alignment: Alignment.center,
child: Icon(Icons.done),
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Event marked as read'),
behavior: SnackBarBehavior.floating,
showCloseIcon: true,
),
);
},
background: Container(
color: Theme.of(context).colorScheme.primaryContainer,
alignment: Alignment.center,
child: Icon(Icons.done),
),
child: ListTile(
leading: Icon(_events![index].notificationType.icon),
title: Text(
'${FriendlyDateFormatter().format(_events![index].createdOn)} - ${_events![index].title}',
),
subtitle: Text(_events![index].body),
trailing: Icon(Icons.more_vert),
onTap: () {},
),
);
},
separatorBuilder: (context, index) => Divider(),
),
child: ListTile(
leading: Icon(events[index].deviceType.icon),
title: Text(
'${FriendlyDateFormatter().format(events[index].dateTime)} - ${events[index].title}',
),
subtitle: Text(events[index].description),
trailing: Icon(Icons.more_vert),
onTap: () {},
),
);
},
separatorBuilder: (context, index) => Divider(),
),
);
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
final class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
-21
View File
@@ -1,21 +0,0 @@
import 'package:flutter/material.dart';
enum DeviceType {
phone,
pc,
tablet,
server;
IconData get icon {
switch (this) {
case phone:
return Icons.phone_iphone;
case pc:
return Icons.computer;
case tablet:
return Icons.tablet;
case server:
return Icons.storage;
}
}
}
+28 -9
View File
@@ -1,19 +1,38 @@
import 'device_type.dart';
import 'notification_type.dart';
class Event {
int id;
String title;
String description;
bool isRead;
DeviceType deviceType;
DateTime dateTime;
String body;
bool isNew;
NotificationType notificationType;
DateTime createdOn;
Event({
required this.id,
required this.title,
required this.description,
required this.deviceType,
required this.dateTime,
this.isRead = false,
required this.body,
required this.notificationType,
required this.createdOn,
this.isNew = true,
});
Event.fromJson(Map<String, dynamic> json)
: id = json['id'] as int,
createdOn = DateTime.parse(json['created_on'] as String),
notificationType = NotificationType.fromString(
json['notification_type'] as String,
),
title = json['title'] as String,
body = json['body'] as String,
isNew = json['is_new'] as bool;
Map<String, dynamic> toJson() => {
'id': id,
'created_on': createdOn.toUtc().toIso8601String(),
'notification_type': notificationType.toString,
'title': title,
'body': body,
'is_new': isNew,
};
}
+29
View File
@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
enum NotificationType {
newDeviceFound,
deviceOnlineAfterTime,
deviceChanged,
other;
IconData get icon {
switch (this) {
case newDeviceFound:
return Icons.devices;
case deviceOnlineAfterTime:
return Icons.timer;
case deviceChanged:
return Icons.change_circle;
case other:
return Icons.priority_high;
}
}
static NotificationType fromString(String value) =>
switch (value.toLowerCase()) {
"newdevicefound" => newDeviceFound,
"deviceonlineaftertime" => deviceOnlineAfterTime,
"devicechanged" => deviceChanged,
_ => other,
};
}
+44
View File
@@ -0,0 +1,44 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:frontend/model/event.dart';
class BackendAPI {
BackendAPI._singleton();
static final BackendAPI instance = BackendAPI._singleton();
static final String _baseUrl = 'http://localhost:3000/api';
static final String _apiKey = 'super_secret';
final Dio _dio = Dio(
BaseOptions(
baseUrl: _baseUrl,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
HttpHeaders.authorizationHeader: 'Bearer $_apiKey',
},
),
);
Future<List<Event>> listNotifications() async {
print('About to call /notifications');
Response response;
response = await _dio.get('/notifications');
print('Received: ' + response.data.toString());
List<dynamic> list = response.data;
print('List contains ' + list.length.toString() + ' items');
List<Event> events = List<Event>.from(
list.map((item) => Event.fromJson(item)),
);
print('Parsed ' + events.length.toString() + ' events');
return events;
}
}
+56 -8
View File
@@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.4.1"
clock:
dependency: transitive
description:
@@ -41,6 +41,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
dio:
dependency: "direct main"
description:
name: dio
sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25
url: "https://pub.dev"
source: hosted
version: "5.9.1"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
fake_async:
dependency: transitive
description:
@@ -80,6 +96,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "17.1.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
intl:
dependency: "direct main"
description:
@@ -132,18 +156,18 @@ packages:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
url: "https://pub.dev"
source: hosted
version: "0.12.17"
version: "0.12.18"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.13.0"
meta:
dependency: transitive
description:
@@ -152,6 +176,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.17.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
nested:
dependency: transitive
description:
@@ -225,10 +257,18 @@ packages:
dependency: transitive
description:
name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
url: "https://pub.dev"
source: hosted
version: "0.7.7"
version: "0.7.9"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math:
dependency: transitive
description:
@@ -245,6 +285,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.2"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
sdks:
dart: ">=3.10.8 <4.0.0"
flutter: ">=3.35.0"
+1
View File
@@ -12,6 +12,7 @@ dependencies:
go_router: ^17.1.0
intl: ^0.20.2
provider: ^6.1.5
dio: ^5.9.1
dev_dependencies:
flutter_test: