From c23dfe16e95713e7058137308bdbc28419609a39 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sat, 07 Mar 2026 11:54:15 +0100
Subject: [PATCH] feat: typing indicator, message deletion, chain playback, autoplay guard

---
 services/audio.ts |   44 +++++++++++++++++++++++++++++++++++++-------
 1 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/services/audio.ts b/services/audio.ts
index ea43236..188164e 100644
--- a/services/audio.ts
+++ b/services/audio.ts
@@ -4,10 +4,32 @@
   setAudioModeAsync,
 } from "expo-audio";
 import * as LegacyFileSystem from "expo-file-system/legacy";
+import { AppState } from "react-native";
 
 export interface RecordingResult {
   uri: string;
   durationMs: number;
+}
+
+// --- Autoplay suppression ---
+// Don't autoplay voice messages when the app is in the background
+// or when the user is on a phone call (detected via audio interruption).
+let _autoplayEnabled = true;
+let _audioInterrupted = false;
+
+// Track app state — suppress autoplay when backgrounded
+AppState.addEventListener("change", (state) => {
+  _autoplayEnabled = state === "active";
+});
+
+/** Check if autoplay is safe right now (app in foreground, no interruption). */
+export function canAutoplay(): boolean {
+  return _autoplayEnabled && !_audioInterrupted;
+}
+
+/** Called externally to signal audio interruption (e.g., phone call started/ended). */
+export function setAudioInterrupted(interrupted: boolean): void {
+  _audioInterrupted = interrupted;
 }
 
 // --- Singleton audio player ---
@@ -94,13 +116,13 @@
 
   while (audioQueue.length > 0) {
     const item = audioQueue.shift()!;
-    await playOneAudio(item.uri, item.onFinish);
+    await playOneAudio(item.uri, item.onFinish, false);
   }
 
   processingQueue = false;
 }
 
-function playOneAudio(uri: string, onFinish?: () => void): Promise<void> {
+function playOneAudio(uri: string, onFinish?: () => void, cancelPrevious = true): Promise<void> {
   return new Promise<void>(async (resolve) => {
     let settled = false;
     const finish = () => {
@@ -118,8 +140,8 @@
       resolve();
     };
 
-    // Stop any currently playing audio first
-    if (cancelCurrent) {
+    // Stop any currently playing audio first (only for non-queued calls)
+    if (cancelPrevious && cancelCurrent) {
       cancelCurrent();
     }
 
@@ -138,9 +160,17 @@
       notifyListeners(uri);
 
       player.addListener("playbackStatusUpdate", (status) => {
-        if (!status.playing && status.currentTime > 0 &&
-            (status.duration <= 0 || status.currentTime >= status.duration)) {
-          finish();
+        if (!status.playing && status.currentTime > 0) {
+          if (status.duration <= 0 || status.currentTime >= status.duration) {
+            // Playback finished naturally
+            finish();
+          } else {
+            // Paused mid-playback — likely audio interruption (phone call)
+            setAudioInterrupted(true);
+          }
+        } else if (status.playing && _audioInterrupted) {
+          // Resumed after interruption
+          setAudioInterrupted(false);
         }
       });
 

--
Gitblit v1.3.1