feat: enhance incoming call notification logic to track seen calls and filter fresh calls
This commit is contained in:
@@ -102,6 +102,7 @@ export function IncomingCallNotification() {
|
|||||||
const { activeCall, startCall } = useCall();
|
const { activeCall, startCall } = useCall();
|
||||||
const [incomingCall, setIncomingCall] = useState<IncomingCall | null>(null);
|
const [incomingCall, setIncomingCall] = useState<IncomingCall | null>(null);
|
||||||
const [declinedRooms, setDeclinedRooms] = useState<Set<string>>(new Set());
|
const [declinedRooms, setDeclinedRooms] = useState<Set<string>>(new Set());
|
||||||
|
const [seenCalls, setSeenCalls] = useState<Map<string, number>>(new Map());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks all rooms for incoming calls
|
* Checks all rooms for incoming calls
|
||||||
@@ -118,15 +119,25 @@ export function IncomingCallNotification() {
|
|||||||
|
|
||||||
// Check all joined rooms for calls
|
// Check all joined rooms for calls
|
||||||
const rooms = mx.getRooms();
|
const rooms = mx.getRooms();
|
||||||
|
const now = Date.now();
|
||||||
|
const MAX_CALL_AGE = 10000; // 10 seconds
|
||||||
|
|
||||||
// Find the first room with an incoming call
|
// Find the first room with an incoming call
|
||||||
const incomingRoom = rooms.find((room) => {
|
const incomingRoom = rooms.find((room) => {
|
||||||
// Only monitor DMs and small groups
|
// Only monitor 1-1 DMs (exactly 2 members)
|
||||||
if (!isDirectOrSmallGroup(room)) return false;
|
if (room.getJoinedMemberCount() !== 2) return false;
|
||||||
// Skip rooms we've declined
|
// Skip rooms we've declined
|
||||||
if (declinedRooms.has(room.roomId)) return false;
|
if (declinedRooms.has(room.roomId)) return false;
|
||||||
const { members } = getOtherCallMembersWithTime(room, myUserId);
|
const { members, earliestTime } = getOtherCallMembersWithTime(room, myUserId);
|
||||||
return members.length > 0;
|
if (members.length === 0) return false;
|
||||||
|
|
||||||
|
// Skip if we've already seen this exact call session
|
||||||
|
const lastSeenTime = seenCalls.get(room.roomId);
|
||||||
|
if (lastSeenTime && lastSeenTime === earliestTime) return false;
|
||||||
|
|
||||||
|
// Only show if the call is fresh (less than 10 seconds old)
|
||||||
|
const callAge = now - earliestTime;
|
||||||
|
return callAge < MAX_CALL_AGE;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (incomingRoom) {
|
if (incomingRoom) {
|
||||||
@@ -140,28 +151,33 @@ export function IncomingCallNotification() {
|
|||||||
} else {
|
} else {
|
||||||
setIncomingCall(null);
|
setIncomingCall(null);
|
||||||
}
|
}
|
||||||
}, [mx, activeCall, declinedRooms]);
|
}, [mx, activeCall, declinedRooms, seenCalls]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles state events for call member changes
|
* Handles state events for call member changes
|
||||||
*/
|
*/
|
||||||
const handleStateEvent = useCallback((event: MatrixEvent) => {
|
const handleStateEvent = useCallback((event: MatrixEvent) => {
|
||||||
if (event.getType() === CALL_MEMBER_EVENT_TYPE) {
|
if (event.getType() === CALL_MEMBER_EVENT_TYPE) {
|
||||||
// Clear declined status if call ended in that room
|
// Clear declined status and seen calls if call ended in that room
|
||||||
const roomId = event.getRoomId();
|
const roomId = event.getRoomId();
|
||||||
if (roomId && declinedRooms.has(roomId)) {
|
if (roomId) {
|
||||||
const room = mx.getRoom(roomId);
|
const room = mx.getRoom(roomId);
|
||||||
if (room) {
|
if (room) {
|
||||||
const myUserId = mx.getUserId();
|
const myUserId = mx.getUserId();
|
||||||
if (myUserId) {
|
if (myUserId) {
|
||||||
const members = getOtherCallMembers(room, myUserId);
|
const members = getOtherCallMembers(room, myUserId);
|
||||||
if (members.length === 0) {
|
if (members.length === 0) {
|
||||||
// Call ended, remove from declined
|
// Call ended, clear tracking
|
||||||
setDeclinedRooms((prev) => {
|
setDeclinedRooms((prev) => {
|
||||||
const newSet = new Set(prev);
|
const newSet = new Set(prev);
|
||||||
newSet.delete(roomId);
|
newSet.delete(roomId);
|
||||||
return newSet;
|
return newSet;
|
||||||
});
|
});
|
||||||
|
setSeenCalls((prev) => {
|
||||||
|
const newMap = new Map(prev);
|
||||||
|
newMap.delete(roomId);
|
||||||
|
return newMap;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,9 +199,12 @@ export function IncomingCallNotification() {
|
|||||||
}, [mx, handleStateEvent, checkForIncomingCalls]);
|
}, [mx, handleStateEvent, checkForIncomingCalls]);
|
||||||
|
|
||||||
// Play/stop dialing sound based on incoming call state
|
// Play/stop dialing sound based on incoming call state
|
||||||
// Only play sound if call started less than 15 seconds ago
|
// Mark call as seen when displayed
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (incomingCall) {
|
if (incomingCall) {
|
||||||
|
// Mark this call as seen so we don't notify again
|
||||||
|
setSeenCalls((prev) => new Map(prev).set(incomingCall.roomId, incomingCall.startTime));
|
||||||
|
|
||||||
const callAge = Date.now() - incomingCall.startTime;
|
const callAge = Date.now() - incomingCall.startTime;
|
||||||
const MAX_CALL_AGE_FOR_SOUND = 15000; // 15 seconds
|
const MAX_CALL_AGE_FOR_SOUND = 15000; // 15 seconds
|
||||||
|
|
||||||
@@ -212,6 +231,7 @@ export function IncomingCallNotification() {
|
|||||||
if (!incomingCall) return;
|
if (!incomingCall) return;
|
||||||
|
|
||||||
getCallSounds().stopDialingLoop();
|
getCallSounds().stopDialingLoop();
|
||||||
|
setSeenCalls((prev) => new Map(prev).set(incomingCall.roomId, incomingCall.startTime));
|
||||||
try {
|
try {
|
||||||
await startCall(incomingCall.roomId, CallType.Voice);
|
await startCall(incomingCall.roomId, CallType.Voice);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -226,6 +246,7 @@ export function IncomingCallNotification() {
|
|||||||
if (!incomingCall) return;
|
if (!incomingCall) return;
|
||||||
|
|
||||||
getCallSounds().stopDialingLoop();
|
getCallSounds().stopDialingLoop();
|
||||||
|
setSeenCalls((prev) => new Map(prev).set(incomingCall.roomId, incomingCall.startTime));
|
||||||
setDeclinedRooms((prev) => new Set(prev).add(incomingCall.roomId));
|
setDeclinedRooms((prev) => new Set(prev).add(incomingCall.roomId));
|
||||||
setIncomingCall(null);
|
setIncomingCall(null);
|
||||||
}, [incomingCall]);
|
}, [incomingCall]);
|
||||||
@@ -237,6 +258,7 @@ export function IncomingCallNotification() {
|
|||||||
|
|
||||||
const room = mx.getRoom(incomingCall.roomId);
|
const room = mx.getRoom(incomingCall.roomId);
|
||||||
const firstCaller = incomingCall.callerIds[0];
|
const firstCaller = incomingCall.callerIds[0];
|
||||||
|
const isGroupCall = room ? room.getJoinedMemberCount() > 2 : false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the display name for a user
|
* Gets the display name for a user
|
||||||
@@ -287,10 +309,12 @@ export function IncomingCallNotification() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="T200" className={css.RoomName}>
|
{!isGroupCall && (
|
||||||
<Icon size="100" src={Icons.Phone} />
|
<Text size="T200" className={css.RoomName}>
|
||||||
<span>Calling from {incomingCall.roomName}</span>
|
<Icon size="100" src={Icons.Phone} />
|
||||||
</Text>
|
<span>Calling from {incomingCall.roomName}</span>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user