diff options
| -rw-r--r-- | assets/src/components/RequestPage.svelte (renamed from assets/src/components/Request.svelte) | 0 | ||||
| -rw-r--r-- | assets/src/components/SharePage.svelte (renamed from assets/src/components/Share.svelte) | 4 | ||||
| -rw-r--r-- | assets/src/components/share/DataPicker.svelte (renamed from assets/src/components/DataPicker.svelte) | 4 | ||||
| -rw-r--r-- | assets/src/components/share/Request.svelte | 31 | ||||
| -rw-r--r-- | assets/src/components/share/Requests.svelte (renamed from assets/src/components/IncomingRequests.svelte) | 7 | ||||
| -rw-r--r-- | assets/src/entries/request.ts | 4 | ||||
| -rw-r--r-- | assets/src/entries/share.ts | 4 | ||||
| -rw-r--r-- | assets/src/network/channel/connection.ts | 2 | ||||
| -rw-r--r-- | assets/src/network/channel/messages/handler.ts | 49 | ||||
| -rw-r--r-- | assets/src/network/channel/request.ts | 10 | ||||
| -rw-r--r-- | assets/src/network/channel/share.ts | 28 | ||||
| -rw-r--r-- | assets/src/network/transfer/transfer.ts | 82 | ||||
| -rw-r--r-- | assets/src/stores/requests.ts | 4 |
13 files changed, 194 insertions, 35 deletions
diff --git a/assets/src/components/Request.svelte b/assets/src/components/RequestPage.svelte index 0c0b111..0c0b111 100644 --- a/assets/src/components/Request.svelte +++ b/assets/src/components/RequestPage.svelte diff --git a/assets/src/components/Share.svelte b/assets/src/components/SharePage.svelte index 5ea55b2..d951288 100644 --- a/assets/src/components/Share.svelte +++ b/assets/src/components/SharePage.svelte @@ -1,7 +1,7 @@ <script lang="ts"> import data from "../stores/data"; - import DataPicker from "./DataPicker.svelte"; - import IncomingRequests from "./IncomingRequests.svelte"; + import DataPicker from "./share/DataPicker.svelte"; + import IncomingRequests from "./share/Requests.svelte"; </script> <main> diff --git a/assets/src/components/DataPicker.svelte b/assets/src/components/share/DataPicker.svelte index 433018d..6e8dbd4 100644 --- a/assets/src/components/DataPicker.svelte +++ b/assets/src/components/share/DataPicker.svelte @@ -1,11 +1,13 @@ <script lang="ts"> - import data from "../stores/data"; + import data from "../../stores/data"; let value = ""; const submit = () => { data.set(value); }; + + // TODO: Accept data other than text. </script> <form on:submit|preventDefault={submit}> diff --git a/assets/src/components/share/Request.svelte b/assets/src/components/share/Request.svelte new file mode 100644 index 0000000..26002ae --- /dev/null +++ b/assets/src/components/share/Request.svelte @@ -0,0 +1,31 @@ +<script lang="ts"> + import { offer } from "../../network/transfer/transfer"; + + export let token: string; + + async function accept() { + const transfer = await offer(token); + } + + function decline() {} +</script> + +<div class="container"> + <span>{token}</span> + <div class="buttons"> + <button on:click={accept}>Accept</button> + <button on:click={decline}>X</button> + </div> +</div> + +<style> + .container { + display: flex; + justify-content: space-between; + align-items: center; + } + + .buttons { + display: flex; + } +</style> diff --git a/assets/src/components/IncomingRequests.svelte b/assets/src/components/share/Requests.svelte index bf08ffe..509d7be 100644 --- a/assets/src/components/IncomingRequests.svelte +++ b/assets/src/components/share/Requests.svelte @@ -1,7 +1,8 @@ <script lang="ts"> - import { getOwnToken, start, Type } from "../network/channel/connection"; + import { getOwnToken, start, Type } from "../../network/channel/connection"; - import requests from "../stores/requests"; + import requests from "../../stores/requests"; + import Request from "./Request.svelte"; const startPromise = start(Type.SHARE); @@ -14,6 +15,6 @@ <h3>Your token is <b>{getOwnToken()}</b>.</h3> {#each $requests as request} - <p>{JSON.stringify(request)}</p> + <Request token={request} /> {/each} {/await} diff --git a/assets/src/entries/request.ts b/assets/src/entries/request.ts index 74f84da..6b8a79f 100644 --- a/assets/src/entries/request.ts +++ b/assets/src/entries/request.ts @@ -1,6 +1,6 @@ -import Request from "../components/Request.svelte"; +import RequestPage from "../components/RequestPage.svelte"; -const app = new Request({ +const app = new RequestPage({ target: document.getElementById("app"), props: {}, }); diff --git a/assets/src/entries/share.ts b/assets/src/entries/share.ts index d0279f1..2704a62 100644 --- a/assets/src/entries/share.ts +++ b/assets/src/entries/share.ts @@ -1,6 +1,6 @@ -import Share from "../components/Share.svelte"; +import SharePage from "../components/SharePage.svelte"; -const app = new Share({ +const app = new SharePage({ target: document.getElementById("app"), props: {}, }); diff --git a/assets/src/network/channel/connection.ts b/assets/src/network/channel/connection.ts index 59eb633..e1ed2d1 100644 --- a/assets/src/network/channel/connection.ts +++ b/assets/src/network/channel/connection.ts @@ -54,6 +54,8 @@ export async function start(type: Type.REQUEST | Type.SHARE) { type === Type.SHARE ? await startShare(connection) : await startRequest(connection); + + updateState(ConnectionState.CONNECTED) } export function send(event: string, data: any): Push { diff --git a/assets/src/network/channel/messages/handler.ts b/assets/src/network/channel/messages/handler.ts index f7a00d2..cc8f005 100644 --- a/assets/src/network/channel/messages/handler.ts +++ b/assets/src/network/channel/messages/handler.ts @@ -1,4 +1,4 @@ -import { getChannel } from "../connection"; +import type { Channel } from "phoenix"; import type { AnyMessage } from "./messages"; export type Handlers = { @@ -14,7 +14,10 @@ export type TokenHandler<Message extends AnyMessage> = { [token: string]: Handler<Message>; }; -export type Handler<Message extends AnyMessage> = (message?: Message) => void; +export type Handler<Message extends AnyMessage> = ( + message?: Message, + unregister?: UnregisterHandler +) => void; export type UnregisterHandler = () => void; @@ -45,17 +48,12 @@ export function registerTokenHandler<Message extends AnyMessage>( const directHandlers = eventHandler.directHandlers; directHandlers.push(handler); - unregister = () => { - const index = directHandlers.findIndex(h => h === handler); - directHandlers.splice(index, 1); - }; + unregister = makeDirectUnregister(directHandlers, handler); } else { const tokenHandler = eventHandler.tokenHandler; tokenHandler[token] = handler; - unregister = () => { - delete tokenHandler[token]; - }; + unregister = makeTokenUnregister(tokenHandler, token); } return unregister; @@ -80,16 +78,35 @@ function handleEvent<Message extends AnyMessage>( if (message["token"] !== undefined) { const token = message["token"]; - const handler: Handler<Message> = eventHandler.tokenHandler[token]; + const tokenHandler = eventHandler.tokenHandler; + const handler: Handler<Message> = tokenHandler[token]; - if (handler === undefined) { - throw new Error("Received message for an unknown token."); + if (handler !== undefined) { + handler(message, makeTokenUnregister(tokenHandler, token)); } - - handler(message); } - for (const handler of eventHandler.directHandlers) { - handler(message); + const directHandlers = eventHandler.directHandlers; + for (const handler of directHandlers) { + handler(message, makeDirectUnregister(directHandlers, handler)); } } + +function makeDirectUnregister<Message extends AnyMessage>( + directHandlers: Handler<Message>[], + handler: Handler<Message> +): UnregisterHandler { + return () => { + const index = directHandlers.findIndex(h => h === handler); + directHandlers.splice(index, 1); + }; +} + +function makeTokenUnregister<Message extends AnyMessage>( + tokenHandler: TokenHandler<Message>, + token: string +): UnregisterHandler { + return () => { + delete tokenHandler[token]; + }; +} diff --git a/assets/src/network/channel/request.ts b/assets/src/network/channel/request.ts index dd18aab..f5145fb 100644 --- a/assets/src/network/channel/request.ts +++ b/assets/src/network/channel/request.ts @@ -1,5 +1,7 @@ import getShareToken from "../../utils/getShareToken"; -import type { Connection } from "./connection"; +import { answer } from "../transfer/transfer"; +import { Connection, on } from "./connection"; +import type { RequestAcceptedMessage } from "./messages/messages"; import { joinRequestChannel } from "./socket"; export async function startRequest(connection: Connection) { @@ -9,4 +11,10 @@ export async function startRequest(connection: Connection) { getShareToken() ); connection.channel = requestChannel; + + on("request_accepted", onRequestAccepted); +} + +async function onRequestAccepted(message: RequestAcceptedMessage) { + await answer(message); } diff --git a/assets/src/network/channel/share.ts b/assets/src/network/channel/share.ts index f93e8cb..6dbf0cd 100644 --- a/assets/src/network/channel/share.ts +++ b/assets/src/network/channel/share.ts @@ -1,5 +1,10 @@ import requests from "../../stores/requests"; -import type { Connection } from "./connection"; +import { Connection, on, onWithToken } from "./connection"; +import type { UnregisterHandler } from "./messages/handler"; +import type { + NewRequestMessage, + RequestCancelledMessage, +} from "./messages/messages"; import { joinShareChannel } from "./socket"; export async function startShare(connection: Connection) { @@ -8,4 +13,23 @@ export async function startShare(connection: Connection) { connection.token ); connection.channel = shareChannel; -} \ No newline at end of file + + on("new_request", onNewRequest); +} + +function onNewRequest(message: NewRequestMessage) { + const token = message.token; + + requests.addRequest(token); + + onWithToken("request_cancelled", token, onRequestCancelled); +} + +function onRequestCancelled( + message: RequestCancelledMessage, + unregister: UnregisterHandler +) { + const token = message.token; + requests.removeRequest(token); + unregister(); +} diff --git a/assets/src/network/transfer/transfer.ts b/assets/src/network/transfer/transfer.ts index 26d2534..1cc029b 100644 --- a/assets/src/network/transfer/transfer.ts +++ b/assets/src/network/transfer/transfer.ts @@ -1,4 +1,10 @@ -import { send } from "../channel/connection"; +import { onWithToken, send } from "../channel/connection"; +import type { UnregisterHandler } from "../channel/messages/handler"; +import type { + ShareAcceptedMessage, + RequestIceCandidateMessage, + ShareIceCandidateMessage, +} from "../channel/messages/messages"; export enum TransferType { OFFER, @@ -11,20 +17,44 @@ export type Transfer = { type: TransferType; }; +const servers = { + iceServers: [ + { + urls: [ + "stun:stun1.l.google.com:19302", + "stun:stun2.l.google.com:19302", + ], + }, + ], + iceCandidatePoolSize: 10, +}; + export async function offer(request_token: string): Promise<Transfer> { const transfer = createTransfer(TransferType.OFFER); const offer = await transfer.pc.createOffer(); transfer.pc.setLocalDescription(offer); - // TODO: Start waiting for remote answer + transfer.pc.onicecandidate = event => { + const candidate = event.candidate; + if (event.candidate !== null) { + send("ice_candidate", { candidate, token: request_token }); + } + }; send("accept_request", { - request: request_token, + token: request_token, sdp: offer.sdp, type: offer.type, }); + onWithToken( + "share_accepted", + request_token, + (message: ShareAcceptedMessage, unregister) => + onShareAccepted(transfer, message, unregister) + ); + return transfer; } @@ -39,6 +69,13 @@ export async function answer( const answer = await transfer.pc.createAnswer(); transfer.pc.setLocalDescription(answer); + transfer.pc.onicecandidate = event => { + const candidate = event.candidate; + if (event.candidate !== null) { + send("ice_candidate", { candidate }); + } + }; + send("accept_share", { sdp: offer.sdp, type: offer.type, @@ -48,15 +85,50 @@ export async function answer( } function createTransfer(type: TransferType): Transfer { - const pc = new RTCPeerConnection(null); + const pc = new RTCPeerConnection(servers); const channel = pc.createDataChannel("channel", { negotiated: true, id: 0, }); + channel.onopen = e => console.log("ooooyeeee"); + return { pc, channel, type, }; -} \ No newline at end of file +} + +function onShareAccepted( + transfer: Transfer, + message: ShareAcceptedMessage, + unregister: UnregisterHandler +) { + const token = message.token; + transfer.pc.setRemoteDescription(message); + + const unregisterIce = onWithToken( + "ice_candidate", + token, + (message: RequestIceCandidateMessage) => + onIncomingIceCandidate(transfer, message) + ); + + transfer.pc.onicegatheringstatechange = event => { + const connection = event.target as any; + console.log(connection.iceGatheringState); + if (connection.iceGatheringState === "complete") { + unregisterIce(); + } + }; + + unregister(); +} + +function onIncomingIceCandidate( + transfer: Transfer, + message: ShareIceCandidateMessage | RequestIceCandidateMessage +) { + transfer.pc.addIceCandidate(message.candidate); +} diff --git a/assets/src/stores/requests.ts b/assets/src/stores/requests.ts index e80eb2b..1dc8cb2 100644 --- a/assets/src/stores/requests.ts +++ b/assets/src/stores/requests.ts @@ -1,11 +1,13 @@ import { writable } from "svelte/store"; const createRequestStore = () => { - const { subscribe, update } = writable([]); + const { subscribe, update } = writable<string[]>([]); return { subscribe, addRequest: request => update(state => [request, ...state]), + removeRequest: request => + update(state => state.filter(r => r !== request)), }; }; |
