mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-18 14:22:53 +00:00
opening chat if clicked on the notification
This commit is contained in:
parent
aa26766bdf
commit
267e2bd376
12 changed files with 200 additions and 94 deletions
|
|
@ -3,6 +3,7 @@
|
|||
## 0.1.2
|
||||
|
||||
- New: Developer settings to reduce flames
|
||||
- New: Clicking on “Text Notifications” will now open the chat directly (Android only)
|
||||
- Improve: Improved troubleshooting for issues with push notifications
|
||||
- Fix: Flash not activated when starting a video recording
|
||||
- Fix: Problem sending media when a recipient has deleted their account.
|
||||
|
|
|
|||
|
|
@ -56,13 +56,20 @@ PODS:
|
|||
- FirebaseAnalytics (~> 12.9.0)
|
||||
- Firebase/CoreOnly (12.9.0):
|
||||
- FirebaseCore (~> 12.9.0)
|
||||
- Firebase/Installations (12.9.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseInstallations (~> 12.9.0)
|
||||
- Firebase/Messaging (12.9.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseMessaging (~> 12.9.0)
|
||||
- firebase_core (4.5.0):
|
||||
- firebase_app_installations (0.4.1):
|
||||
- Firebase/Installations (= 12.9.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
- firebase_core (4.6.0):
|
||||
- Firebase/CoreOnly (= 12.9.0)
|
||||
- Flutter
|
||||
- firebase_messaging (16.1.2):
|
||||
- firebase_messaging (16.1.3):
|
||||
- Firebase/Messaging (= 12.9.0)
|
||||
- firebase_core
|
||||
- Flutter
|
||||
|
|
@ -278,17 +285,17 @@ PODS:
|
|||
- PromisesObjC (2.4.0)
|
||||
- restart_app (1.7.3):
|
||||
- Flutter
|
||||
- SDWebImage (5.21.6):
|
||||
- SDWebImage/Core (= 5.21.6)
|
||||
- SDWebImage/Core (5.21.6)
|
||||
- SDWebImage (5.21.7):
|
||||
- SDWebImage/Core (= 5.21.7)
|
||||
- SDWebImage/Core (5.21.7)
|
||||
- SDWebImageWebPCoder (0.15.0):
|
||||
- libwebp (~> 1.0)
|
||||
- SDWebImage/Core (~> 5.17)
|
||||
- Sentry/HybridSDK (8.56.2)
|
||||
- sentry_flutter (9.14.0):
|
||||
- Sentry/HybridSDK (8.58.0)
|
||||
- sentry_flutter (9.16.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- Sentry/HybridSDK (= 8.56.2)
|
||||
- Sentry/HybridSDK (= 8.58.0)
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
|
|
@ -297,32 +304,32 @@ PODS:
|
|||
- sqflite_darwin (0.0.4):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqlite3 (3.51.1):
|
||||
- sqlite3/common (= 3.51.1)
|
||||
- sqlite3/common (3.51.1)
|
||||
- sqlite3/dbstatvtab (3.51.1):
|
||||
- sqlite3 (3.52.0):
|
||||
- sqlite3/common (= 3.52.0)
|
||||
- sqlite3/common (3.52.0)
|
||||
- sqlite3/dbstatvtab (3.52.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/fts5 (3.51.1):
|
||||
- sqlite3/fts5 (3.52.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/math (3.51.1):
|
||||
- sqlite3/math (3.52.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/perf-threadsafe (3.51.1):
|
||||
- sqlite3/perf-threadsafe (3.52.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.51.1):
|
||||
- sqlite3/rtree (3.52.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/session (3.51.1):
|
||||
- sqlite3/session (3.52.0):
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- sqlite3 (~> 3.51.1)
|
||||
- sqlite3 (~> 3.52.0)
|
||||
- sqlite3/dbstatvtab
|
||||
- sqlite3/fts5
|
||||
- sqlite3/math
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- sqlite3/session
|
||||
- SwiftProtobuf (1.34.1)
|
||||
- SwiftProtobuf (1.36.1)
|
||||
- SwiftyGif (5.4.5)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
|
|
@ -343,6 +350,7 @@ DEPENDENCIES:
|
|||
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Firebase
|
||||
- firebase_app_installations (from `.symlinks/plugins/firebase_app_installations/ios`)
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
- firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`)
|
||||
- FirebaseCore
|
||||
|
|
@ -430,6 +438,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/emoji_picker_flutter/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
firebase_app_installations:
|
||||
:path: ".symlinks/plugins/firebase_app_installations/ios"
|
||||
firebase_core:
|
||||
:path: ".symlinks/plugins/firebase_core/ios"
|
||||
firebase_messaging:
|
||||
|
|
@ -493,7 +503,7 @@ SPEC CHECKSUMS:
|
|||
app_links: a754cbec3c255bd4bbb4d236ecc06f28cd9a7ce8
|
||||
audio_waveforms: a6dde7fe7c0ea05f06ffbdb0f7c1b2b2ba6cedcf
|
||||
background_downloader: 50e91d979067b82081aba359d7d916b3ba5fadad
|
||||
camera_avfoundation: 5675ca25298b6f81fa0a325188e7df62cc217741
|
||||
camera_avfoundation: 968a9a5323c79a99c166ad9d7866bfd2047b5a9b
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
cryptography_flutter_plus: 44f4e9e4079395fcbb3e7809c0ac2c6ae2d9576f
|
||||
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
|
||||
|
|
@ -502,8 +512,9 @@ SPEC CHECKSUMS:
|
|||
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
|
||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||
Firebase: 065f2bb395062046623036d8e6dc857bc2521d56
|
||||
firebase_core: afac1aac13c931e0401c7e74ed1276112030efab
|
||||
firebase_messaging: 7cb2727feb789751fc6936bcc8e08408970e2820
|
||||
firebase_app_installations: 1abd8d071ea2022d7888f7a9713710c37136ff91
|
||||
firebase_core: 8e6f58412ca227827c366b92e7cee047a2148c60
|
||||
firebase_messaging: c3aa897e0d40109cfb7927c40dc0dea799863f3b
|
||||
FirebaseAnalytics: cd7d01d352f3c237c9a0e31552c257cd0b0c0352
|
||||
FirebaseCore: 428912f751178b06bef0a1793effeb4a5e09a9b8
|
||||
FirebaseCoreInternal: b321eafae5362113bc182956fafc9922cfc77b72
|
||||
|
|
@ -544,16 +555,16 @@ SPEC CHECKSUMS:
|
|||
pro_video_editor: 44ef9a6d48dbd757ed428cf35396dd05f35c7830
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
restart_app: 0714144901e260eae68f7afc2fc4aacc1a323ad2
|
||||
SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477
|
||||
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
|
||||
SDWebImageWebPCoder: 0e06e365080397465cc73a7a9b472d8a3bd0f377
|
||||
Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7
|
||||
sentry_flutter: 841fa2fe08dc72eb95e2320b76e3f751f3400cf5
|
||||
Sentry: d587a8fe91ca13503ecd69a1905f3e8a0fcf61be
|
||||
sentry_flutter: 31101687061fb85211ebab09ce6eb8db4e9ba74f
|
||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sqlite3: 8d708bc63e9f4ce48f0ad9d6269e478c5ced1d9b
|
||||
sqlite3_flutter_libs: d13b8b3003f18f596e542bcb9482d105577eff41
|
||||
SwiftProtobuf: c901f00a3e125dc33cac9b16824da85682ee47da
|
||||
sqlite3: a51c07cf16e023d6c48abd5e5791a61a47354921
|
||||
sqlite3_flutter_libs: b3e120efe9a82017e5552a620f696589ed4f62ab
|
||||
SwiftProtobuf: 9e106a71456f4d3f6a3b0c8fd87ef0be085efc38
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||
|
|
|
|||
|
|
@ -133,9 +133,7 @@ class ApiService {
|
|||
return;
|
||||
}
|
||||
reconnectionTimer?.cancel();
|
||||
Log.info('Starting reconnection timer with $_reconnectionDelay s delay');
|
||||
reconnectionTimer = Timer(Duration(seconds: _reconnectionDelay), () async {
|
||||
Log.info('Reconnection timer triggered');
|
||||
reconnectionTimer = null;
|
||||
// only try to reconnect in case the app is in the foreground
|
||||
if (!globalIsAppInBackground) {
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ Future<(Uint8List, Uint8List?)?> tryToSendCompleteMessage({
|
|||
);
|
||||
|
||||
Uint8List? pushData;
|
||||
if (pushNotification != null && receipt.retryCount <= 3) {
|
||||
/// In case the message has to be resend more than three times, do not show a notification again...
|
||||
if (pushNotification != null && receipt.retryCount <= 1) {
|
||||
// Only show the push notification the first two time.
|
||||
pushData = await encryptPushNotification(
|
||||
receipt.contactId,
|
||||
pushNotification,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:clock/clock.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:hashlib/random.dart';
|
||||
|
|
@ -25,6 +26,7 @@ import 'package:twonly/src/services/api/client2client/reaction.c2c.dart';
|
|||
import 'package:twonly/src/services/api/client2client/text_message.c2c.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/group.services.dart';
|
||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||
import 'package:twonly/src/services/signal/encryption.signal.dart';
|
||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
|
@ -164,7 +166,7 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
|||
final (
|
||||
encryptedContent,
|
||||
plainTextContent,
|
||||
) = await handleEncryptedMessage(
|
||||
) = await handleEncryptedMessageRaw(
|
||||
fromUserId,
|
||||
encryptedContentRaw,
|
||||
message.type,
|
||||
|
|
@ -182,6 +184,9 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
|||
encryptedContent: encryptedContent.writeToBuffer(),
|
||||
);
|
||||
receiptIdDB = const Value.absent();
|
||||
} else {
|
||||
// Message was successful processed
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -206,19 +211,19 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
|
|||
}
|
||||
}
|
||||
|
||||
Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||
Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessageRaw(
|
||||
int fromUserId,
|
||||
Uint8List encryptedContentRaw,
|
||||
Message_Type messageType,
|
||||
String receiptId,
|
||||
) async {
|
||||
final (content, decryptionErrorType) = await signalDecryptMessage(
|
||||
final (encryptedContent, decryptionErrorType) = await signalDecryptMessage(
|
||||
fromUserId,
|
||||
encryptedContentRaw,
|
||||
messageType.value,
|
||||
);
|
||||
|
||||
if (content == null) {
|
||||
if (encryptedContent == null) {
|
||||
return (
|
||||
null,
|
||||
PlaintextContent()
|
||||
|
|
@ -227,6 +232,27 @@ Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
|||
);
|
||||
}
|
||||
|
||||
final (a, b) = await handleEncryptedMessage(
|
||||
fromUserId,
|
||||
encryptedContent,
|
||||
messageType,
|
||||
receiptId,
|
||||
);
|
||||
|
||||
if (Platform.isAndroid && a == null && b == null) {
|
||||
// Message was handled without any error -> Show push notification to the user.
|
||||
await showPushNotificationFromServerMessages(fromUserId, encryptedContent);
|
||||
}
|
||||
|
||||
return (a, b);
|
||||
}
|
||||
|
||||
Future<(EncryptedContent?, PlaintextContent?)> handleEncryptedMessage(
|
||||
int fromUserId,
|
||||
EncryptedContent content,
|
||||
Message_Type messageType,
|
||||
String receiptId,
|
||||
) async {
|
||||
// We got a valid message fromUserId, so mark all messages which where
|
||||
// send to the user but not yet ACK for retransmission. All marked messages
|
||||
// will be either transmitted again after a new server connection (minimum 20 seconds).
|
||||
|
|
|
|||
|
|
@ -176,6 +176,6 @@ bool isItPossibleToRestoreFlames(Group group) {
|
|||
return group.maxFlameCounter > 2 &&
|
||||
flameCounter < group.maxFlameCounter &&
|
||||
group.maxFlameCounterFrom!.isAfter(
|
||||
clock.now().subtract(const Duration(days: 5)),
|
||||
clock.now().subtract(const Duration(days: 7)),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,13 +72,19 @@ Future<void> compressAndOverlayVideo(MediaFileService media) async {
|
|||
|
||||
try {
|
||||
final task = VideoRenderData(
|
||||
video: EditorVideo.file(media.originalPath),
|
||||
imageBytes: media.overlayImagePath.readAsBytesSync(),
|
||||
videoSegments: [
|
||||
VideoSegment(video: EditorVideo.file(media.originalPath)),
|
||||
],
|
||||
imageLayers: [
|
||||
ImageLayer(image: EditorLayerImage.file(media.overlayImagePath)),
|
||||
],
|
||||
enableAudio: !media.removeAudio,
|
||||
);
|
||||
|
||||
await ProVideoEditor.instance
|
||||
.renderVideoToFile(media.ffmpegOutputPath.path, task);
|
||||
await ProVideoEditor.instance.renderVideoToFile(
|
||||
media.ffmpegOutputPath.path,
|
||||
task,
|
||||
);
|
||||
|
||||
if (Platform.isIOS ||
|
||||
media.ffmpegOutputPath.statSync().size >= 10_000_000 ||
|
||||
|
|
@ -115,8 +121,8 @@ Future<void> compressAndOverlayVideo(MediaFileService media) async {
|
|||
|
||||
final sizeFrom = (media.ffmpegOutputPath.statSync().size / 1024 / 1024)
|
||||
.toStringAsFixed(2);
|
||||
final sizeTo =
|
||||
(media.tempPath.statSync().size / 1024 / 1024).toStringAsFixed(2);
|
||||
final sizeTo = (media.tempPath.statSync().size / 1024 / 1024)
|
||||
.toStringAsFixed(2);
|
||||
|
||||
Log.info(
|
||||
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video. Reduced from $sizeFrom to $sizeTo bytes.',
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ import 'package:cryptography_flutter_plus/cryptography_flutter_plus.dart';
|
|||
import 'package:cryptography_plus/cryptography_plus.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/constants/secure_storage_keys.dart';
|
||||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||
import 'package:twonly/src/localization/generated/app_localizations_de.dart';
|
||||
import 'package:twonly/src/localization/generated/app_localizations_en.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
|
||||
import 'package:twonly/src/model/protobuf/client/generated/push_notification.pb.dart';
|
||||
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
|
@ -45,10 +47,34 @@ Future<void> customLocalPushNotification(String title, String msg) async {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> showPushNotificationFromServerMessages(
|
||||
int fromUserId,
|
||||
EncryptedContent encryptedContent,
|
||||
) async {
|
||||
final pushData = await getPushNotificationFromEncryptedContent(
|
||||
null, // this is the toUserID which must be null as this means that the targetMessageId was send from this user.
|
||||
null,
|
||||
encryptedContent,
|
||||
);
|
||||
if (pushData != null) {
|
||||
final pushUsers = await getPushKeys(SecureStorageKeys.receivingPushKeys);
|
||||
for (final pushUser in pushUsers) {
|
||||
if (pushUser.userId.toInt() == fromUserId) {
|
||||
String? groupId;
|
||||
if (encryptedContent.hasGroupId()) {
|
||||
groupId = encryptedContent.groupId;
|
||||
}
|
||||
return showLocalPushNotification(pushUser, pushData, groupId: groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handlePushData(String pushDataB64) async {
|
||||
try {
|
||||
final pushData =
|
||||
EncryptedPushNotification.fromBuffer(base64.decode(pushDataB64));
|
||||
final pushData = EncryptedPushNotification.fromBuffer(
|
||||
base64.decode(pushDataB64),
|
||||
);
|
||||
|
||||
PushNotification? pushNotification;
|
||||
PushUser? foundPushUser;
|
||||
|
|
@ -121,8 +147,10 @@ Future<PushNotification?> tryDecryptMessage(
|
|||
mac: Mac(push.mac),
|
||||
);
|
||||
|
||||
final plaintext =
|
||||
await chacha20.decrypt(secretBox, secretKey: secretKeyData);
|
||||
final plaintext = await chacha20.decrypt(
|
||||
secretBox,
|
||||
secretKey: secretKeyData,
|
||||
);
|
||||
return PushNotification.fromBuffer(plaintext);
|
||||
} catch (e) {
|
||||
// this error is allowed to happen...
|
||||
|
|
@ -132,8 +160,9 @@ Future<PushNotification?> tryDecryptMessage(
|
|||
|
||||
Future<void> showLocalPushNotification(
|
||||
PushUser pushUser,
|
||||
PushNotification pushNotification,
|
||||
) async {
|
||||
PushNotification pushNotification, {
|
||||
String? groupId,
|
||||
}) async {
|
||||
String? title;
|
||||
String? body;
|
||||
|
||||
|
|
@ -174,13 +203,25 @@ Future<void> showLocalPushNotification(
|
|||
iOS: darwinNotificationDetails,
|
||||
);
|
||||
|
||||
String? payload;
|
||||
|
||||
if (groupId != null &&
|
||||
(pushNotification.kind == PushKind.text ||
|
||||
pushNotification.kind == PushKind.response ||
|
||||
pushNotification.kind == PushKind.reactionToAudio ||
|
||||
pushNotification.kind == PushKind.reactionToImage ||
|
||||
pushNotification.kind == PushKind.reactionToText ||
|
||||
pushNotification.kind == PushKind.reactionToAudio)) {
|
||||
payload = Routes.chatsMessages(groupId);
|
||||
}
|
||||
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
pushUser.userId.toInt() %
|
||||
2147483647, // Invalid argument (id): must fit within the size of a 32-bit integer
|
||||
// Invalid argument (id): must fit within the size of a 32-bit integer
|
||||
pushUser.userId.toInt() % 2147483647,
|
||||
title,
|
||||
body,
|
||||
notificationDetails,
|
||||
// payload: pushNotification.kind.name,
|
||||
payload: payload,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -259,17 +300,22 @@ String getPushNotificationText(PushNotification pushNotification) {
|
|||
PushKind.storedMediaFile.name: lang.notificationStoredMediaFile,
|
||||
PushKind.reaction.name: lang.notificationReaction,
|
||||
PushKind.reopenedMedia.name: lang.notificationReopenedMedia,
|
||||
PushKind.reactionToVideo.name:
|
||||
lang.notificationReactionToVideo(pushNotification.additionalContent),
|
||||
PushKind.reactionToAudio.name:
|
||||
lang.notificationReactionToAudio(pushNotification.additionalContent),
|
||||
PushKind.reactionToText.name:
|
||||
lang.notificationReactionToText(pushNotification.additionalContent),
|
||||
PushKind.reactionToImage.name:
|
||||
lang.notificationReactionToImage(pushNotification.additionalContent),
|
||||
PushKind.reactionToVideo.name: lang.notificationReactionToVideo(
|
||||
pushNotification.additionalContent,
|
||||
),
|
||||
PushKind.reactionToAudio.name: lang.notificationReactionToAudio(
|
||||
pushNotification.additionalContent,
|
||||
),
|
||||
PushKind.reactionToText.name: lang.notificationReactionToText(
|
||||
pushNotification.additionalContent,
|
||||
),
|
||||
PushKind.reactionToImage.name: lang.notificationReactionToImage(
|
||||
pushNotification.additionalContent,
|
||||
),
|
||||
PushKind.response.name: lang.notificationResponse(inGroup),
|
||||
PushKind.addedToGroup.name:
|
||||
lang.notificationAddedToGroup(pushNotification.additionalContent),
|
||||
PushKind.addedToGroup.name: lang.notificationAddedToGroup(
|
||||
pushNotification.additionalContent,
|
||||
),
|
||||
};
|
||||
|
||||
return pushNotificationText[pushNotification.kind.name] ?? '';
|
||||
|
|
|
|||
|
|
@ -110,35 +110,23 @@ Future<void> initFCMService() async {
|
|||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
|
||||
unawaited(checkForTokenUpdates());
|
||||
await checkForTokenUpdates();
|
||||
|
||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
|
||||
// You may set the permission requests to "provisional" which allows the user to choose what type
|
||||
// of notifications they would like to receive once the user receives a notification.
|
||||
// final notificationSettings =
|
||||
// await FirebaseMessaging.instance.requestPermission(provisional: true);
|
||||
await FirebaseMessaging.instance.requestPermission();
|
||||
|
||||
// For apple platforms, ensure the APNS token is available before making any FCM plugin API calls
|
||||
// if (Platform.isIOS) {
|
||||
// final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
|
||||
// if (apnsToken == null) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
FirebaseMessaging.onMessage.listen(handleRemoteMessage);
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
initLogger();
|
||||
// Log.info('Handling a background message: ${message.messageId}');
|
||||
final isInitialized = await initBackgroundExecution();
|
||||
Log.info('Handling a background message: ${message.messageId}');
|
||||
await handleRemoteMessage(message);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
if (await initBackgroundExecution()) {
|
||||
if (isInitialized) {
|
||||
await handlePeriodicTask();
|
||||
}
|
||||
} else {
|
||||
|
|
@ -164,7 +152,11 @@ Future<void> handleRemoteMessage(RemoteMessage message) async {
|
|||
final body =
|
||||
message.notification?.body ?? message.data['body'] as String? ?? '';
|
||||
await customLocalPushNotification(title, body);
|
||||
} else if (message.data['push_data'] != null) {
|
||||
await handlePushData(message.data['push_data'] as String);
|
||||
}
|
||||
|
||||
// On Android the push notification is now shown in the server_message.dart. This ensures
|
||||
// that the messages was successfully decrypted before showing the push notification
|
||||
// else if (message.data['push_data'] != null) {
|
||||
// await handlePushData(message.data['push_data'] as String);
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,13 +49,15 @@ Future<void> setupNotificationWithUsers({
|
|||
|
||||
final contacts = await twonlyDB.contactsDao.getAllContacts();
|
||||
for (final contact in contacts) {
|
||||
final pushUser =
|
||||
pushUsers.firstWhereOrNull((x) => x.userId == contact.userId);
|
||||
final pushUser = pushUsers.firstWhereOrNull(
|
||||
(x) => x.userId == contact.userId,
|
||||
);
|
||||
|
||||
if (pushUser != null && pushUser.pushKeys.isNotEmpty) {
|
||||
// make it harder to predict the change of the key
|
||||
final timeBefore =
|
||||
clock.now().subtract(Duration(days: 10 + random.nextInt(5)));
|
||||
final timeBefore = clock.now().subtract(
|
||||
Duration(days: 10 + random.nextInt(5)),
|
||||
);
|
||||
final lastKey = pushUser.pushKeys.last;
|
||||
final createdAt = DateTime.fromMillisecondsSinceEpoch(
|
||||
lastKey.createdAtUnixTimestamp.toInt(),
|
||||
|
|
@ -197,7 +199,7 @@ Future<void> updateLastMessageId(int fromUserId, String messageId) async {
|
|||
}
|
||||
|
||||
Future<PushNotification?> getPushNotificationFromEncryptedContent(
|
||||
int toUserId,
|
||||
int? toUserId,
|
||||
String? messageId,
|
||||
EncryptedContent content,
|
||||
) async {
|
||||
|
|
@ -210,7 +212,7 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
|
|||
final msg = await twonlyDB.messagesDao
|
||||
.getMessageById(content.reaction.targetMessageId)
|
||||
.getSingleOrNull();
|
||||
if (msg == null || msg.senderId == null || msg.senderId != toUserId) {
|
||||
if (msg == null || msg.senderId != toUserId) {
|
||||
return null;
|
||||
}
|
||||
if (msg.content != null) {
|
||||
|
|
@ -285,7 +287,7 @@ Future<PushNotification?> getPushNotificationFromEncryptedContent(
|
|||
.getMessageById(content.reaction.targetMessageId)
|
||||
.getSingleOrNull();
|
||||
// These notifications should only be send to the original sender.
|
||||
if (msg == null || msg.senderId == null || msg.senderId != toUserId) {
|
||||
if (msg == null || msg.senderId != toUserId) {
|
||||
return null;
|
||||
}
|
||||
switch (content.mediaUpdate.type) {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ import 'package:sentry_flutter/sentry_flutter.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/utils/exclusive_access.dart';
|
||||
|
||||
bool _isInitialized = false;
|
||||
|
||||
void initLogger() {
|
||||
// Logger.root.level = kReleaseMode ? Level.INFO : Level.ALL;
|
||||
if (_isInitialized) return;
|
||||
_isInitialized = true;
|
||||
Logger.root.level = Level.ALL;
|
||||
Logger.root.onRecord.listen((record) async {
|
||||
unawaited(_writeLogToFile(record));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_sharing_intent/model/sharing_file.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/providers/routing.provider.dart';
|
||||
import 'package:twonly/src/services/intent/links.intent.dart';
|
||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
||||
|
|
@ -106,7 +108,12 @@ class HomeViewState extends State<HomeView> {
|
|||
});
|
||||
};
|
||||
selectNotificationStream.stream.listen((response) async {
|
||||
globalUpdateOfHomeViewPageIndex(0);
|
||||
if (response.payload != null &&
|
||||
response.payload!.startsWith(Routes.chats)) {
|
||||
await routerProvider.push(response.payload!);
|
||||
} else {
|
||||
globalUpdateOfHomeViewPageIndex(0);
|
||||
}
|
||||
});
|
||||
unawaited(_mainCameraController.selectCamera(0, true));
|
||||
unawaited(initAsync());
|
||||
|
|
@ -140,13 +147,26 @@ class HomeViewState extends State<HomeView> {
|
|||
}
|
||||
|
||||
Future<void> initAsync() async {
|
||||
final notificationAppLaunchDetails =
|
||||
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
|
||||
final notificationAppLaunchDetails = await flutterLocalNotificationsPlugin
|
||||
.getNotificationAppLaunchDetails();
|
||||
|
||||
if (widget.initialPage == 0 ||
|
||||
(notificationAppLaunchDetails != null &&
|
||||
notificationAppLaunchDetails.didNotificationLaunchApp)) {
|
||||
globalUpdateOfHomeViewPageIndex(0);
|
||||
var pushed = false;
|
||||
|
||||
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
|
||||
final payload =
|
||||
notificationAppLaunchDetails?.notificationResponse?.payload;
|
||||
if (payload != null && payload.startsWith(Routes.chats)) {
|
||||
await routerProvider.push(payload);
|
||||
pushed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pushed) {
|
||||
globalUpdateOfHomeViewPageIndex(0);
|
||||
}
|
||||
}
|
||||
|
||||
final draftMedia = await twonlyDB.mediaFilesDao.getDraftMediaFile();
|
||||
|
|
@ -168,8 +188,9 @@ class HomeViewState extends State<HomeView> {
|
|||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: GestureDetector(
|
||||
onDoubleTap:
|
||||
offsetRatio == 0 ? _mainCameraController.onDoubleTap : null,
|
||||
onDoubleTap: offsetRatio == 0
|
||||
? _mainCameraController.onDoubleTap
|
||||
: null,
|
||||
onTapDown: offsetRatio == 0 ? _mainCameraController.onTapDown : null,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
|
|
|
|||
Loading…
Reference in a new issue