import { render } from "solid-js/web";
import { createState, createSignal, reconcile, createMemo } from "solid-js";
import { req, on } from './websocket';
import { create_deferred, c, Null } from './util';
import { state, setState } from './store';
import storage from 'kv-storage-polyfill';
import './index.css';
import NotFound from './pages/NotFound';
import NotAuthorized from './pages/NotAuthorized';
import NotAuthenticated from './pages/NotAuthenticated';
import Home from './pages/Home';
import User from './pages/User';
import Users from './pages/Users';
import Products from './pages/Products';
onpopstate = function() {
setState('path', location.pathname.substring(1));
}
if(history.state === null) {
history.replaceState({}, null);
}
function atHome() {return state.path.length === 0}
function atCart() {return state.path === 'cart/'}
const reservationRegex = /^\/((?<id>\d+)\/)?reservation\/$/;
const spyRegex = /^\/(?<id>\d+)$/;
const fuckRegex = /^((store\/(?<sid>\d+)(-(?<sslug>[A-Za-z0-9_]+))?\/(?<thing>product|review))|product)\/(?<rest>.*)$/;
const somethingRegex = /^(?<id>\d+)(-(?<slug>[A-Za-z0-9_]+))?(\/edit)?$/;
const atSpecificStoreRegex = /^(?<id>\d+)(-(?<slug>[A-Za-z0-9_]+))?\/(?<what>.+)/;
render(function() {
const [wsSuccess, setWsSuccess] = createSignal(null);
//disconnected, store, user are the three possible first events
on('disconnected', function() {
if(wsSuccess() === null) {
setWsSuccess(false);
}
});
on('store', function(how, data) {
if(wsSuccess() === null) {
setWsSuccess(true);
}
switch(how) {
case 'replace':
setState('stores', reconcile(data, {key: 'product_id'}));
break;
case 'add':
setState('stores', state.stores.length, data);
break;
case 'remove':
setState('stores', stores => stores.filter(store => store.id !== data));
break;
case 'update':
setState('stores', state.stores.findIndex(store => store.id === data.id), data);//I think we do not want reconcile here since this is partial...
break;
}
});
on('user', function(how, data, why) {
if(why) {//&& wsSuccess() so that alert isn't the first thing user sees on navigating to the site. up for discussion
alert(why);
}
if(how === 'replace' && data !== null) {
setState('user', reconcile(data));
} else {//update || null
setState('user', data);
}
if(wsSuccess() === null) {
setWsSuccess(true);
}
});
let email;
const [passphrase, setPassphrase] = createSignal('');
let remember_me;
const [pp_visible, set_pp_visible] = createSignal(false);
const [action, setAction] = createSignal('login');
const atUser = createMemo(() => state.path.indexOf('user') === 0 ? state.path.substring(4) : false, false, true);
const atReservations = createMemo(() => {
const a = atUser();
if(a === false) {
return undefined;
} else {
return a.match(reservationRegex)?.groups;
}
}, undefined, (a, b) => a === b || typeof a === 'object' && typeof b === 'object' && a.id === b.id);
const spy = createMemo(() => {
const a = atUser();
if(a === false) {
return undefined;
} else {
return a.match(spyRegex)?.groups;
}
}, undefined, (a, b) => a === b || typeof a === 'object' && typeof b === 'object' && a.id === b.id);
const fuck = createMemo(() => state.path.match(fuckRegex)?.groups, undefined, (a, b) => a === b || typeof a === 'object' && typeof b === 'object' && a.sid === b.sid && a.sslug === b.sslug && a.thing === b.thing && a.rest === b.rest);
const something = createMemo(() => {
const f = fuck();
if(f === undefined) {
return undefined;
} else {
return f.rest.match(somethingRegex)?.groups;
}
}, undefined, (a, b) => a === b || typeof a === 'object' && typeof b === 'object' && a.id === b.id && a.slug === b.slug);
const atStore = createMemo(() => state.path.indexOf('store/') === 0 ? state.path.substring(6) : false, false, true);
const atSpecificStore = createMemo(() => {
const w = atStore();
if(w === false) {
return undefined;
} else {
return w.match(atSpecificStoreRegex)?.groups;
}
}, undefined, (a, b) => a === b || typeof a === 'object' && typeof b === 'object' && a.id === b.id && a.slug === b.slug && a.what === b.what);
const gay = createMemo(() => fuck()?.sid === undefined || fuck()?.rest === 'new' || fuck()?.rest === 'edit' ? Null : Store, Null, true);
function login_success() {
storage.delete('carts');
setPassphrase('');
}
return <Switch fallback={<>
<header>
<nav>
<a class="pull" classList={{wow: atHome()}} onClick={c} href="/">home</a><br />
<a classList={{wow: atCart()}} onClick={c} href="/cart/">cart</a><br />
<a classList={{wow: state.path.indexOf('product') === 0}} onClick={c} href="/product/">products</a><br />
<a classList={{wow: atStore() !== false}} onClick={c} href="/store/">stores</a><br />
<Show when={state.user}>
<a classList={{wow: atUser() !== false}} onClick={c} href="/user">{state.user.email}</a><br />
</Show>
</nav>
<Show when={state.user} fallback={<>
<form onsubmit={async e => {
e.preventDefault();
switch(action()) {
case 'login':
case 'register':
req(action(), {
remember_me: remember_me.checked,
email: email.value,
passphrase: passphrase(),
carts: await storage.get('carts')
}).then(login_success, alert);
break;
case 'login_otp':
req('login_otp', {
remember_me: remember_me.checked,
carts: await storage.get('carts'),
otp: passphrase()
}).then(login_success, alert);
break;
case 'request_otp':
req('request_otp', {email: email.value}).then(() => {
alert('check your email');
setPassphrase('');
}, alert);
break;
}
}}>
<label>
email
<input required={action() !== 'login_otp'} type="email" placeholder="me@domain.com" ref={email} />
</label>
<label style="display: flex /*https://philipwalton.github.io/solved-by-flexbox/demos/input-add-ons/ this just makes the button height match and line up with the input field (2px woopdedoo)*/">
passphrase
<input class="longplaceholder" type={pp_visible() ? "text" : "password"} placeholder="a long passphrase is both easier to remember and more secure than a password!" value={passphrase()} onInput={e => setPassphrase(e.target.value)} />
<button type="button" onClick={() => set_pp_visible(!pp_visible())}>{pp_visible() ? '👀' : '🙈'}</button>
</label>
<label><input type="checkbox" ref={remember_me}/>remember me</label>
<button onClick={() => setAction('login')}>login</button>
<button onClick={() => setAction('register')}>register</button>
<button onClick={() => {
if(passphrase()) {
setAction('login_otp');
} else {
setAction('request_otp');
}
}}>{passphrase() ? 'login via code from email' : 'send login link to email'}</button>
</form>
</>}>
<button type="button" onClick={() => req('logout')}>logout</button>
</Show>
</header>
<main>
<Switch>
<Match when={atHome()}><Home /></Match>
<Match when={atCart()}><Cart /></Match>
<Match when={atUser() !== false}>
<Switch fallback={NotFound}>
<Match when={atUser().length === 0}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user}>
<User />
</Match>
</Switch>
</Match>
<Match when={atUser() === '/'}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god}>
<Users />
</Match>
<Match when={state.user?.god === false}>
<NotAuthorized />
</Match>
</Switch>
</Match>
<Match when={atReservations()}>
<Switch fallback={NotAuthenticated}>
<Match when={atReservations().id !== undefined && state.user?.god === false}>
<NotAuthorized />
</Match>
<Match when={atReservations().id !== undefined && state.user?.god || atReservations().id === undefined && state.user}>
<User_Reservations id={atReservations().id}/>
</Match>
</Switch>
</Match>
<Match when={spy()}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god}>
<User id={spy().id} />
</Match>
<Match when={state.user?.god === false}><NotAuthorized /></Match>
</Switch>
</Match>
</Switch>
</Match>
<Match when={fuck()}>
<Dynamic component={gay()} id={fuck()?.sid} slug={fuck()?.sslug}>
{/*@once*/<Switch fallback={NotFound}>
<Match when={fuck()?.thing === 'review' && fuck()?.rest.length === 0}>reviews</Match>
<Match when={(fuck()?.thing === 'product' || fuck()?.sid === undefined)}>
<Switch fallback={NotFound}>
<Match when={fuck()?.rest.length === 0}><Products /></Match>
<Match when={something()}><Product id={something().id} />need switch</Match>
<Match when={fuck()?.rest === 'new'}>new need switch</Match>
<Match when={fuck()?.rest === 'edit'}>edit need switch</Match>
</Switch>
</Match>
</Switch>}
</Dynamic>
</Match>
<Match when={atStore() !== false}>
<Switch fallback={NotFound}>
<Match when={atStore().length === 0}>
<Stores />
</Match>
<Match when={atStore() === 'new'}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god}><CreateStore /></Match>
<Match when={state.user?.god === false}><NotAuthorized /></Match>
</Switch>
</Match>
<Match when={atStore() === 'edit'}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god}>bulk / table edit of stores</Match>
<Match when={state.user?.god === false}><NotAuthorized /></Match>
</Switch>
</Match>
<Match when={atStore() === 'reservation/'}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god}>super aggregated stats to stroke ego</Match>
<Match when={state.user?.god === false}><NotAuthorized /></Match>
</Switch>
</Match>
<Match when={atSpecificStore()}>
<Switch fallback={NotFound}>
<Match when={atSpecificStore().what === 'edit'}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god || state.user?.acl?.[atSpecificStore().id]}>form here</Match>
<Match when={state.user?.acl?.[atSpecificStore().id] === false}><NotAuthorized /></Match>
</Switch>
</Match>
<Match when={atSpecificStore().what === 'reservation/'}>
<Switch fallback={NotAuthenticated}>
<Match when={state.user?.god || typeof state.user?.acl?.[atSpecificStore().id] === 'boolean'}>store_reservations id=atSpecificStore().id</Match>
<Match when={state.user?.acl?.[atSpecificStore().id] === undefined}><NotAuthorized /></Match>
</Switch>
</Match>
</Switch>
</Match>
</Switch>
</Match>
</Switch>
</main>
<footer>
Sticky footer {new Date().getFullYear()}
</footer>
</>}>
<Match when={wsSuccess() === false}>
can't connect to backend. nothing is going to work
</Match>
</Switch>
}, document.body);