diff --git a/assets/audio/messages.mp3 b/assets/audio/messages.mp3 new file mode 100644 index 00000000..7f375702 Binary files /dev/null and b/assets/audio/messages.mp3 differ diff --git a/ios/Podfile.lock b/ios/Podfile.lock index d8e72f8b..cde3d9ba 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,7 @@ PODS: - Alamofire (5.11.0) + - audio_session (0.0.1): + - Flutter - connectivity_plus (0.0.1): - Flutter - croppy (0.0.1): @@ -219,6 +221,9 @@ PODS: - Flutter - irondash_engine_context (0.0.1): - Flutter + - just_audio (0.0.1): + - Flutter + - FlutterMacOS - KeychainAccess (4.2.2) - Kingfisher (8.6.2) - KingfisherWebP (1.7.2): @@ -333,6 +338,7 @@ PODS: DEPENDENCIES: - Alamofire + - audio_session (from `.symlinks/plugins/audio_session/ios`) - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - croppy (from `.symlinks/plugins/croppy/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) @@ -357,6 +363,7 @@ DEPENDENCIES: - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`) - irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`) + - just_audio (from `.symlinks/plugins/just_audio/darwin`) - Kingfisher (~> 8.0) - KingfisherWebP - livekit_client (from `.symlinks/plugins/livekit_client/ios`) @@ -418,6 +425,8 @@ SPEC REPOS: - WebRTC-SDK EXTERNAL SOURCES: + audio_session: + :path: ".symlinks/plugins/audio_session/ios" connectivity_plus: :path: ".symlinks/plugins/connectivity_plus/ios" croppy: @@ -466,6 +475,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/in_app_review/ios" irondash_engine_context: :path: ".symlinks/plugins/irondash_engine_context/ios" + just_audio: + :path: ".symlinks/plugins/just_audio/darwin" livekit_client: :path: ".symlinks/plugins/livekit_client/ios" local_auth_darwin: @@ -519,6 +530,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Alamofire: bd5e7b23a1a750975288482c1831d71e74415f86 + audio_session: 9bb7f6c970f21241b19f5a3658097ae459681ba0 connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd croppy: 979e8ddc254f4642bffe7d52dc7193354b27ba30 device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe @@ -559,6 +571,7 @@ SPEC CHECKSUMS: image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 in_app_review: 7dd1ea365263f834b8464673f9df72c80c17c937 irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486 + just_audio: 4e391f57b79cad2b0674030a00453ca5ce817eed KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 Kingfisher: 23d18f54677d973b713e54ce6a8f5eef6e7056ba KingfisherWebP: 38b9721821947f547afb78f933f75f4f9e0ae402 diff --git a/lib/pods/chat/chat_subscribe.dart b/lib/pods/chat/chat_subscribe.dart index 9c2879fc..2f7d4bb7 100644 --- a/lib/pods/chat/chat_subscribe.dart +++ b/lib/pods/chat/chat_subscribe.dart @@ -2,6 +2,8 @@ import "dart:async"; import "dart:convert"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:just_audio/just_audio.dart"; +import "package:island/pods/config.dart"; import "package:island/models/chat.dart"; import "package:island/pods/chat/chat_room.dart"; import "package:island/pods/lifecycle.dart"; @@ -198,7 +200,7 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier { return _typingStatuses; } - void onMessage(WebSocketPacket pkt) { + Future onMessage(WebSocketPacket pkt) async { if (!pkt.type.startsWith('messages')) return; if (['messages.read'].contains(pkt.type)) return; @@ -238,6 +240,17 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier { _messagesNotifier.receiveMessage(message); // Send read receipt for new message sendReadReceipt(); + // Play sound for new messages when app is unfocused + if (pkt.type == 'messages.new' && + message.senderId != _chatIdentity.id && + ref.read(appLifecycleStateProvider).value != AppLifecycleState.resumed && + ref.read(appSettingsProvider).soundEffects) { + final player = AudioPlayer(); + await player.setVolume(0.75); + await player.setAudioSource(AudioSource.asset('assets/audio/messages.mp3')); + await player.play(); + player.dispose(); + } } } @@ -275,4 +288,4 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier { _typingCooldownTimer = null; }); } -} +} \ No newline at end of file diff --git a/lib/services/notify.universal.dart b/lib/services/notify.universal.dart index a1abd3a9..f51c273a 100644 --- a/lib/services/notify.universal.dart +++ b/lib/services/notify.universal.dart @@ -107,13 +107,10 @@ StreamSubscription setupNotificationListener( } if (settings.soundEffects) { final player = AudioPlayer(); - player - .setAudioSource( - AudioSource.asset('assets/audio/notification.mp3'), - ) - .then((_) { - player.play().then((_) => player.dispose()); - }); + await player.setVolume(0.75); + await player.setAudioSource(AudioSource.asset('assets/audio/notification.mp3')); + await player.play(); + player.dispose(); } showTopSnackBar( globalOverlay.currentState!, @@ -231,4 +228,4 @@ Future _putTokenToRemote( "/ring/notifications/subscription", data: {"provider": provider, "device_token": token}, ); -} +} \ No newline at end of file diff --git a/lib/services/notify.windows.dart b/lib/services/notify.windows.dart index 17dc2073..373c3ef9 100644 --- a/lib/services/notify.windows.dart +++ b/lib/services/notify.windows.dart @@ -70,13 +70,10 @@ StreamSubscription setupNotificationListener( } if (settings.soundEffects) { final player = AudioPlayer(); - player - .setAudioSource( - AudioSource.asset('assets/audio/notification.mp3'), - ) - .then((_) { - player.play().then((_) => player.dispose()); - }); + await player.setVolume(0.75); + await player.setAudioSource(AudioSource.asset('assets/audio/notification.mp3')); + await player.play(); + player.dispose(); } showTopSnackBar( globalOverlay.currentState!, @@ -224,4 +221,4 @@ Future _putTokenToRemote( "/ring/notifications/subscription", data: {"provider": provider, "device_token": token}, ); -} +} \ No newline at end of file diff --git a/lib/widgets/task_overlay.dart b/lib/widgets/task_overlay.dart index d5dbffe9..5a030571 100644 --- a/lib/widgets/task_overlay.dart +++ b/lib/widgets/task_overlay.dart @@ -275,7 +275,8 @@ class _TaskOverlayContent extends HookConsumerWidget { ), if (activeTasks.any( (task) => - task.status == DriveTaskStatus.inProgress, + task.status == DriveTaskStatus.inProgress && + task.uploadedBytes < task.fileSize, )) CircularProgressIndicator( value: null, // Indeterminate @@ -378,7 +379,8 @@ class _TaskOverlayContent extends HookConsumerWidget { if (activeTasks.any( (task) => task.status == - DriveTaskStatus.inProgress, + DriveTaskStatus.inProgress && + task.uploadedBytes < task.fileSize, )) CircularProgressIndicator( value: null, // Indeterminate @@ -533,14 +535,9 @@ class _TaskOverlayContent extends HookConsumerWidget { } double? _getTaskProgress(DriveTask task) { - if (task.status == DriveTaskStatus.completed) return 1.0; + if (task.status == DriveTaskStatus.completed || (task.uploadedBytes >= task.fileSize && task.fileSize > 0)) return 1.0; if (task.status != DriveTaskStatus.inProgress) return 0.0; - // If all bytes are uploaded but still in progress, show indeterminate - if (task.uploadedBytes >= task.fileSize && task.fileSize > 0) { - return null; // Indeterminate progress - } - return task.fileSize > 0 ? task.uploadedBytes / task.fileSize : 0.0; } @@ -672,14 +669,9 @@ class UploadTaskTile extends StatefulWidget { State createState() => _UploadTaskTileState(); static double? _getTaskProgress(DriveTask task) { - if (task.status == DriveTaskStatus.completed) return 1.0; + if (task.status == DriveTaskStatus.completed || (task.uploadedBytes >= task.fileSize && task.fileSize > 0)) return 1.0; if (task.status == DriveTaskStatus.inProgress) return null; - // If all bytes are uploaded but still in progress, show indeterminate - if (task.uploadedBytes >= task.fileSize && task.fileSize > 0) { - return null; // Indeterminate progress - } - return task.fileSize > 0 ? task.uploadedBytes / task.fileSize : 0.0; } } @@ -1029,4 +1021,4 @@ class _UploadTaskTileState extends State return '${duration.inSeconds}s'; } } -} +} \ No newline at end of file