mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 10:08:00 -06:00
WIP - Add system notifications for document upload progress/status
This commit is contained in:
11
lib/features/notifications/cubit/notification_cubit.dart
Normal file
11
lib/features/notifications/cubit/notification_cubit.dart
Normal 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) {}
|
||||
}
|
||||
16
lib/features/notifications/cubit/notification_state.dart
Normal file
16
lib/features/notifications/cubit/notification_state.dart
Normal 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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
enum NotificationResponseAction {
|
||||
openCreatedDocument,
|
||||
acknowledgeCreatedDocument;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
enum NotificationChannel {
|
||||
task("task_channel", "Paperless Tasks");
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
|
||||
const NotificationChannel(this.id, this.name);
|
||||
}
|
||||
Reference in New Issue
Block a user