navbar and notifications
This commit is contained in:
1 parent
a97560c1c7
commit
5b0c7c05ca
11 files changed
+65
-46
No files matched your search
@@ -3,9 +3,9 @@ push offers
|
||||
|
||||
add last synced time to want
|
||||
|
||||
contact options on landing page
|
||||
go over and reorganise event logic
|
||||
|
||||
move want add to rpc
|
||||
contact options on landing page
|
||||
|
||||
skip unofficial release
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ datasource db {
|
||||
}
|
||||
|
||||
model Listing {
|
||||
|
||||
// example listingId 2148851036
|
||||
// works fine as a JS number
|
||||
// but JS numbers are cast to int4 for pg by prisma
|
||||
// becomes -2146116260
|
||||
id BigInt @id
|
||||
seller BigInt
|
||||
data Json
|
||||
|
||||
@@ -63,16 +63,6 @@ export default function createApi(log: Log, pg: Pg, discogs: Discogs, offers: Of
|
||||
ctx.status = 200;
|
||||
});
|
||||
|
||||
router.post('/want', async function wantPost(ctx, next) {
|
||||
ctx.assert(ctx.isAuthenticated(), 401, 'log in required');
|
||||
const { releaseId } = ctx.request.body;
|
||||
const user = await pg.userFindId(ctx.state.user.id);
|
||||
const release = await discogs.release(releaseId);
|
||||
const want = await pg.wantCreate(release, user);
|
||||
events.emit(Events.WantsAdded, [want]);
|
||||
ctx.response.body = [want];
|
||||
});
|
||||
|
||||
router.post('/want/discogs', async function wantListPost(ctx, next) {
|
||||
ctx.assert(ctx.isAuthenticated(), 401, 'log in required');
|
||||
const { username } = ctx.request.body;
|
||||
|
||||
+11
-7
@@ -12,11 +12,11 @@ import routerAuth from './routers/router.auth';
|
||||
export enum Events {
|
||||
WantsAdded = 'WantsAdded',
|
||||
Sync = 'Sync',
|
||||
ListingsUpdated = 'ListingsUpdated',
|
||||
Connection = 'Connection',
|
||||
|
||||
OfferUpdated = 'OfferUpdated',
|
||||
WantUpdated = 'WantUpdated',
|
||||
ListingsUpdated = 'ListingsUpdated',
|
||||
OffersUpdated = 'OffersUpdated',
|
||||
WantsUpdated = 'WantsUpdated',
|
||||
}
|
||||
|
||||
export default function createEvents(log: Log, pg: Pg, discogs, offers) {
|
||||
@@ -68,6 +68,10 @@ export default function createEvents(log: Log, pg: Pg, discogs, offers) {
|
||||
events.on(Events.ListingsUpdated, async function listingsUpdated() {
|
||||
const updatedOffers = await offers.generate()
|
||||
log.debug({ offers: updatedOffers.length }, 'offers up to date');
|
||||
|
||||
for (const [id, rpc] of connections) {
|
||||
rpc.pushUserOffers();
|
||||
}
|
||||
});
|
||||
|
||||
events.on(Events.Connection, async function onConnection({ rpc, id }) {
|
||||
@@ -75,17 +79,17 @@ export default function createEvents(log: Log, pg: Pg, discogs, offers) {
|
||||
connections.set(id, rpc);
|
||||
})
|
||||
|
||||
events.on(Events.OfferUpdated, async function onOfferUpdated(offer: Offer) {
|
||||
events.on(Events.OffersUpdated, async function onOffersUpdated(offer: Offer) {
|
||||
const rpc = connections.get(offer.userId);
|
||||
if (!rpc) return null;
|
||||
log.debug({ event: Events.OfferUpdated, rpc, offer });
|
||||
log.debug({ event: Events.OffersUpdated, rpc, offer });
|
||||
return rpc.pushUserOffers();
|
||||
});
|
||||
|
||||
events.on(Events.WantUpdated, async function onWantUpdated(want: Want) {
|
||||
events.on(Events.WantsUpdated, async function onWantsUpdated(want: Want) {
|
||||
const rpc = connections.get(want.userId);
|
||||
if (!rpc) return null;
|
||||
log.debug({ event: Events.WantUpdated, rpc, want });
|
||||
log.debug({ event: Events.WantsUpdated, rpc, want });
|
||||
return rpc.pushUserWants();
|
||||
});
|
||||
|
||||
|
||||
+3
-2
@@ -21,8 +21,8 @@ export default function createNotify(log: Log, pg: Pg, events) {
|
||||
log.debug({ channel: OFFERS_CHANNEL, action, id });
|
||||
if (action === 'DELETE') return null;
|
||||
|
||||
const offer = await pg.offerFindId(id);
|
||||
// events.emit(Events.OfferUpdated, offer);
|
||||
// const offer = await pg.offerFindId(id);
|
||||
// events.emit(Events.OffersUpdated, offer);
|
||||
}
|
||||
|
||||
async function handleUsers(payload) {
|
||||
@@ -37,6 +37,7 @@ export default function createNotify(log: Log, pg: Pg, events) {
|
||||
|
||||
async function handleWants(payload) {
|
||||
const { action, id } = payload;
|
||||
// const want = await pg.offerFindId(id);
|
||||
log.debug({ channel: WANTS_CHANNEL, action, id });
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,12 @@ export default function createPg(log: Log, prisma: PrismaClient) {
|
||||
});
|
||||
}
|
||||
|
||||
async function wantFindId(id: UUID) {
|
||||
return prisma.want.findFirstOrThrow({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async function wantList(user: User) {
|
||||
return prisma.want.findMany({
|
||||
where: { userId: user.id },
|
||||
|
||||
@@ -37,6 +37,7 @@ export enum RpcMessageType {
|
||||
AcpListingsSync = 'AcpListingsSync',
|
||||
|
||||
WantRm = 'WantRm',
|
||||
WantAdd = 'WantAdd',
|
||||
}
|
||||
|
||||
type ErrorMessage = [RpcMessageType.Error, string];
|
||||
@@ -82,6 +83,10 @@ type WantRmParams = {
|
||||
id: UUID,
|
||||
};
|
||||
|
||||
type WantAddParams = {
|
||||
releaseId: number,
|
||||
};
|
||||
|
||||
function checkMsgType(msgType: RpcMessageType): msgType is RpcMessageType {
|
||||
if (RpcMessageType[msgType]) {
|
||||
return true;
|
||||
@@ -124,6 +129,18 @@ export default function createRpc(log: Log, pg: Pg, ws, discogs: Discogs, offers
|
||||
return [RpcMessageType.Wants, wants];
|
||||
}
|
||||
|
||||
async function wantAdd(params: WantAddParams): Promise<WantsMessage> {
|
||||
const { releaseId } = params;
|
||||
assert(releaseId, 'no id supplied');
|
||||
|
||||
const release = await discogs.release(releaseId);
|
||||
const want = await pg.wantCreate(release, user);
|
||||
events.emit(Events.WantsAdded, [want]);
|
||||
|
||||
const wants = await pg.userWants(user);
|
||||
return [RpcMessageType.Wants, wants];
|
||||
}
|
||||
|
||||
async function acpOfferStatusSet(params: OfferStatusSetParams): Promise<AcpOffersMessage> {
|
||||
const { id, status } = params;
|
||||
assert(isAdmin(user), 'not authorised');
|
||||
@@ -171,6 +188,8 @@ export default function createRpc(log: Log, pg: Pg, ws, discogs: Discogs, offers
|
||||
|
||||
case RpcMessageType.WantRm:
|
||||
return wantRm(params);
|
||||
case RpcMessageType.WantAdd:
|
||||
return wantAdd(params);
|
||||
|
||||
case RpcMessageType.AcpOfferStatusSet:
|
||||
return acpOfferStatusSet(params);
|
||||
|
||||
@@ -49,8 +49,12 @@ function OfferPriceBtn(offer) {
|
||||
? offer.status
|
||||
: 'Accept';
|
||||
|
||||
const title = accepted
|
||||
? 'Click to cancel'
|
||||
: 'Click to accept this offer';
|
||||
|
||||
return (
|
||||
<button disabled={confirmed} onClick={() => setStatus()} class={classes}>${offer.total} {offer.condition}</button>
|
||||
<button disabled={confirmed} title={title} onClick={() => setStatus()} class={classes}>${offer.total} {offer.condition}</button>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,15 +33,10 @@ api.interceptors.response.use(onSuccess, onError);
|
||||
|
||||
const actions = {
|
||||
// TODO move to rpc
|
||||
|
||||
postWantSearch: async function postWantSearch(query) {
|
||||
return api.post('/want/search', { query });
|
||||
},
|
||||
|
||||
postWant: async function postWant(releaseId) {
|
||||
return api.post('/want', { releaseId });
|
||||
},
|
||||
|
||||
postDiscogs: async function postDiscogs(username) {
|
||||
return api.post('/want/discogs', { username });
|
||||
},
|
||||
@@ -71,14 +66,6 @@ const actions = {
|
||||
password,
|
||||
});
|
||||
},
|
||||
|
||||
postOffers: async function postOffers(listingIds, shipping, baseShipping) {
|
||||
return api.post('/offers', {
|
||||
listingIds,
|
||||
shipping,
|
||||
baseShipping,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default actions;
|
||||
@@ -7,25 +7,22 @@ import { State } from './../store';
|
||||
|
||||
import http from '../http';
|
||||
|
||||
function Result(r, wants) {
|
||||
const [added, setAdded] = useState(false)
|
||||
|
||||
function Result(r, wants, ws) {
|
||||
async function onClick() {
|
||||
const [error, want] = await http.postWant(r.id);
|
||||
if (error) return false;
|
||||
setAdded(true);
|
||||
ws.sendWantAdd(r.id);
|
||||
}
|
||||
|
||||
const wanted = wants.some(w => w.release === r.id);
|
||||
// releaseIds are bigints on server
|
||||
const wanted = wants.some(w => BigInt(w.release) === BigInt(r.id));
|
||||
|
||||
const classes = `${added || wanted ? 'added' : ''}`;
|
||||
const btnText = added
|
||||
const classes = `${wanted ? 'added' : ''}`;
|
||||
const btnText = wanted
|
||||
? <Check />
|
||||
: '+';
|
||||
|
||||
return (
|
||||
<article key={r.id}>
|
||||
<button onClick={onClick} disabled={added} class={classes} >{btnText}</button>
|
||||
<button onClick={onClick} disabled={wanted} class={classes} >{btnText}</button>
|
||||
<div>
|
||||
<h3>{r.title} <a href={`https://discogs.com${r.uri}`} target="_blank"><ExternalLink /></a></h3>
|
||||
<h5 class='alt-text' >{r.year}</h5>
|
||||
@@ -45,6 +42,7 @@ const User = () => {
|
||||
const [infoText, setInfoText] = useState('');
|
||||
|
||||
const wants = useSelector((state: State) => state.wants);
|
||||
const ws = useSelector((state: State) => state.ws);
|
||||
|
||||
async function fetchResults() {
|
||||
const [error, results] = await http.postWantSearch(query);
|
||||
@@ -114,7 +112,7 @@ const User = () => {
|
||||
</div>
|
||||
<div class='results' ref={resultsRef}>
|
||||
{infoEl}
|
||||
{results.map(r => Result(r, wants))}
|
||||
{results.map(r => Result(r, wants, ws))}
|
||||
{topEl}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +37,10 @@ export default function createSocket(dispatch) {
|
||||
send(['WantRm', { id }]);
|
||||
}
|
||||
|
||||
function sendWantAdd(releaseId) {
|
||||
send(['WantAdd', { releaseId }]);
|
||||
}
|
||||
|
||||
function sendOffers() {
|
||||
send(['Offers', {}]);
|
||||
}
|
||||
@@ -157,6 +161,7 @@ export default function createSocket(dispatch) {
|
||||
|
||||
sendOfferStatusSet,
|
||||
sendWantRm,
|
||||
sendWantAdd,
|
||||
|
||||
sendOffers,
|
||||
sendWants,
|
||||
|
||||
Reference in new issue
Block a user