Implement mobile connection resumes

This commit is contained in:
2022-08-27 02:59:38 +02:00
parent 426e120e21
commit 5fdf49555f
3 changed files with 89 additions and 9 deletions

View File

@ -29,6 +29,18 @@ export class App {
this.handleUrl(document.URL); this.handleUrl(document.URL);
} }
close() {
if (this.session) this.session.close();
}
sleep() {
if (this.session) this.session.sleep();
}
async wakeup() {
if (this.session) await this.session.wakeup();
}
setHash(hash: string) { setHash(hash: string) {
const url = new URL(document.URL); const url = new URL(document.URL);
url.hash = hash; url.hash = hash;
@ -244,8 +256,8 @@ export class App {
} }
}); });
this.session.subscribe(response => { this.session.subscribe(event => {
this.addMessage(response.data.data.message); this.addMessage(event.data.message);
}); });
} }
} }

View File

@ -7,5 +7,32 @@ window.addEventListener("DOMContentLoaded", () => {
for (const noscript of document.getElementsByTagName("noscript")) { for (const noscript of document.getElementsByTagName("noscript")) {
noscript.remove(); noscript.remove();
} }
new Locutus.App();
const locutus = new Locutus.App();
if (isMobilePlatform()) {
console.log("MOBILE");
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === 'hidden') {
locutus.sleep();
} else if (document.visibilityState === 'visible') {
locutus.wakeup().then();
}
});
} else {
console.log("DESKTOP");
window.addEventListener("beforeunload", (evt) => {
locutus.close();
});
}
}); });
function isMobilePlatform(): boolean {
if (window.matchMedia("(any-pointer:coarse)").matches) return true;
const ua = navigator.userAgent;
if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {
return true;
} else if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {
return true;
}
return false;
}

View File

@ -30,6 +30,13 @@ interface OutputEnvelopeJson {
} | null, } | null,
} }
interface EventJson {
data: {
message: string,
}
uuid: string,
}
interface WellKnownJson { interface WellKnownJson {
domain: DomainJson, domain: DomainJson,
domain_servers: DomainServerJson[] domain_servers: DomainServerJson[]
@ -149,6 +156,7 @@ export class Session {
websocket: WebSocket | null; websocket: WebSocket | null;
numRequests: number; numRequests: number;
subscriptions: string[]; subscriptions: string[];
subscriptionEndpoints: {cb: (a: EventJson) => void}[];
constructor(domain: Domain) { constructor(domain: Domain) {
this.domain = domain; this.domain = domain;
@ -158,6 +166,28 @@ export class Session {
this.httpBaseUrl = null; this.httpBaseUrl = null;
this.websocket = null; this.websocket = null;
this.subscriptions = []; this.subscriptions = [];
this.subscriptionEndpoints = [];
}
close() {
if (this.websocket && (this.websocket.readyState !== WebSocket.CLOSING && this.websocket.readyState !== WebSocket.CLOSED)) {
this.websocket.close();
this.subscriptions = [];
this.websocket = null;
this.server = null;
}
}
sleep() {
this.close();
}
async wakeup() {
await this.chooseDomainServer();
await this.ping();
for (const endpoint of this.subscriptionEndpoints) {
await this._subscribe(endpoint.cb);
}
} }
async chooseDomainServer(): Promise<{ http: number | null, ws: number | null } | undefined> { async chooseDomainServer(): Promise<{ http: number | null, ws: number | null } | undefined> {
@ -289,7 +319,7 @@ export class Session {
responseData.duration = endTime - startTime; responseData.duration = endTime - startTime;
return responseData; return responseData;
} else { } else {
return null; // TODO subscription id return await this.waitForWebSocketResponse(req_nr, timeout);
} }
} else if (this.httpBaseUrl) { } else if (this.httpBaseUrl) {
const controller = new AbortController(); const controller = new AbortController();
@ -369,25 +399,36 @@ export class Session {
}); });
} }
async subscribe(cb: (a: OutputEnvelopeJson) => void) { async subscribe(cb: (a: EventJson) => void) {
this.subscriptionEndpoints.push({cb: cb});
await this._subscribe(cb);
}
private async _subscribe(cb: (a: EventJson) => void) {
this.numRequests++; this.numRequests++;
if (this.websocket !== null) { if (this.websocket !== null) {
let subscription = await this.send('subscribe', {}, 60_000, false, cb); const subscription = await this.send('subscribe', {}, 60_000, false, (res) => {
if (res.data && res.data.events) {
for (const event of res.data.events) {
cb(event);
}
}
});
this.subscriptions.push(subscription); this.subscriptions.push(subscription);
return subscription; return subscription;
} else { } else {
this.send('subscribe', {}, 60_000).then((res) => { this.send('subscribe', {}, 60_000).then((res) => {
if (res.status === "success") { if (res.status === "success") {
this.subscribe(cb); this._subscribe(cb);
cb(res); cb(res);
} else { } else {
setTimeout(() => { setTimeout(() => {
this.subscribe(cb); this._subscribe(cb);
}, 1000); }, 1000);
} }
}).catch(() => { }).catch(() => {
setTimeout(() => { setTimeout(() => {
this.subscribe(cb); this._subscribe(cb);
}, 1000); }, 1000);
}); });
return null; // TODO subscription id return null; // TODO subscription id