kopia lustrzana https://gitlab.com/mysocialportal/relatica
Initial rudimentary notifications viewer and back end
rodzic
6af1c4f214
commit
f4dee56650
|
|
@ -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/credentials.dart';
|
||||||
import 'models/exec_error.dart';
|
import 'models/exec_error.dart';
|
||||||
import 'models/timeline_entry.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';
|
import 'serializers/mastodon/timeline_entry_mastodon_extensions.dart';
|
||||||
|
|
||||||
class FriendicaClient {
|
class FriendicaClient {
|
||||||
|
|
@ -27,6 +29,23 @@ class FriendicaClient {
|
||||||
_authHeader = "Basic $encodedAuthString";
|
_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(
|
FutureResult<List<TimelineEntry>, ExecError> getUserTimeline(
|
||||||
{String userId = '', int page = 1, int count = 10}) async {
|
{String userId = '', int page = 1, int count = 10}) async {
|
||||||
_logger.finest(() => 'Getting user timeline for $userId');
|
_logger.finest(() => 'Getting user timeline for $userId');
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import 'routes.dart';
|
||||||
import 'services/auth_service.dart';
|
import 'services/auth_service.dart';
|
||||||
import 'services/connections_manager.dart';
|
import 'services/connections_manager.dart';
|
||||||
import 'services/entry_manager_service.dart';
|
import 'services/entry_manager_service.dart';
|
||||||
|
import 'services/notifications_manager.dart';
|
||||||
import 'services/secrets_service.dart';
|
import 'services/secrets_service.dart';
|
||||||
import 'services/timeline_manager.dart';
|
import 'services/timeline_manager.dart';
|
||||||
import 'utils/app_scrolling_behavior.dart';
|
import 'utils/app_scrolling_behavior.dart';
|
||||||
|
|
@ -31,6 +32,8 @@ void main() async {
|
||||||
getIt.registerSingleton<SecretsService>(secretsService);
|
getIt.registerSingleton<SecretsService>(secretsService);
|
||||||
getIt.registerSingleton<AuthService>(authService);
|
getIt.registerSingleton<AuthService>(authService);
|
||||||
getIt.registerSingleton<TimelineManager>(timelineManager);
|
getIt.registerSingleton<TimelineManager>(timelineManager);
|
||||||
|
getIt.registerLazySingleton<NotificationsManager>(
|
||||||
|
() => NotificationsManager());
|
||||||
await secretsService.initialize().andThenSuccessAsync((credentials) async {
|
await secretsService.initialize().andThenSuccessAsync((credentials) async {
|
||||||
if (credentials.isEmpty) {
|
if (credentials.isEmpty) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -71,6 +74,9 @@ class App extends StatelessWidget {
|
||||||
ChangeNotifierProvider<TimelineManager>(
|
ChangeNotifierProvider<TimelineManager>(
|
||||||
create: (_) => getIt<TimelineManager>(),
|
create: (_) => getIt<TimelineManager>(),
|
||||||
),
|
),
|
||||||
|
ChangeNotifierProvider<NotificationsManager>(
|
||||||
|
create: (_) => getIt<NotificationsManager>(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: MaterialApp.router(
|
child: MaterialApp.router(
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
|
|
||||||
|
|
@ -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:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../controls/app_bottom_nav_bar.dart';
|
import '../controls/app_bottom_nav_bar.dart';
|
||||||
|
import '../controls/notifications_control.dart';
|
||||||
|
import '../services/notifications_manager.dart';
|
||||||
|
|
||||||
class NotificationsScreen extends StatelessWidget {
|
class NotificationsScreen extends StatelessWidget {
|
||||||
const NotificationsScreen({super.key});
|
const NotificationsScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final manager = context.watch<NotificationsManager>();
|
||||||
|
final notifications = manager.notifications;
|
||||||
|
if (notifications.isEmpty) {
|
||||||
|
manager.updateNotifications();
|
||||||
|
}
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Notifications'),
|
title: Text('Notifications'),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: ListView.separated(
|
||||||
child: Column(
|
itemBuilder: (context, index) {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
return NotificationControl(notification: notifications[index]);
|
||||||
children: [
|
},
|
||||||
Text('Notifications'),
|
separatorBuilder: (context, index) {
|
||||||
],
|
return Divider();
|
||||||
),
|
},
|
||||||
),
|
itemCount: notifications.length),
|
||||||
bottomNavigationBar: AppBottomNavBar(
|
bottomNavigationBar: AppBottomNavBar(
|
||||||
currentButton: NavBarButtons.notifications,
|
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'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Ładowanie…
Reference in New Issue