unistore wip
This commit is contained in:
1 parent
a43d4f32e4
commit
cf01670ee6
17 files changed
+239
-65
No files matched your search
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
parserOpts: { strictMode: false },
|
||||
plugins: ['@babel/plugin-transform-react-jsx-source']
|
||||
};
|
||||
Generated
+25
@@ -19,6 +19,7 @@
|
||||
"unistore": "^3.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.18.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"eslint": "^8.20.0",
|
||||
@@ -1598,6 +1599,21 @@
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-react-jsx-source": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz",
|
||||
"integrity": "sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-regenerator": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz",
|
||||
@@ -22250,6 +22266,15 @@
|
||||
"@babel/types": "^7.18.6"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-react-jsx-source": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz",
|
||||
"integrity": "sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.18.6"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-regenerator": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"unistore": "^3.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.18.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"eslint": "^8.20.0",
|
||||
|
||||
+18
-26
@@ -1,8 +1,11 @@
|
||||
import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { Route, Router, route } from 'preact-router';
|
||||
import { Provider, connect } from 'unistore/preact'
|
||||
|
||||
import actions from './actions';
|
||||
import { createStore, actions } from './store';
|
||||
import createWs from './ws';
|
||||
import http from './http';
|
||||
|
||||
import Header from './components/header';
|
||||
|
||||
@@ -13,47 +16,36 @@ import Login from './routes/login';
|
||||
import Search from './routes/search';
|
||||
import List from './routes/list';
|
||||
|
||||
function App() {
|
||||
const [user, setUser] = useState(null);
|
||||
const store = createStore();
|
||||
const ws = createWs(store);
|
||||
|
||||
useEffect(() => {
|
||||
async function getLogin() {
|
||||
const [error, user] = await actions.getLogin();
|
||||
async function init() {
|
||||
const [error, user] = await http.getLogin();
|
||||
if (error) return route('/login');
|
||||
setUser(user);
|
||||
route('/bundles');
|
||||
}
|
||||
store.setState({ user });
|
||||
}
|
||||
|
||||
getLogin();
|
||||
}, []);
|
||||
|
||||
function loginSuccess(user) {
|
||||
setUser(user);
|
||||
route('/search');
|
||||
}
|
||||
|
||||
async function logout(e) {
|
||||
e.preventDefault();
|
||||
await actions.postLogout();
|
||||
route('/login');
|
||||
setUser(null);
|
||||
}
|
||||
init();
|
||||
|
||||
const App = () =>{
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<main id="app">
|
||||
<Header user={user} logout={logout} />
|
||||
<Header />
|
||||
<Router>
|
||||
<Route path="/" component={Home} />
|
||||
<Route path="/login" component={Login} success={loginSuccess} mode="login" />
|
||||
<Route path="/register" component={Login} success={loginSuccess} mode="register" />
|
||||
<Route path="/login" component={Login} mode="login" />
|
||||
<Route path="/register" component={Login} mode="register" />
|
||||
<Route path="/search" component={Search} />
|
||||
<Route path="/list" component={List} user={user} />
|
||||
<Route path="/list" component={List} />
|
||||
|
||||
{/* ACP */}
|
||||
<Route path="/bundles" component={Bundles} />
|
||||
<Route path="/offers/acp" component={Admin} />
|
||||
</Router>
|
||||
</main>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function Offer(offer) {
|
||||
const [accepted, setAccepted] = useState(offer.status !== 'pending');
|
||||
|
||||
async function onClick() {
|
||||
const [error] = await actions.postOfferAccept(offer.id);
|
||||
const [error] = await http.postOfferAccept(offer.id);
|
||||
if (error) return false;
|
||||
setAccepted(true);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function Bundle(bundle) {
|
||||
|
||||
async function submit(e) {
|
||||
e.preventDefault();
|
||||
const [error, offers] = await actions.postOffers(state.checkedIds, shipping, baseShipping);
|
||||
const [error, offers] = await http.postOffers(state.checkedIds, shipping, baseShipping);
|
||||
console.log(error, offers);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import { h } from 'preact';
|
||||
import { route } from 'preact-router';
|
||||
import { connect } from 'unistore/preact'
|
||||
|
||||
import actions from './../actions';
|
||||
import http from './../http';
|
||||
import { actions } from './../store';
|
||||
|
||||
const Header = ({ user, logout }) => {
|
||||
|
||||
function logoutClick(e) {
|
||||
e.preventDefault();
|
||||
logout();
|
||||
}
|
||||
|
||||
const nav = () => (
|
||||
<nav>
|
||||
<a href="/">Offers</a>
|
||||
@@ -29,7 +37,7 @@ const Header = ({ user, logout }) => {
|
||||
const userNav = () => (
|
||||
<nav>
|
||||
<a href="/account">Account</a>
|
||||
<a href="/auth/logout" onClick={(e) => logout(e)}>Logout</a>
|
||||
<a href="/auth/logout" onClick={logoutClick}>Logout</a>
|
||||
</nav>
|
||||
|
||||
)
|
||||
@@ -43,4 +51,4 @@ const Header = ({ user, logout }) => {
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
||||
export default connect(['user'], actions)(Header);
|
||||
@@ -2,7 +2,7 @@ import { h } from 'preact';
|
||||
import { ExternalLink, Check } from 'react-feather';
|
||||
import { useState } from 'preact/hooks';
|
||||
|
||||
import actions from '../actions';
|
||||
import http from '../http';
|
||||
|
||||
export default function Offer(offer) {
|
||||
const { want: { data: details } } = offer;
|
||||
@@ -10,7 +10,7 @@ export default function Offer(offer) {
|
||||
const [accepted, setAccepted] = useState(offer.status !== 'pending');
|
||||
|
||||
async function onClick() {
|
||||
const [error] = await actions.postOfferAccept(offer.id);
|
||||
const [error] = await http.postOfferAccept(offer.id);
|
||||
if (error) return false;
|
||||
setAccepted(true);
|
||||
}
|
||||
|
||||
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
|
||||
import actions from './../actions';
|
||||
import http from './../http';
|
||||
import AcpOffer from './../components/acp.offer';
|
||||
|
||||
function Item(item) {
|
||||
@@ -40,7 +40,7 @@ function Home() {
|
||||
|
||||
useEffect(() => {
|
||||
async function getOffers() {
|
||||
const [error, offers] = await actions.getOffersAcp();
|
||||
const [error, offers] = await http.getOffersAcp();
|
||||
if (error) return console.error(error);
|
||||
setOffers(offers);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
|
||||
import actions from './../actions';
|
||||
import http from './../http';
|
||||
import Bundle from './../components/bundle';
|
||||
|
||||
function Bundles() {
|
||||
@@ -9,7 +9,7 @@ function Bundles() {
|
||||
|
||||
useEffect(() => {
|
||||
async function getBundles() {
|
||||
const [error, bundles] = await actions.getBundles();
|
||||
const [error, bundles] = await http.getBundles();
|
||||
if (error) return console.error(error);
|
||||
setBundles(bundles);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { route } from 'preact-router';
|
||||
import { connect } from 'unistore/preact'
|
||||
|
||||
import http from './../http';
|
||||
import { actions } from './../store';
|
||||
|
||||
import actions from './../actions';
|
||||
import Offer from './../components/offer';
|
||||
|
||||
function Home() {
|
||||
const [offers, setOffers] = useState([]);
|
||||
|
||||
function Home({ offers, setOffers }) {
|
||||
useEffect(() => {
|
||||
async function getOffers() {
|
||||
const [error, offers] = await actions.getOffers();
|
||||
const [error, offers] = await http.getOffers();
|
||||
if (error) return console.error(error);
|
||||
console.log(offers);
|
||||
setOffers(offers);
|
||||
if (offers.length === 0) route('/search');
|
||||
}
|
||||
@@ -35,4 +35,4 @@ function Home() {
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
export default connect(['offers'], actions)(Home);
|
||||
@@ -2,7 +2,7 @@ import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
|
||||
// import style from './style.less';
|
||||
import actions from './../actions';
|
||||
import http from './../http';
|
||||
import Want from './../components/want';
|
||||
|
||||
interface Props {
|
||||
@@ -16,7 +16,7 @@ const List = ({ user }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
async function getWants() {
|
||||
const [error, wants] = await actions.getWants();
|
||||
const [error, wants] = await http.getWants();
|
||||
if (error) return console.error(error);
|
||||
setOffers(wants);
|
||||
}
|
||||
|
||||
+23
-11
@@ -1,23 +1,27 @@
|
||||
import { h } from 'preact';
|
||||
import { useState } from 'preact/hooks';
|
||||
import actions from './../actions';
|
||||
import { route } from 'preact-router';
|
||||
import { useState, useEffect, useRef } from 'preact/hooks';
|
||||
import { connect } from 'unistore/preact'
|
||||
|
||||
const Login = ({ success, mode }) => {
|
||||
import http from './../http';
|
||||
import { actions } from './../store';
|
||||
|
||||
const Login = ({ setUser, mode }) => {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
async function postLogin() {
|
||||
const [error, user] = await actions.postLogin(email, password);
|
||||
const [error, user] = await http.postLogin(email, password);
|
||||
if (error) return setError(error);
|
||||
success(user);
|
||||
setUser(user, '/offers');
|
||||
}
|
||||
|
||||
async function postRegister() {
|
||||
const [error, user] = await actions.postRegister(email, password);
|
||||
const [error, user] = await http.postRegister(email, password);
|
||||
if (error) return setError(error);
|
||||
success(user);
|
||||
setUser(user, '/search');
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
@@ -26,6 +30,12 @@ const Login = ({ success, mode }) => {
|
||||
if (mode === 'register') return postRegister();
|
||||
}
|
||||
|
||||
const emailRef = useRef<HTMLInputElement>()
|
||||
useEffect(() => {
|
||||
emailRef.current.focus()
|
||||
}, [])
|
||||
|
||||
|
||||
const errorEl = error
|
||||
? <div class='alt-text'>{error}</div>
|
||||
: null;
|
||||
@@ -36,7 +46,7 @@ const Login = ({ success, mode }) => {
|
||||
<form action="">
|
||||
<div class="item">
|
||||
<label for="email">email</label>
|
||||
<input type="email" name="email" id="email" value={email} required onInput={(e) => setEmail((e.target as HTMLInputElement).value)} />
|
||||
<input type="email" name="email" id="email" ref={emailRef} value={email} required onInput={(e) => setEmail((e.target as HTMLInputElement).value)} />
|
||||
</div>
|
||||
<div class="item">
|
||||
<label for="password">password</label>
|
||||
@@ -60,7 +70,7 @@ const Login = ({ success, mode }) => {
|
||||
<form action="">
|
||||
<div class="item">
|
||||
<label for="email">email</label>
|
||||
<input type="email" name="email" id="email" value={email} required onInput={(e) => setEmail((e.target as HTMLInputElement).value)} />
|
||||
<input type="email" name="email" id="email" ref={emailRef} value={email} required onInput={(e) => setEmail((e.target as HTMLInputElement).value)} />
|
||||
</div>
|
||||
<div class="item">
|
||||
<label for="password">password</label>
|
||||
@@ -74,10 +84,12 @@ const Login = ({ success, mode }) => {
|
||||
);
|
||||
};
|
||||
|
||||
function LoginPage({ success, mode }) {
|
||||
const LoginForm = connect([], actions)(Login);
|
||||
|
||||
function LoginPage({ mode }) {
|
||||
return (
|
||||
<div class="landing">
|
||||
<Login success={success} mode={mode} />
|
||||
<LoginForm mode={mode} />
|
||||
<div></div>
|
||||
<div class="info">
|
||||
<p>Mates Crates is a record importing service.</p>
|
||||
|
||||
@@ -2,13 +2,13 @@ import { h } from 'preact';
|
||||
import { useState, useRef, useEffect } from 'preact/hooks';
|
||||
import { ExternalLink, Check } from 'react-feather';
|
||||
|
||||
import actions from '../actions';
|
||||
import http from '../http';
|
||||
|
||||
function Result(r) {
|
||||
const [added, setAdded] = useState(false)
|
||||
|
||||
async function onClick() {
|
||||
const [error, want] = await actions.postWant(r.id);
|
||||
const [error, want] = await http.postWant(r.id);
|
||||
if (error) return false;
|
||||
setAdded(true);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ const User = () => {
|
||||
const [infoText, setInfoText] = useState('');
|
||||
|
||||
async function fetchResults() {
|
||||
const [error, results] = await actions.postWantSearch(artist, title);
|
||||
const [error, results] = await http.postWantSearch(artist, title);
|
||||
|
||||
if (results.length === 0) {
|
||||
setResults([]);
|
||||
@@ -52,7 +52,7 @@ const User = () => {
|
||||
}
|
||||
|
||||
async function submitWantlist() {
|
||||
const [error, wants] = await actions.postDiscogs(username);
|
||||
const [error, wants] = await http.postDiscogs(username);
|
||||
if (wants.length === 0) {
|
||||
setResults([]);
|
||||
return setInfoText('No new additions');
|
||||
|
||||
+24
-4
@@ -1,9 +1,29 @@
|
||||
import createStore from 'unistore'
|
||||
import { route } from 'preact-router';
|
||||
import unistore from 'unistore'
|
||||
|
||||
export default function mcratesStore() {
|
||||
return createStore({
|
||||
import http from './http';
|
||||
|
||||
export function createStore() {
|
||||
return unistore({
|
||||
bundles: [],
|
||||
offers: []
|
||||
offers: [],
|
||||
user: null,
|
||||
});
|
||||
}
|
||||
|
||||
export const actions = store => ({
|
||||
setUser: async function setUser(state, user, dest) {
|
||||
route(dest);
|
||||
return { user };
|
||||
},
|
||||
|
||||
setOffers: async function setOffers(state, offers) {
|
||||
return { offers };
|
||||
},
|
||||
|
||||
logout: async function logout(store, e) {
|
||||
await http.postLogout();
|
||||
route('/login');
|
||||
return { user: null };
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,115 @@
|
||||
const SOCKET_URL = (window.location.host.includes('localhost') || window.location.host === '' || window.location.host.includes('192.168.0'))
|
||||
? `ws://${window.location.hostname}:40080`
|
||||
: `${window.location.protocol === 'https:' ? 'wss://' : 'ws://'}${window.location.host}/api/ws`;
|
||||
|
||||
export default function createSocket(store) {
|
||||
let ws;
|
||||
|
||||
// -------------
|
||||
// Outgoing
|
||||
// -------------
|
||||
function send(msg) {
|
||||
if (msg[0] !== 'Ping') console.log('outgoing msg', msg);
|
||||
if (ws.readyState !== 1) return false;
|
||||
try {
|
||||
ws.send(JSON.stringify(msg));
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
// let ping;
|
||||
function sendPing() {
|
||||
// ping = Date.now();
|
||||
send(['Ping', {}]);
|
||||
|
||||
// setTimeout(sendPing, 50);
|
||||
}
|
||||
|
||||
function sendSourcesAvailable() {
|
||||
send(['SourcesAvailable', {}]);
|
||||
}
|
||||
|
||||
let pongTimeout;
|
||||
function onPong() {
|
||||
pongTimeout = setTimeout(sendPing, 1000);
|
||||
}
|
||||
|
||||
// -------------
|
||||
// Incoming
|
||||
// -------------
|
||||
function onSourcesAvailable(clouds) {
|
||||
// store.dispatch(http.LIBRARY_RECEIVE_POINTCLOUDS, clouds);
|
||||
}
|
||||
|
||||
// -------------
|
||||
// Setup
|
||||
// -------------
|
||||
// when the server sends a reply it will have one of these message types
|
||||
// this object wraps the reply types to a function
|
||||
const handlers = {
|
||||
SourcesAvailable: onSourcesAvailable,
|
||||
|
||||
Pong: onPong,
|
||||
Error: errHandler,
|
||||
};
|
||||
|
||||
function errHandler(error) {
|
||||
// store.dispatch(http.LIBRARY_EVENT, { level: 'error', log: error });
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
function onMessage(event) {
|
||||
const res = event.data;
|
||||
const [msgType, params] = JSON.parse(res);
|
||||
if (msgType !== 'Pong') {
|
||||
console.log(msgType, params);
|
||||
}
|
||||
|
||||
// check for error and split into response type and data
|
||||
if (!handlers[msgType]) return console.error(`${msgType} handler missing`);
|
||||
return handlers[msgType](params);
|
||||
}
|
||||
|
||||
// Connection opened
|
||||
function onOpen() {
|
||||
sendPing();
|
||||
}
|
||||
|
||||
function onError(event) {
|
||||
console.error('WebSocket error', event);
|
||||
}
|
||||
|
||||
function onClose(event) {
|
||||
console.error('WebSocket closed', event);
|
||||
return setTimeout(connect, 2000);
|
||||
}
|
||||
|
||||
function connect() {
|
||||
if (ws) {
|
||||
clearTimeout(pongTimeout);
|
||||
ws.removeEventListener('open', onOpen);
|
||||
ws.removeEventListener('message', onMessage);
|
||||
ws.removeEventListener('error', onError);
|
||||
ws.removeEventListener('close', onClose);
|
||||
ws = null;
|
||||
}
|
||||
|
||||
console.log('connecting to websocket', SOCKET_URL);
|
||||
ws = new WebSocket(SOCKET_URL);
|
||||
|
||||
// Listen for messages
|
||||
ws.addEventListener('open', onOpen);
|
||||
ws.addEventListener('message', onMessage);
|
||||
ws.addEventListener('error', onError);
|
||||
ws.addEventListener('close', onClose);
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
return {
|
||||
connect,
|
||||
|
||||
sendSourcesAvailable,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user