import storage from 'kv-storage-polyfill';
import { create_deferred } from './util';
let request_ID = 0;
let ready = create_deferred();
let closing = create_deferred();
let promises = new Map();
let user = null;
let stores;
let w;
async function create_ws(otp) {
const token = user?.token || await storage.get('token');
const carts = await storage.get('carts');
let shit = [];
if(token) {shit.push('token=' + token)};
if(otp) {shit.push('otp=' + otp)};
if((token || otp) && Array.isArray(carts)) {shit.push(...carts.map(c => 'cart=' + c))};
let ws = new WebSocket(`ws${location.protocol === 'https:' ? 's' : ''}://${import.meta.env.VITE_websocket || location.hostname}${shit.length ? '/?' + shit.join('&') : ''}`);
ws.onclose = function(event) {
ready.reject(event.code); closing.resolve();
for(let p of promises.values()) {
p[1]('websocket closed');
}
promises.clear();
request_ID = 0;
ready = create_deferred();
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({what: 'disconnected'});
}
}
ws.onopen = function(event) {
if(shit.length === 0) {
ready.resolve();
for(let i = 0; i < ports.length; ++i) { ports[i].postMessage({
what: "user",
how: "replace",
data: null
});
}
}
closing = create_deferred();
}
ws.onmessage = function(event) {
let d = JSON.parse(event.data);
let p;
if(p = promises.get(d.response_ID)) {
p[0](d.data);
} else {
let ps = hashbrown.get(d.what);
if(d.what === 'user') {
switch(d.how) {
case 'replace':
ready.resolve();
if((user = d.data) === null) {
storage.delete('token');
console.log(d.why);
} else {
if(d.data.token) { if(ports.length !== 1) {
console.error("unpossible");
} else {
ports[0].postMessage('save');
(save = create_deferred()).then(() => storage.set('token', token), () => {});
}
}
storage.delete('carts');
}
break;
case 'update':
if(user) {
Object.assign(user, d.data);
} else {
console.log('shieeet', d);
}
break;
}
} else if(d.what === 'store') {
switch(d.how) {
case 'replace':
stores = d.data;
break;
case 'add':
stores.push(d.data);
break;
case 'remove':
stores.splice(stores.findIndex(s => s.id === d.data), 1);
break;
case 'update':
break;
}
}
if(ps) {
for(const port of ps) {
port.postMessage(d);
}
} else {
console.log('no one listening :(', d)
}
}
}
return ws;
}
let hashbrown = new Map();
self.hb = hashbrown;
let save;
const ports = [];
onconnect = async function(e) {
const idx = ports.push(e.ports[0]) - 1;
ports[idx].onmessage = async function(e) {
if(typeof e.data === 'boolean') {
if(e.data) {
save.resolve();
} else {
save.reject();
}
}
if(e.data === 'closing') {
ports.splice(ports.indexOf(this), 1);
if(ports.length) { for(const [event, set_ports] of hashbrown) {
set_ports.delete(this);
if(set_ports.size === 0) {
hashbrown.delete(event);
const rr = request_ID;
w.send(JSON.stringify({what: 'unsubscribe', parameters: {what: event}, request_ID}));
let data = await new Promise((resolve, reject) => promises.set(request_ID++, [resolve, reject])).finally(() => promises.delete(rr));
if(data === false) {
console.error(`failed to unsubscribe to ${event} probably because never subscribed to begin with`);
} else if(data === undefined) {
console.error('unpossible');
} else {
console.log('successed?')
}
}
}
}
} else if(e.data.indexOf('first') === 0) {
let otp = e.data.substring(5);
if(w === undefined || w.readyState > 1) {
if(w?.readyState > 1) {
await closing;
}
w = await create_ws(otp);
} else if(otp && user === null) {
try {
await ready;
} catch(e) {
console.error('otp', e);
return;
}
const rr = request_ID;
const params = {
what: 'login_otp',
parameters: {
carts: await storage.get('carts'),
otp
},
request_ID
}
try {
w.send(JSON.stringify(params));
} catch(e) {
console.log(e);
console.log(params);
return;
}
let data = await new Promise((resolve, reject) => promises.set(request_ID++, [resolve, reject])).finally(() => promises.delete(rr));
if(typeof data === 'string') {
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({
what: "user",
how: "replace",
data: null,
why: data
});
}
} else {
const {token, ...noTokenUser} = user = data;
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({
what: "user",
how: "replace",
data: noTokenUser
});
}
this.postMessage('save');
(save = create_deferred()).then(() => storage.set('token', token), () => {});
storage.delete('carts');
}
}
} else if(e.data.indexOf('o') === 0) {
const [action, event] = e.data.split(',');
if(action === 'on') {
if(hashbrown.has(event)) {
hashbrown.get(event).add(this);
} else {
hashbrown.set(event, new Set([this]));
}
} else if(action === 'off') {
if(event === 'all') {
for(const [event, set_ports] of hashbrown) {
set_ports.delete(this);
if(set_ports.size === 0) {
hashbrown.delete(event);
const rr = request_ID;
w.send(JSON.stringify({what: 'unsubscribe', parameters: {what: event}, request_ID}));
let data = await new Promise((resolve, reject) => promises.set(request_ID++, [resolve, reject])).finally(() => promises.delete(rr));
if(data === false) {
console.error(`failed to unsubscribe to ${event} probably because never subscribed to begin with`);
} else if(data === undefined) {
console.error('unpossible');
} else {
console.log('successed?')
}
}
}
} else if(hashbrown.has(event)) {
let s = hashbrown.get(event);
s.delete(this);
if(s.size === 0) {
hashbrown.delete(event);
const rr = request_ID;
w.send(JSON.stringify({what: 'unsubscribe', parameters: {what: event}, request_ID}));
let data = await new Promise((resolve, reject) => promises.set(request_ID++, [resolve, reject])).finally(() => promises.delete(rr));
if(data === false) {
console.error(`failed to unsubscribe to ${event} probably because never subscribed to begin with`);
} else if(data === undefined) {
console.error('unpossible');
} else {
console.log('successed?')
}
}
} else {
this.postMessage("you weren't even listening to this event to begin with...");
}
}
} else {
let {what, r, parameters} = JSON.parse(e.data);
if(w.readyState > 1) {
await closing;
w = await create_ws();
}
try {
await ready;
} catch(e) {
this.postMessage({r, failure: e});
return;
}
let remember_me;
if(parameters) {
({remember_me, ...parameters} = parameters);
}
const rr = request_ID;
try {
w.send(JSON.stringify({what, parameters, request_ID}));
} catch(e) {
console.log(e);
console.log({what, parameters, request_ID});
return;
}
let data = await new Promise((resolve, reject) => promises.set(request_ID++, [resolve, reject])).finally(() => promises.delete(rr));
this.postMessage({r, data});
if(typeof remember_me === 'boolean' && typeof data === 'object') {
if(remember_me) {
storage.set('token', data.token);
}
if(what === 'register') { user = Object.assign(data, {email: parameters.email, god: false, acl: null});
} else if(what === 'login') { user = Object.assign(data, {email: parameters.email});
} else if(what === 'login_otp') { user = data;
}
const {token, ...noTokenUser} = user;
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({
what: "user",
how: "replace",
data: noTokenUser
});
}
} else if(what === 'logout' || data === 'unauthenticated' || what === 'unregister') {
user = null;
storage.delete('token');
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({what: "user", how: 'replace', data: null});
}
} else if(data === 'unauthorized') {
if(user) {
user.god = false;
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({what: "user", how: 'update', data: {god: false}});
}
} else {
console.log('nani????');
}
} else if(data === undefined && what === 'update_profile') {
if(parameters.email) {
user.email = parameters.email;
for(let i = 0; i < ports.length; ++i) {
ports[i].postMessage({what: "user", how: 'update', data: {email: user.email}});
}
}
}
}
}
if(await Promise.race([ready, "promises suck because can't synchronously inspect status"]) === undefined) {
if(stores) {
ports[idx].postMessage({
what: "store",
how: "replace",
data: stores
});
}
if(user) {
const {token, ...noTokenUser} = user;
ports[idx].postMessage({
what: "user",
how: "replace",
data: noTokenUser
});
} else {
ports[idx].postMessage({
what: "user",
how: "replace",
data: null
});
}
}
}