Skip to content

Commit

Permalink
Add better save functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
oamaok committed Oct 17, 2021
1 parent 5d4277e commit 7555db4
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 74 deletions.
2 changes: 1 addition & 1 deletion client/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const getIdentity = () => {
return get('/api/identity')
}

export const saveNewPatch = (patch: Patch) => {
export const savePatch = (patch: Patch) => {
return post('/api/patch', { body: patch })
}

Expand Down
5 changes: 3 additions & 2 deletions client/src/components/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const initialize = async () => {
history.replaceState({}, '', '/')
loadSaveState()
} else {
loadPatch(patch.patch)
loadPatch(patch)
}

break
Expand All @@ -47,7 +47,8 @@ const initialize = async () => {
}

const loadPatch = async (savedPatch: types.Patch) => {
const { currentId, modules, knobs, cables } = savedPatch
const { currentId, modules, knobs, cables, metadata } = savedPatch
patch.metadata = metadata
patch.knobs = knobs
patch.currentId = currentId
patch.modules = modules
Expand Down
37 changes: 26 additions & 11 deletions client/src/components/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { h, useState } from 'kaiku'
import state, { patchDetails, patch } from '../../state'
import { h, useState, Fragment } from 'kaiku'
import state, { patch } from '../../state'
import MenuBar, { VerticalDivider } from '../menu-bar/MenuBar'
import classNames from 'classnames/bind'
import styles from './header.css'
Expand All @@ -9,17 +9,33 @@ const css = classNames.bind(styles)

const Menu = () => {
const savePatch = async () => {
const res = await api.saveNewPatch(patch)
const res = await api.savePatch(patch)

history.pushState({}, '', `/patch/${res.id}`)
}

const isOwnPatch =
state.route.name === 'index' ||
(state.user && state.user.id === patch.metadata.author?.id)

return (
<div className={css('menu')}>
{state.user && (
<button className={css('item')} onClick={savePatch}>
Save patch
</button>
{isOwnPatch && (
<div className={css('patch-settings')}>
<div className={css('name')}>
Patch name
<input
type="text"
value={patch.metadata.name}
onInput={(evt) => {
patch.metadata.name = evt.target.value
}}
/>
</div>
<button className={css('item')} onClick={savePatch}>
Save patch
</button>
</div>
)}
{/*
<div className={css('item')}>New patch</div>
Expand All @@ -35,7 +51,7 @@ const Header = () => {
const openMenu = () => {}

const patchAuthor =
patchDetails.author?.username ?? state.user?.username ?? 'anonymous'
patch.metadata.author?.username ?? state.user?.username ?? 'anonymous'

return (
<MenuBar top left>
Expand All @@ -46,13 +62,12 @@ const Header = () => {
}}
className={css('menu-button')}
>
Patch
</button>
<VerticalDivider />
<div className={css('patch-name')}>
Patch:
<i>
{patchDetails.name}{patchAuthor}
<b>{patch.metadata.name}</b> by <b>{patchAuthor}</b>
</i>
</div>

Expand Down
18 changes: 8 additions & 10 deletions client/src/components/header/header.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,7 @@
min-width: 200px;
}

.patch-name i {
margin: 0 8px;
}

.patch-details input {
margin-left: 10px;
}

.menu-button {
text-decoration: none;
margin-left: 72px;
}

Expand All @@ -39,7 +30,7 @@
flex-direction: column;
position: absolute;
box-shadow: var(--box-shadow);
width: 200px;
width: 400px;
}

.menu::after {
Expand All @@ -53,6 +44,13 @@
transform: rotate(45deg);
}

.patch-settings {
}

.patch-settings .name input {
margin-left: 10px;
}

.menu .item {
margin-bottom: 10px;
}
Expand Down
13 changes: 6 additions & 7 deletions client/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ const state = createState<State>({
user: null,
hint: null,
activeModule: null,
patchDetails: {
id: null,
name: 'untitled',
version: 0,
author: null,
},
patch: {
metadata: {
id: null,
author: null,
name: 'untitled',
},
currentId: 0,
modules: {},
knobs: {},
Expand All @@ -30,7 +29,7 @@ const state = createState<State>({
})

export default state
export const { cursor, patchDetails, viewport, patch, socketPositions } = state
export const { cursor, viewport, patch, socketPositions } = state
export const nextId = () => `${patch.currentId++}` as Id

window.addEventListener('popstate', () => {
Expand Down
6 changes: 0 additions & 6 deletions client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ export type State = {
cursor: Vec2
socketPositions: Record<Id, Record<string, Vec2>>
viewOffset: Vec2
patchDetails: {
id: null | string
name: string
author: null | User
version: number
}
patch: Patch
route: Route
activeCable: {
Expand Down
19 changes: 14 additions & 5 deletions common/validators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as t from 'io-ts'

const nullable = <T extends t.Any>(x: T) => t.union([x, t.null])

export const Vec2 = t.type({
x: t.number,
y: t.number,
Expand All @@ -25,18 +27,25 @@ export const ModuleState = t.type({
state: t.union([t.undefined, t.unknown]),
})

export const User = t.type({
id: t.string,
username: t.string,
})

export const PatchMetadata = t.type({
id: nullable(t.string),
name: t.string,
author: nullable(User),
})

export const Patch = t.type({
metadata: PatchMetadata,
currentId: t.number,
modules: t.record(t.string, ModuleState),
knobs: t.record(t.string, t.record(t.string, t.number)),
cables: t.array(Cable),
})

export const User = t.type({
id: t.string,
username: t.string,
})

export const UserRegistration = t.type({
username: t.string,
password: t.string,
Expand Down
46 changes: 24 additions & 22 deletions server/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,30 @@ export const getUserPatches = (userId: string) => {
`)
}

export const getLatestPatchVersion = async (patchId: string) => {
export const getLatestPatchVersion = async (
patchId: string
): Promise<Patch | null> => {
const [patch] = await query(sql`
SELECT id, patch, name version, createdAt
SELECT patches.id AS id, patch, name, users.id AS authorId, users.username AS authorName, version
FROM patches
WHERE id = ${patchId}
JOIN users ON users.id = patches.authorId
WHERE patches.id = ${patchId}
ORDER BY version DESC
LIMIT 1
`)

if (!patch) return null

return {
...patch,
patch: JSON.parse(patch.patch),
metadata: {
id: patch.id,
name: patch.name,
author: {
id: patch.authorId,
username: patch.authorName,
},
},
...JSON.parse(patch.patch),
}
}

Expand All @@ -106,44 +116,36 @@ export const getPatchVersion = (patchId: string, version: number) => {
WHERE id = ${patchId} AND version = ${version}
`)
}
export const saveNewPatch = async (
userId: string,
patchName: string,
patch: Patch
) => {
export const saveNewPatch = async (userId: string, patch: Patch) => {
const patchId = uuid()

const res = await query(sql`
INSERT INTO patches (id, name, authorId, createdAt, patch)
VALUES (${patchId}, ${patchName}, ${userId}, ${Date.now()}, ${JSON.stringify(
patch
)})
VALUES (${patchId}, ${
patch.metadata.name
}, ${userId}, ${Date.now()}, ${JSON.stringify(patch)})
`)

return { id: patchId, version: 0 }
}

export const savePatchVersion = async (
userId: string,
patchId: string,
patch: Patch
) => {
export const savePatchVersion = async (userId: string, patch: Patch) => {
const [latestVersion] = await query(sql`
SELECT version FROM patches WHERE id = ${patchId} ORDER BY version DESC LIMIT 1
SELECT version FROM patches WHERE id = ${patch.metadata.id} ORDER BY version DESC LIMIT 1
`)

const nextVersion = latestVersion.version + 1

await query(sql`
INSERT INTO patches (id, version, name, authorId, createdAt, patch)
VALUES (
${patchId},
${patch.metadata.id},
${nextVersion},
'untitled',
${patch.metadata.name},
${userId},
${Date.now()},
${JSON.stringify(patch)}
)
`)
return { id: patchId, version: nextVersion }
return { id: patch.metadata.id, version: nextVersion }
}
34 changes: 24 additions & 10 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ const unauthorized = (res: Response) => {
res.end()
}

const badRequest = (res: Response) => {
res.status(400)
res.json({ error: 'bad request' })
res.end()
}

const server = http.createServer(
router()
.get('/*', serverStatic('./dist/client/index.html'))
Expand Down Expand Up @@ -88,26 +94,34 @@ const server = http.createServer(
res.json(patch)
res.end()
})
.post('/api/patch/:id', validators.Patch, async (req, res) => {
.post('/api/patch', validators.Patch, async (req, res) => {
const { authorization } = req
if (!authorization) {
unauthorized(res)
return
}

res.json(
await db.savePatchVersion(authorization.id, req.parameters.id, req.body)
)
res.end()
})
.post('/api/patch', validators.Patch, async (req, res) => {
const { authorization } = req
if (!authorization) {
const patch = req.body

if (!patch.metadata.id) {
res.json(await db.saveNewPatch(authorization.id, patch))
res.end()
return
}

const latestVersion = await db.getLatestPatchVersion(patch.metadata.id)

if (!latestVersion) {
badRequest(res)
return
}

if (latestVersion.metadata.author?.id !== authorization.id) {
unauthorized(res)
return
}

res.json(await db.saveNewPatch(authorization.id, 'untitled', req.body))
res.json(await db.savePatchVersion(authorization.id, patch))
res.end()
})
)
Expand Down

0 comments on commit 7555db4

Please sign in to comment.