Cleanup API calls

This commit is contained in:
Paul Bienkowski 2021-02-14 19:21:34 +01:00
parent 2e7cdc24f3
commit 281a7ac90c
4 changed files with 88 additions and 23 deletions

View file

@ -1,3 +1,5 @@
import {stringifyParams} from 'query'
class API {
setAuthorizationHeader(authorization) {
this.authorization = authorization
@ -12,7 +14,30 @@ class API {
},
})
return await response.json()
if (response.status === 200) {
return await response.json()
} else {
return null
}
}
async post(url, {body: body_, ...options}) {
const body = typeof body_ !== 'string' ? JSON.stringify(body_) : body_
return await this.fetch(url, {...options, body, method: 'post',
headers: {
...(options.headers || {}),
'Content-Type': 'application/json',
},
})
}
async get(url, {query, ...options} = {}) {
const queryString = query ? stringifyParams(query) : null
return await this.fetch(url + (queryString ? '?' + queryString : ''), {method: 'get', ...options})
}
async delete(url, options = {}) {
return await this.get(url, {...options, method: 'delete'})
}
}

View file

@ -3,7 +3,23 @@ import {Segment, Form, Button, Loader, Header, Comment} from 'semantic-ui-react'
import {FormattedDate} from 'components'
export default function TrackComments({comments, login, hideLoader}) {
function CommentForm({onSubmit}) {
const [body, setBody] = React.useState('')
const onSubmitComment = React.useCallback(() => {
onSubmit({body})
setBody('')
}, [onSubmit, body])
return (
<Form reply onSubmit={onSubmitComment}>
<Form.TextArea rows={4} value={body} onChange={(e) => setBody(e.target.value)} />
<Button content="Post comment" labelPosition="left" icon="edit" primary />
</Form>
)
}
export default function TrackComments({comments, onSubmit, onDelete, login, hideLoader}) {
return (
<Segment basic>
<Comment.Group>
@ -24,16 +40,19 @@ export default function TrackComments({comments, login, hideLoader}) {
</div>
</Comment.Metadata>
<Comment.Text>{comment.body}</Comment.Text>
{login?.username === comment.author.username && (
<Comment.Actions>
<Comment.Action onClick={(e) => {
onDelete(comment.id)
e.preventDefault()
}}>Delete</Comment.Action>
</Comment.Actions>
)}
</Comment.Content>
</Comment>
))}
{login && comments != null && (
<Form reply>
<Form.TextArea rows={4} />
<Button content="Post comment" labelPosition="left" icon="edit" primary />
</Form>
)}
{login && comments != null && <CommentForm onSubmit={onSubmit} />}
</Comment.Group>
</Segment>
)

View file

@ -2,8 +2,8 @@ import React from 'react'
import {connect} from 'react-redux'
import {Segment, Dimmer, Grid, Loader, Header} from 'semantic-ui-react'
import {useParams} from 'react-router-dom'
import {concat, combineLatest, of, from} from 'rxjs'
import {pluck, distinctUntilChanged, map, switchMap, startWith} from 'rxjs/operators'
import {concat, combineLatest, of, from, Subject} from 'rxjs'
import {pluck, distinctUntilChanged, map, switchMap, startWith, sample} from 'rxjs/operators'
import {useObservable} from 'rxjs-hooks'
import api from 'api'
@ -15,9 +15,17 @@ import TrackComments from './TrackComments'
import TrackDetails from './TrackDetails'
import TrackMap from './TrackMap'
function useTriggerSubject() {
const subject$ = React.useMemo(() => new Subject(), [])
const trigger = React.useCallback(() => subject$.next(null), [])
return [trigger, subject$]
}
const TrackPage = connect((state) => ({login: state.login}))(function TrackPage({login}) {
const {slug} = useParams()
const [reloadComments, reloadComments$] = useTriggerSubject()
const data: {
track: null | Track
trackData: null | TrackData
@ -26,21 +34,22 @@ const TrackPage = connect((state) => ({login: state.login}))(function TrackPage(
(_$, args$) => {
const slug$ = args$.pipe(pluck(0), distinctUntilChanged())
const track$ = slug$.pipe(
map((slug) => '/tracks/' + slug),
switchMap((url) => concat(of(null), from(api.fetch(url)))),
map((slug) => `/tracks/${slug}`),
switchMap((url) => concat(of(null), from(api.get(url)))),
pluck('track')
)
const trackData$ = slug$.pipe(
map((slug) => '/tracks/' + slug + '/data'),
switchMap((url) => concat(of(null), from(api.fetch(url)))),
map((slug) => `/tracks/${slug}/data`),
switchMap((url) => concat(of(null), from(api.get(url)))),
pluck('trackData'),
startWith(null) // show track infos before track data is loaded
)
const comments$ = slug$.pipe(
map((slug) => '/tracks/' + slug + '/comments'),
switchMap((url) => concat(of(null), from(api.fetch(url)))),
const comments$ = concat(of(null), reloadComments$).pipe(
switchMap(() => slug$),
map((slug) => `/tracks/${slug}/comments`),
switchMap(url => api.get(url)),
pluck('comments'),
startWith(null) // show track infos before comments are loaded
)
@ -53,6 +62,18 @@ const TrackPage = connect((state) => ({login: state.login}))(function TrackPage(
[slug]
)
const onSubmitComment = React.useCallback(async ({body}) => {
await api.post(`/tracks/${slug}/comments`, {
body: {comment: {body}}
})
reloadComments()
}, [])
const onDeleteComment = React.useCallback(async (id) => {
await api.delete(`/tracks/${slug}/comments/${id}`)
reloadComments()
}, [])
const isAuthor = login?.username === data?.track?.author?.username
const {track, trackData, comments} = data || {}
@ -85,7 +106,7 @@ const TrackPage = connect((state) => ({login: state.login}))(function TrackPage(
</Grid.Row>
</Grid>
<TrackComments {...{hideLoader: loading, comments, login}} />
<TrackComments {...{hideLoader: loading, comments, login}} onSubmit={onSubmitComment} onDelete={onDeleteComment} />
{/* <pre>{JSON.stringify(data, null, 2)}</pre> */}
</Page>

View file

@ -4,13 +4,13 @@ import {Item, Tab, Loader, Pagination, Icon} from 'semantic-ui-react'
import {useObservable} from 'rxjs-hooks'
import {Link, useHistory, useRouteMatch} from 'react-router-dom'
import {of, from, concat} from 'rxjs'
import {map, switchMap, distinctUntilChanged, debounceTime} from 'rxjs/operators'
import {map, switchMap, distinctUntilChanged} from 'rxjs/operators'
import _ from 'lodash'
import type {Track} from '../types'
import {Page} from '../components'
import api from '../api'
import {useQueryParam, stringifyParams} from '../query'
import {useQueryParam} from '../query'
function TracksPageTabs() {
const history = useHistory()
@ -49,11 +49,11 @@ function TrackList({privateFeed}: {privateFeed: boolean}) {
inputs$.pipe(
map(([page, privateFeed]) => {
const url = '/tracks' + (privateFeed ? '/feed' : '')
const params = {limit: pageSize, offset: pageSize * (page - 1)}
return {url, params}
const query = {limit: pageSize, offset: pageSize * (page - 1)}
return {url, query}
}),
distinctUntilChanged(_.isEqual),
switchMap((request) => concat(of(null), from(api.fetch(request.url + '?' + stringifyParams(request.params)))))
switchMap((request) => concat(of(null), from(api.get(request.url, {query: request.query})))),
),
null,
[page, privateFeed]