Skip to content

Commit

Permalink
Merge pull request #34 from compose-us/legacy-api-replacement-where-a…
Browse files Browse the repository at this point in the history
…re-you-at

Legacy api replacement for where are you at
  • Loading branch information
nidhal-labidi authored Aug 20, 2024
2 parents 0e102c0 + ffd8abe commit 4fd7814
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 109 deletions.
1 change: 0 additions & 1 deletion flottform/forms/src/flottform-channel-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export class FlottformChannelClient extends EventEmitter<Listeners> {
this.dataChannel.bufferedAmountLowThreshold = this.BUFFER_THRESHOLD;
// Set the listener to listen then emit an event when the buffer has more space available and can be used to send more data
this.dataChannel.onbufferedamountlow = () => {
console.log('----------EVENT FIRED----------');
this.emit('bufferedamountlow');
};
this.dataChannel.onopen = (e) => {
Expand Down
78 changes: 78 additions & 0 deletions flottform/forms/src/flottform-text-input-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { FlottformChannelClient } from './flottform-channel-client';
import { DEFAULT_WEBRTC_CONFIG, EventEmitter, Logger, POLL_TIME_IN_MS } from './internal';

type Listeners = {
connected: [];
'webrtc:connection-impossible': [];
sending: []; // Emitted to signal the start of sending the file(s)
done: [];
disconnected: [];
error: [e: string];
};

export class FlottformTextInputClient extends EventEmitter<Listeners> {
private channel: FlottformChannelClient | null = null;
private logger: Logger;

constructor({
endpointId,
flottformApi,
rtcConfiguration = DEFAULT_WEBRTC_CONFIG,
pollTimeForIceInMs = POLL_TIME_IN_MS,
logger = console
}: {
endpointId: string;
flottformApi: string;
rtcConfiguration?: RTCConfiguration;
pollTimeForIceInMs?: number;
logger?: Logger;
}) {
super();
this.channel = new FlottformChannelClient({
endpointId,
flottformApi,
rtcConfiguration,
pollTimeForIceInMs,
logger
});
this.logger = logger;
this.registerListeners();
}
start = () => {
this.channel?.start();
};

close = () => {
// Should be called once all the text has been sent.
this.channel?.close();
};

sendText = (text: string) => {
// For now, I didn't handle very large texts since for most use cases the text won't exceed the size of 1 chunk ( 16KB )
this.emit('sending');
this.channel?.sendData(text);
this.emit('done');
};

private registerListeners = () => {
this.channel?.on('init', () => {});
this.channel?.on('retrieving-info-from-endpoint', () => {});
this.channel?.on('sending-client-info', () => {});
this.channel?.on('connecting-to-host', () => {});
this.channel?.on('connected', () => {
this.emit('connected');
});
this.channel?.on('connection-impossible', () => {
this.emit('webrtc:connection-impossible');
});
this.channel?.on('done', () => {
this.emit('done');
});
this.channel?.on('disconnected', () => {
this.emit('disconnected');
});
this.channel?.on('error', (e) => {
this.emit('error', e);
});
};
}
116 changes: 116 additions & 0 deletions flottform/forms/src/flottform-text-input-host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { FlottformChannelHost } from './flottform-channel-host';
import { Styles } from './flottform-styles';
import { DEFAULT_WEBRTC_CONFIG, EventEmitter, Logger, POLL_TIME_IN_MS } from './internal';

type Listeners = {
new: [];
disconnected: [];
error: [error: any];
connected: [];
receive: []; // Emitted to signal the start of receiving the file(s)
done: [data: string];
'endpoint-created': [{ link: string; qrCode: string }];
'webrtc:waiting-for-client': [
event: { link: string; qrCode: string; channel: FlottformChannelHost }
];
'webrtc:waiting-for-ice': [];
'webrtc:waiting-for-file': [];
};

const noop = () => {};

export class FlottformTextInputHost extends EventEmitter<Listeners> {
private channel: FlottformChannelHost | null = null;
private logger: Logger;
private link: string = '';
private qrCode: string = '';

constructor({
flottformApi,
createClientUrl,
rtcConfiguration = DEFAULT_WEBRTC_CONFIG,
pollTimeForIceInMs = POLL_TIME_IN_MS,
theme,
logger = console
}: {
flottformApi: string | URL;
createClientUrl: (params: { endpointId: string }) => Promise<string>;
rtcConfiguration?: RTCConfiguration;
pollTimeForIceInMs?: number;
theme?: (myself: FlottformTextInputHost) => void;
logger?: Logger;
}) {
super();
this.channel = new FlottformChannelHost({
flottformApi,
createClientUrl,
rtcConfiguration,
pollTimeForIceInMs,
logger
});
this.logger = logger;

this.registerListeners();
theme && theme(this);
}

start = () => {
this.channel?.start();
};

close = () => {
this.channel?.close();
};

getLink = () => {
if (this.link === '') {
this.logger.error(
'Flottform is currently establishing the connection. Link is unavailable for now!'
);
}
return this.link;
};

getQrCode = () => {
if (this.qrCode === '') {
this.logger.error(
'Flottform is currently establishing the connection. qrCode is unavailable for now!'
);
}
return this.qrCode;
};

private handleIncomingData = (e: MessageEvent<any>) => {
// We suppose that the data received is small enough to be all included in 1 message
this.emit('done', e.data);
};

private registerListeners = () => {
this.channel?.on('new', ({ channel }) => {
this.emit('new');
});
this.channel?.on('waiting-for-client', (event) => {
this.emit('webrtc:waiting-for-client', event);
const { qrCode, link } = event;
this.emit('endpoint-created', { link, qrCode });
this.link = link;
this.qrCode = qrCode;
});
this.channel?.on('waiting-for-ice', () => {
this.emit('webrtc:waiting-for-ice');
});
this.channel?.on('waiting-for-file', () => {
this.emit('webrtc:waiting-for-file');
this.emit('connected');
});
this.channel?.on('receiving-data', (e) => {
this.handleIncomingData(e);
});
this.channel?.on('disconnected', () => {
this.emit('disconnected');
});
this.channel?.on('error', (error) => {
this.emit('error', error);
});
};
}
2 changes: 2 additions & 0 deletions flottform/forms/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export { FlottformFileInputHost } from './flottform-file-input-host';
export { FlottformFileInputClient } from './flottform-file-input-client';
export { FlottformTextInputClient } from './flottform-text-input-client';
export { FlottformTextInputHost } from './flottform-text-input-host';
export type { ClientState, SafeEndpointInfo, Logger } from './internal';
import './theme/style.css';
export { defaultThemeForFileInput } from './theme/defaultTheme';
93 changes: 49 additions & 44 deletions flottform/where-are-you-at/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,70 +1,72 @@
<script lang="ts">
import { createFlottformInput } from '@flottform/forms';
import { FlottformTextInputHost } from '@flottform/forms';
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
import type { FlottformState } from '../../../forms/dist/internal';
import ShowLocation from './ShowLocation.svelte';
import { clientBase, sdpExchangeServerBase } from '$lib/api';
type Coordinates = {
accuracy: number;
altitude: number | null;
altitudeAccuracy: number | null;
heading: number | null;
latitude: number;
longitude: number;
speed: number | null;
};
const createClientUrl = async ({ endpointId }: { endpointId: string }) => {
if (!browser) {
throw Error('should not create a client URL when not in browser mode');
}
return `${clientBase}${endpointId}`;
};
let inputField: HTMLInputElement;
let partnerLink: HTMLAnchorElement;
let qrCodeImage: HTMLImageElement;
let createChannelHandler: () => void;
let currentState = writable<FlottformState>('new');
let currentState = writable<
'new' | 'endpoint-created' | 'webrtc:waiting-for-ice' | 'connected' | 'done' | 'error'
>('new');
let qrCodeData = writable<string>('');
let partnerLinkHref = writable<string>('');
let latitude = writable<number>();
let longitude = writable<number>();
onMount(() => {
createFlottformInput({
const flottformTextInputHost = new FlottformTextInputHost({
flottformApi: sdpExchangeServerBase,
createClientUrl,
useDefaultUi: false,
onProgress: async () => {},
onResult: async ({ arrayBuffers }) => {
const decoder = new TextDecoder();
let result = '';
for (const ab of arrayBuffers) {
result += decoder.decode(ab);
}
const coords = JSON.parse(result);
$latitude = coords.latitude;
$longitude = coords.longitude;
inputField.value = result;
},
onStateChange: (state, details) => {
if ($currentState === 'done') {
return;
}
$currentState = state;
if (state === 'new') {
const { createChannel } = details;
createChannelHandler = createChannel;
return;
}
if (state === 'waiting-for-client') {
const { qrCode, link } = details;
console.log({ qrCode, link });
$qrCodeData = qrCode;
$partnerLinkHref = link;
return;
}
}
createClientUrl
});
createChannelHandler = flottformTextInputHost.start;
flottformTextInputHost.on('webrtc:waiting-for-ice', () => {
$currentState = 'webrtc:waiting-for-ice';
});
flottformTextInputHost.on('endpoint-created', ({ link, qrCode }) => {
$currentState = 'endpoint-created';
$partnerLinkHref = link;
$qrCodeData = qrCode;
});
flottformTextInputHost.on('connected', () => {
$currentState = 'connected';
});
flottformTextInputHost.on('done', (message: string) => {
$currentState = 'done';
const coords: Coordinates = JSON.parse(message);
$latitude = coords.latitude;
$longitude = coords.longitude;
// Channel will be closed since we won't receive data anymore.
flottformTextInputHost.close();
});
flottformTextInputHost.on('error', (error) => {
$currentState = 'error';
});
});
</script>

<div class="w-full min-h-svh grid place-items-center">
<input type="hidden" name="location" bind:this={inputField} />
{#if $currentState === 'new'}
<button
on:click={createChannelHandler}
Expand All @@ -75,17 +77,20 @@
Ask your friend to share their location
{#if createChannelHandler === undefined}<span class="animate-spin">⏳</span>{/if}
</button>
{:else if $currentState === 'waiting-for-client'}
{:else if $currentState === 'endpoint-created'}
<div>
<img bind:this={qrCodeImage} alt={$partnerLinkHref} src={$qrCodeData} class="mx-auto" />
<a bind:this={partnerLink} href={$partnerLinkHref} rel="external">{$partnerLinkHref}</a>
<a
bind:this={partnerLink}
href={$partnerLinkHref}
target="_blank"
rel="external noopener noreferrer">{$partnerLinkHref}</a
>
</div>
{:else if $currentState === 'waiting-for-ice'}
{:else if $currentState === 'webrtc:waiting-for-ice'}
Trying to establish a connection with your friend
{:else if $currentState === 'waiting-for-file'}
{:else if $currentState === 'connected'}
Waiting for friend to send their location
{:else if $currentState === 'receiving-data'}
Receiving location
{:else if $currentState === 'done'}
<div class="h-3/4 max-w-3xl w-full">
<ShowLocation latitude={$latitude} longitude={$longitude} />
Expand Down
Loading

0 comments on commit 4fd7814

Please sign in to comment.