mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 13:33:32 +00:00
Initial rudimentary notifications viewer and back end
This commit is contained in:
parent
6af1c4f214
commit
f4dee56650
7 changed files with 191 additions and 8 deletions
28
lib/controls/notifications_control.dart
Normal file
28
lib/controls/notifications_control.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||
|
||||
import '../models/user_notification.dart';
|
||||
import '../utils/dateutils.dart';
|
||||
|
||||
class NotificationControl extends StatelessWidget {
|
||||
final UserNotification notification;
|
||||
|
||||
const NotificationControl({
|
||||
super.key,
|
||||
required this.notification,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
tileColor: notification.seen ? null : Colors.black12,
|
||||
leading: Text(notification.fromName),
|
||||
title: HtmlWidget(notification.content),
|
||||
subtitle:
|
||||
Text(ElapsedDateUtils.epochSecondsToString(notification.timestamp)),
|
||||
trailing: notification.seen
|
||||
? null
|
||||
: IconButton(onPressed: () {}, icon: Icon(Icons.close_rounded)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ import 'models/TimelineIdentifiers.dart';
|
|||
import 'models/credentials.dart';
|
||||
import 'models/exec_error.dart';
|
||||
import 'models/timeline_entry.dart';
|
||||
import 'models/user_notification.dart';
|
||||
import 'serializers/friendica/notification_friendica_extension.dart';
|
||||
import 'serializers/mastodon/timeline_entry_mastodon_extensions.dart';
|
||||
|
||||
class FriendicaClient {
|
||||
|
@ -27,6 +29,23 @@ class FriendicaClient {
|
|||
_authHeader = "Basic $encodedAuthString";
|
||||
}
|
||||
|
||||
FutureResult<List<UserNotification>, ExecError> getNotifications() async {
|
||||
final url = 'https://$serverName/api/friendica/notifications';
|
||||
final request = Uri.parse(url);
|
||||
_logger.finest(() => 'Getting new notifications');
|
||||
return (await _getApiListRequest(request).andThenSuccessAsync(
|
||||
(notificationsJson) async => notificationsJson
|
||||
.map((json) => NotificationFriendicaExtension.fromJson(json))
|
||||
.toList()))
|
||||
.mapError((error) {
|
||||
if (error is ExecError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return ExecError(type: ErrorType.localError, message: error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
FutureResult<List<TimelineEntry>, ExecError> getUserTimeline(
|
||||
{String userId = '', int page = 1, int count = 10}) async {
|
||||
_logger.finest(() => 'Getting user timeline for $userId');
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'routes.dart';
|
|||
import 'services/auth_service.dart';
|
||||
import 'services/connections_manager.dart';
|
||||
import 'services/entry_manager_service.dart';
|
||||
import 'services/notifications_manager.dart';
|
||||
import 'services/secrets_service.dart';
|
||||
import 'services/timeline_manager.dart';
|
||||
import 'utils/app_scrolling_behavior.dart';
|
||||
|
@ -31,6 +32,8 @@ void main() async {
|
|||
getIt.registerSingleton<SecretsService>(secretsService);
|
||||
getIt.registerSingleton<AuthService>(authService);
|
||||
getIt.registerSingleton<TimelineManager>(timelineManager);
|
||||
getIt.registerLazySingleton<NotificationsManager>(
|
||||
() => NotificationsManager());
|
||||
await secretsService.initialize().andThenSuccessAsync((credentials) async {
|
||||
if (credentials.isEmpty) {
|
||||
return;
|
||||
|
@ -71,6 +74,9 @@ class App extends StatelessWidget {
|
|||
ChangeNotifierProvider<TimelineManager>(
|
||||
create: (_) => getIt<TimelineManager>(),
|
||||
),
|
||||
ChangeNotifierProvider<NotificationsManager>(
|
||||
create: (_) => getIt<NotificationsManager>(),
|
||||
),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
theme: ThemeData(
|
||||
|
|
32
lib/models/user_notification.dart
Normal file
32
lib/models/user_notification.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
enum NotificationType {
|
||||
favorite,
|
||||
follow,
|
||||
follow_request,
|
||||
mention,
|
||||
reshare,
|
||||
status
|
||||
}
|
||||
|
||||
class UserNotification {
|
||||
final String id;
|
||||
final String type;
|
||||
final String fromName;
|
||||
final String fromUrl;
|
||||
final int timestamp;
|
||||
final String iid;
|
||||
final bool seen;
|
||||
final String content;
|
||||
final String link;
|
||||
|
||||
UserNotification({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.fromName,
|
||||
required this.fromUrl,
|
||||
required this.timestamp,
|
||||
required this.iid,
|
||||
required this.seen,
|
||||
required this.content,
|
||||
required this.link,
|
||||
});
|
||||
}
|
|
@ -1,24 +1,32 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../controls/app_bottom_nav_bar.dart';
|
||||
import '../controls/notifications_control.dart';
|
||||
import '../services/notifications_manager.dart';
|
||||
|
||||
class NotificationsScreen extends StatelessWidget {
|
||||
const NotificationsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final manager = context.watch<NotificationsManager>();
|
||||
final notifications = manager.notifications;
|
||||
if (notifications.isEmpty) {
|
||||
manager.updateNotifications();
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Notifications'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Notifications'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
return NotificationControl(notification: notifications[index]);
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return Divider();
|
||||
},
|
||||
itemCount: notifications.length),
|
||||
bottomNavigationBar: AppBottomNavBar(
|
||||
currentButton: NavBarButtons.notifications,
|
||||
),
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import '../../models/user_notification.dart';
|
||||
|
||||
extension NotificationFriendicaExtension on UserNotification {
|
||||
static UserNotification fromJson(Map<String, dynamic> json) =>
|
||||
UserNotification(
|
||||
id: json['id'].toString(),
|
||||
type: json['type'].toString(),
|
||||
fromName: json['name'],
|
||||
fromUrl: json['url'],
|
||||
timestamp: int.tryParse(json['timestamp'] ?? '') ?? 0,
|
||||
iid: json['iid'].toString(),
|
||||
seen: json['seen'] ?? false,
|
||||
content: json['msg_html'],
|
||||
link: json['link'],
|
||||
);
|
||||
}
|
74
lib/services/notifications_manager.dart
Normal file
74
lib/services/notifications_manager.dart
Normal file
|
@ -0,0 +1,74 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:result_monad/result_monad.dart';
|
||||
|
||||
import '../globals.dart';
|
||||
import '../models/exec_error.dart';
|
||||
import '../models/user_notification.dart';
|
||||
import 'auth_service.dart';
|
||||
|
||||
class NotificationsManager extends ChangeNotifier {
|
||||
static final _logger = Logger('NotificationManager');
|
||||
final _notifications = <String, UserNotification>{};
|
||||
|
||||
List<UserNotification> get notifications {
|
||||
final result = List<UserNotification>.from(_notifications.values);
|
||||
result.sort((n1, n2) {
|
||||
if (n1.seen == n2.seen) {
|
||||
return n2.timestamp.compareTo(n1.timestamp);
|
||||
}
|
||||
|
||||
if (n1.seen && !n2.seen) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FutureResult<List<UserNotification>, ExecError> updateNotifications() async {
|
||||
final auth = getIt<AuthService>();
|
||||
final clientResult = auth.currentClient;
|
||||
if (clientResult.isFailure) {
|
||||
_logger.severe('Error getting Friendica client: ${clientResult.error}');
|
||||
return clientResult.errorCast();
|
||||
}
|
||||
|
||||
final client = clientResult.value;
|
||||
final result = await client.getNotifications();
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
|
||||
for (final n in result.value) {
|
||||
_notifications[n.id] = n;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
return Result.ok(notifications);
|
||||
}
|
||||
|
||||
FutureResult<UserNotification, ExecError> markSeen(String id) async {
|
||||
final auth = getIt<AuthService>();
|
||||
final clientResult = auth.currentClient;
|
||||
if (clientResult.isFailure) {
|
||||
_logger.severe('Error getting Friendica client: ${clientResult.error}');
|
||||
return clientResult.errorCast();
|
||||
}
|
||||
|
||||
final client = clientResult.value;
|
||||
final result = await client.getNotifications();
|
||||
if (result.isFailure) {
|
||||
return result.errorCast();
|
||||
}
|
||||
|
||||
for (final n in result.value) {
|
||||
_notifications[n.id] = n;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
return Result.ok(notifications.first);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue