import { completeIce, rtcConfiguration, sendFrame } from './mobile/transport.mjs' const host = window.mobileHost, $ = id => document.getElementById(id), peers = new Map() const status = text => { $('status').textContent = text } function action(id, work) { $(id).onclick = async () => { $(id).disabled = true; try { await work() } catch (error) { status(error.message) } finally { $(id).disabled = false } } } action('login-start', async () => { const result = await host.call('account.login.start'); $('login-code').textContent = `Açılan hesap sayfasında yalnız bu kodu onaylayın: ${result.code}` }) action('login-check', async () => { const result = await host.call('account.login.check'); status(result.approved ? 'Hesaba giriş yapıldı. Bu bilgisayarı eşleştirebilirsiniz.' : 'Tarayıcıdaki kod onayı bekleniyor.') }) action('logout', async () => { await host.call('account.logout'); listening = false; $('login-code').textContent = ''; status('Mobil oturum kapatıldı; hesap ve Google anahtarı bu bilgisayar kasasından temizlendi.') }) action('save', async () => { const token = $('account').value; $('account').value = ''; await host.call('account.save', { token }); status('Hesap anahtarı Windows kasasına kaydedildi.') }) action('pair', async () => { const pair = await host.call('desktop.pair'); $('code').textContent = `Açılan hesap sayfasında bu kodu onaylayın: ${pair.code}. Süre: ${pair.expiresAt}` }) action('approve', async () => { await host.call('phone.approve', { pairingId: $('pairing').value.trim(), code: $('phone-code').value.trim().toUpperCase() }); $('phone-code').value = ''; status('Telefon bu bilgisayarda onaylandı.') }) action('refresh', async () => { const data = await host.call('devices'); $('devices').replaceChildren() for (const device of data.devices) { const row = document.createElement('p'), label = document.createElement('span'), button = document.createElement('button') label.textContent = `${device.name} · ${device.platform}${device.self ? ' · Bu bilgisayar' : ''}` button.textContent = 'Cihazı iptal et' button.onclick = async () => { button.disabled = true; try { await host.call('device.revoke', { deviceId: device.id, version: device.version }); row.remove(); status('Cihaz iptal edildi.') } catch (error) { status(error.message); button.disabled = false } } row.append(label, button); $('devices').append(row) } }) function close(id) { const entry = peers.get(id); if (!entry) return; peers.delete(id); clearInterval(entry.timer); entry.pc.close(); void host.call('channel.close', { channelId: id }) } host.onEvent(event => { if (event.close) { close(event.channelId); return }; const entry = peers.get(event.channelId); if (entry?.channel?.readyState === 'open') sendFrame(entry.channel, event.frame) }) async function connect(offer) { const ice = await host.call('ice') const pc = new RTCPeerConnection(rtcConfiguration(ice.iceServers)), entry = { pc }; peers.set(offer.id, entry) pc.ondatachannel = event => { const channel = event.channel; entry.channel = channel if (channel.label !== 'aiturk-mobile-v1') { close(offer.id); return } let pending = 0 channel.onmessage = async event => { if (typeof event.data !== 'string' || event.data.length > 65000 || ++pending > 8) { close(offer.id); return } let message try { message = JSON.parse(event.data) const result = await host.call('channel.request', { channelId: offer.id, message }) if (channel.readyState === 'open') sendFrame(channel, { id: message.id, result }) } catch (error) { if (message?.id && channel.readyState === 'open') channel.send(JSON.stringify({ id: message.id, error: error.message })) } finally { pending-- } } channel.onclose = () => close(offer.id) channel.onopen = () => status('Telefon şifreli veri kanalıyla bağlı.') } try { await pc.setRemoteDescription({ type: 'offer', sdp: offer.sdp }) await pc.setLocalDescription(await pc.createAnswer()); await completeIce(pc) await host.call('channel.answer', { channelId: offer.id, sdp: pc.localDescription.sdp }) entry.timer = setInterval(() => { void host.call('channel.heartbeat', { channelId: offer.id }).catch(() => close(offer.id)) }, 10000) } catch (error) { close(offer.id); throw error } } let listening = false action('listen', async () => { if (listening) return listening = true; status('Onaylı telefon bağlantısı bekleniyor.') while (listening) { try { const data = await host.call('inbox'); for (const offer of data.channels) if (!peers.has(offer.id)) await connect(offer) } catch (error) { status(error.message); listening = false; break } await new Promise(resolve => setTimeout(resolve, 2000)) } }) window.addEventListener('pagehide', () => { listening = false; for (const id of peers.keys()) close(id) })