import querystring from 'query-string'
import { WS } from './lib/websocket'
import { goto } from '@sapper/app'
class Bootstrap {
constructor() {
this.url = null
window.notify = this.notify.bind(this)
const parseWebsocketOrigin = (string) => {
if (/localhost/.test(string)) {
return {
host: 'localhost',
protocol: 'ws://',
port: 25706
}
} else {
return {
host: 'www.processor.' + string.match(/(?<=\/\/)(www.app.)?([^:]*)/)[2],
protocol: 'wss://',
}
}
}
const ws = new WS(
Object.assign(parseWebsocketOrigin(location.origin), {
connection: 'node'
})
)
ws.on('open', () => {
this.notify({
error: 'Connected to node',
duration: 1000,
prompt: false,
duplicate: false
})
})
ws.on('message', ({ data }) => {
try {
const message = JSON.parse(data)
console.log(`incoming ${data}`)
switch (message.o) {
case 'p':
case 'r':
if (message.v === null) {
message.v = undefined
}
const path = message.p
const value = message.v
if (_.isPlainObject(message.v)) {
datastore.merge(path, value)
} else {
datastore.set(path, value)
}
const queued = datastore.get('/q' + message.p)
if (_.isEqual(message.v, queued)) {
const q_path = '/q' + message.p
const q_steps = q_path.slice(1).split('/')
datastore.delete(q_steps, { silent: true })
}
break
case 'a':
console.log(message)
if (message.v === undefined) { return }
console.log(message.c)
switch (message.c) {
case 0:
const pointer = Pointer
.create(message.t)
.replace('/#', '/create')
.replace('state', 'action')
datastore.write(pointer, message.v)
datastore.destroy(pointer, message.v)
if (!message.v) authorization.reset(message.t)
break
case 2:
authorization.deriveSharedKey(message.t, message.v, ws)
break
case 4:
if (authorization.verifySessionKey(message.t, message.v, ws) &&
/\/users/.test(message.t)) {
authorization.requestPersistantSession(message.v.u, ws)
}
break
case 5:
if (!message.v) { break }
authorization.pickUpSession(message.t, message.v, ws)
break
case 6:
if (!message.v) { break }
authorization.setPersistantSession(message.v, ws)
break
}
break
}
} catch (e) {
console.error('Error parsing message from server.', e, data)
}
})
ws.on('close', () => {
this.notify({
error: 'Disconnected from djin node',
duration: 0,
duplicate: false
})
})
const subscriber = {}
datastore.subscribe('q/setup/#', subscriber, (topic, pointer, value) => {
if (_.isPlainObject(value)) {
return
}
send(value, pointer.path)
})
datastore.subscribe('session/redirect', subscriber, (topic, pointer, value) => {
if (_.isString(value)) {
datastore.write('/session/redirect', null)
goto(value)
}
})
datastore.subscribe('session/path/*', this, (topic, pointer, value) => {
try {
const { route, query, hash } = value
let next = route || '/'
localStorage.setItem('djinlist', JSON.stringify(value))
const session_query = {}
Object.keys(query || {})
.sort()
.forEach(key => {
session_query[key] = query[key]
})
const session_hash = {}
Object.keys(hash || {})
.sort()
.forEach(key => {
session_hash[key] = hash[key]
})
next = next +
(session_query && (session_query.length != 0) ? '?' + querystring.stringify(session_query) : '') +
(session_hash && (session_hash.length != 0) ? '#' + querystring.stringify(session_hash) : '')
if ((location.pathname || '' + location.query || '' + location.hash || '') === next) {
return
}
if (!('standalone' in navigator && navigator.standalone === true)) {
history.replaceState({}, '', next)
}
} catch (e) {
return
}
})
if (!('standalone' in navigator && navigator.standalone === true)) {
window.addEventListener('popstate', this.parse)
}
setTimeout(() => this.initialize(), 0)
}
parse() {
const origin = location.origin
const route = location.pathname
const query = querystring.parse(location.search.slice(1))
const hash = querystring.parse(location.hash.slice(1))
let match = route.match(/^(\/\w+)*/)
let evaluateRoute = ({ route, query, hash }) => {
datastore.write('/session/path', {
origin,
route,
query,
hash
})
}
if (match != null) {
let [, route ] = match
evaluateRoute({ route, query, hash })
} else {
const local = localStorage.getItem('djinlist')
if (local == null) {
evaluateRoute({ route: null, query, hash })
return
}
const local_json = JSON.parse(local)
evaluateRoute(_.merge(local_json, {query, hash}))
}
return
}
initialize() {
this.parse()
}
notify(options) {
let id = 1
while (datastore.has(`/session/notifications/${id}`)) {
id += 1
}
const notification = Object.assign(
{},
{
id,
time: Date.now(),
duration: 2000,
prompt: true,
message: options.error.toString() || 'An error occurred',
error: 'An error occurred',
duplicate: true,
overwrite: false
},
options
)
if (!notification.duplicate) {
const notifications = datastore.read('/session/notifications/+')
for (const n of Object.values(notifications)) {
if (n.message === notification.message && !n.hidden && !n.overwrite) {
return
}
}
}
datastore.write(`/session/notifications/${id}`, notification)
}
}
export { Bootstrap }