WIP - Add system notifications for document upload progress/status

This commit is contained in:
Anton Stubenbord
2023-01-09 01:35:47 +01:00
parent 3c6c4e63d7
commit 8cf3020335
34 changed files with 615 additions and 44 deletions

View File

@@ -0,0 +1,11 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
part 'notification_state.dart';
class NotificationCubit extends Cubit<NotificationState> {
NotificationCubit() : super(NotificationInitialState());
void navigateTo(String route, dynamic args) {}
}

View File

@@ -0,0 +1,16 @@
part of 'notification_cubit.dart';
abstract class NotificationState extends Equatable {
const NotificationState();
@override
List<Object> get props => [];
}
class NotificationInitialState extends NotificationState {}
class NotificationOpenDocumentDetailsPageState extends NotificationState {
final int documentId;
const NotificationOpenDocumentDetailsPageState(this.documentId);
}

View File

@@ -0,0 +1,152 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/features/notifications/services/models/notification_payloads/open_created_document_notification_payload.dart';
import 'package:paperless_mobile/features/notifications/services/notification_actions.dart';
import 'package:paperless_mobile/features/notifications/services/notification_channels.dart';
class LocalNotificationService {
final FlutterLocalNotificationsPlugin _plugin =
FlutterLocalNotificationsPlugin();
LocalNotificationService._();
static final LocalNotificationService instance = LocalNotificationService._();
Future<void> initialize() async {
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('ic_stat_paperless_logo_green');
final DarwinInitializationSettings initializationSettingsDarwin =
DarwinInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsDarwin,
);
await _plugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,
);
}
//TODO: INTL
Future<void> notifyTaskChanged(Task task) {
log("[LocalNotificationService] notifyTaskChanged: ${task.toString()}");
int id = task.id;
final status = task.status;
late String title;
late String? body;
late int timestampMillis;
bool showProgress =
status == TaskStatus.started || status == TaskStatus.pending;
int progress = 0;
dynamic payload;
switch (status) {
case TaskStatus.started:
title = "Document received";
body = task.taskFileName;
timestampMillis = task.dateCreated.millisecondsSinceEpoch;
progress = 10;
break;
case TaskStatus.pending:
title = "Processing document...";
body = task.taskFileName;
timestampMillis = task.dateCreated.millisecondsSinceEpoch;
progress = 70;
break;
case TaskStatus.failure:
title = "Failed to process document";
body = "Document ${task.taskFileName} was rejected by the server.";
timestampMillis = task.dateCreated.millisecondsSinceEpoch;
break;
case TaskStatus.success:
title = "Document successfully created";
body = task.taskFileName;
timestampMillis = task.dateDone!.millisecondsSinceEpoch;
payload = CreateDocumentSuccessNotificationResponsePayload(
task.relatedDocumentId!,
);
break;
default:
break;
}
return _plugin.show(
id,
title,
body,
NotificationDetails(
android: AndroidNotificationDetails(
'${NotificationChannel.task.id}_${task.id}',
NotificationChannel.task.name,
category: AndroidNotificationCategory.status,
ongoing: showProgress,
showProgress: showProgress,
maxProgress: 100,
when: timestampMillis,
progress: progress,
actions: status == TaskStatus.success
? [
AndroidNotificationAction(
NotificationResponseAction.openCreatedDocument.name,
"Open",
showsUserInterface: true,
),
AndroidNotificationAction(
NotificationResponseAction.acknowledgeCreatedDocument.name,
"Acknowledge",
),
]
: [],
),
),
payload: jsonEncode(payload),
);
}
void onDidReceiveLocalNotification(
int id,
String? title,
String? body,
String? payload,
) {}
void onDidReceiveNotificationResponse(NotificationResponse response) {
log("Received Notification: ${response.payload}");
if (response.notificationResponseType ==
NotificationResponseType.selectedNotificationAction) {
final action =
NotificationResponseAction.values.byName(response.actionId!);
_handleResponseAction(action, response);
}
// Non-actionable notification pressed, ignoring...
}
void _handleResponseAction(
NotificationResponseAction action,
NotificationResponse response,
) {
switch (action) {
case NotificationResponseAction.openCreatedDocument:
final payload =
CreateDocumentSuccessNotificationResponsePayload.fromJson(
jsonDecode(response.payload!),
);
log("Navigate to document ${payload.documentId}");
break;
case NotificationResponseAction.acknowledgeCreatedDocument:
final payload =
CreateDocumentSuccessNotificationResponsePayload.fromJson(
jsonDecode(response.payload!),
);
log("Acknowledge document ${payload.documentId}");
break;
}
}
}

View File

@@ -0,0 +1,17 @@
import 'package:json_annotation/json_annotation.dart';
part 'open_created_document_notification_payload.g.dart';
@JsonSerializable()
class CreateDocumentSuccessNotificationResponsePayload {
final int documentId;
CreateDocumentSuccessNotificationResponsePayload(this.documentId);
factory CreateDocumentSuccessNotificationResponsePayload.fromJson(
Map<String, dynamic> json) =>
_$CreateDocumentSuccessNotificationResponsePayloadFromJson(json);
Map<String, dynamic> toJson() =>
_$CreateDocumentSuccessNotificationResponsePayloadToJson(this);
}

View File

@@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'open_created_document_notification_payload.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CreateDocumentSuccessNotificationResponsePayload
_$CreateDocumentSuccessNotificationResponsePayloadFromJson(
Map<String, dynamic> json) =>
CreateDocumentSuccessNotificationResponsePayload(
json['documentId'] as int,
);
Map<String, dynamic> _$CreateDocumentSuccessNotificationResponsePayloadToJson(
CreateDocumentSuccessNotificationResponsePayload instance) =>
<String, dynamic>{
'documentId': instance.documentId,
};

View File

@@ -0,0 +1,4 @@
enum NotificationResponseAction {
openCreatedDocument,
acknowledgeCreatedDocument;
}

View File

@@ -0,0 +1,8 @@
enum NotificationChannel {
task("task_channel", "Paperless Tasks");
final String id;
final String name;
const NotificationChannel(this.id, this.name);
}