feat: Initialize monorepo structure for Electron, Web, and Android apps with shared UI components for voice and presence.
All checks were successful
Build and Release / build-and-release (push) Successful in 14m34s
All checks were successful
Build and Release / build-and-release (push) Successful in 14m34s
This commit is contained in:
@@ -72,6 +72,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
);
|
||||
const isMovingRef = useRef(false);
|
||||
const isDMCallRef = useRef(false);
|
||||
const lastSpokeRef = useRef(Date.now());
|
||||
const [isReceivingScreenShareAudio, setIsReceivingScreenShareAudio] = useState(false);
|
||||
const [isReconnecting, setIsReconnecting] = useState(false);
|
||||
const [connectionQualities, setConnectionQualities] = useState({});
|
||||
@@ -245,6 +246,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
setActiveChannelId(channelId);
|
||||
setActiveChannelName(channelName);
|
||||
setConnectionState('connecting');
|
||||
window.__inVoiceCall = true;
|
||||
|
||||
try {
|
||||
// Request microphone permission (triggers Android runtime prompt)
|
||||
@@ -381,6 +383,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
playSound('leave');
|
||||
setConnectionState('disconnected');
|
||||
setActiveChannelId(null);
|
||||
window.__inVoiceCall = false;
|
||||
setRoom(null);
|
||||
setToken(null);
|
||||
clearSpeakerTimers();
|
||||
@@ -397,6 +400,12 @@ export const VoiceProvider = ({ children }) => {
|
||||
newRoom.on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
||||
const currentSpeakerIds = new Set(speakers.map(p => p.identity));
|
||||
|
||||
// Track when local user last spoke (for Android AFK detection)
|
||||
const localIdentity = newRoom.localParticipant?.identity;
|
||||
if (localIdentity && currentSpeakerIds.has(localIdentity)) {
|
||||
lastSpokeRef.current = Date.now();
|
||||
}
|
||||
|
||||
// Cancel pending removal timers for anyone who is speaking again
|
||||
for (const id of currentSpeakerIds) {
|
||||
const timer = speakerRemovalTimers.current.get(id);
|
||||
@@ -456,6 +465,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
console.error('Voice Connection Failed:', err);
|
||||
setConnectionState('error');
|
||||
setActiveChannelId(null);
|
||||
window.__inVoiceCall = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -565,16 +575,33 @@ export const VoiceProvider = ({ children }) => {
|
||||
// AFK idle polling: move user to AFK channel when idle exceeds timeout
|
||||
useEffect(() => {
|
||||
if (!activeChannelId || !serverSettings?.afkChannelId || isInAfkChannel) return;
|
||||
if (!idle?.getSystemIdleTime) return;
|
||||
if (isDMCallRef.current) return; // Skip AFK for DM calls
|
||||
|
||||
const isCapacitor = !!window.Capacitor?.isNativePlatform?.();
|
||||
|
||||
// On desktop/web, require system idle API; on Capacitor, use lastSpokeRef
|
||||
if (!isCapacitor && !idle?.getSystemIdleTime) return;
|
||||
|
||||
const afkTimeout = serverSettings.afkTimeout || 300;
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const idleSeconds = await idle.getSystemIdleTime();
|
||||
let idleSeconds;
|
||||
if (isCapacitor) {
|
||||
// On Android, idle = time since user last spoke in voice
|
||||
idleSeconds = Math.floor((Date.now() - lastSpokeRef.current) / 1000);
|
||||
} else {
|
||||
idleSeconds = await idle.getSystemIdleTime();
|
||||
}
|
||||
|
||||
if (idleSeconds >= afkTimeout) {
|
||||
const userId = localStorage.getItem('userId');
|
||||
if (!userId) return;
|
||||
|
||||
// On Capacitor, also set user status to idle
|
||||
if (isCapacitor) {
|
||||
await convex.mutation(api.auth.updateStatus, { userId, status: 'idle' });
|
||||
}
|
||||
|
||||
await convex.mutation(api.voiceState.afkMove, {
|
||||
userId,
|
||||
afkChannelId: serverSettings.afkChannelId,
|
||||
@@ -766,6 +793,7 @@ export const VoiceProvider = ({ children }) => {
|
||||
const disconnectVoice = () => {
|
||||
console.log('User manually disconnected voice');
|
||||
isDMCallRef.current = false;
|
||||
window.__inVoiceCall = false;
|
||||
voiceService?.stopService();
|
||||
if (room) room.disconnect();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user