-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0874c06
commit d9382fe
Showing
7 changed files
with
287 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import good | ||
from datetime import datetime, time | ||
from ..base import Api_handler, Level | ||
|
||
_schema = { | ||
'message': good.All(str, good.Length(min=1, max=400)), | ||
'date': good.Date('%Y-%m-%d'), | ||
} | ||
|
||
class Collection_handler(Api_handler): | ||
|
||
__schema__ = good.Schema(_schema, default_keys=good.Required) | ||
|
||
@Level(1) | ||
async def get(self, channel_id): | ||
cmds = await self.db.fetchall(''' | ||
SELECT * | ||
FROM twitch_quotes | ||
WHERE channel_id=%s AND enabled=1 | ||
ORDER BY number DESC | ||
''', (channel_id,)) | ||
self.write_object(cmds) | ||
|
||
@Level(1) | ||
async def post(self, channel_id): | ||
data = self.validate() | ||
data['channel_id'] = channel_id | ||
data['created_at'] = datetime.combine(data['date'], time(0, 0, 0)) | ||
data['updated_at'] = datetime.utcnow() | ||
raise Exception('Not implemented') | ||
# TODO: get user name | ||
#date['created_by'] = self.current_user. | ||
fields = ','.join([f for f in data]) | ||
vfields = ','.join(['%s' for _ in data]) | ||
values = list(data.values()) | ||
c = await self.db.execute( | ||
'INSERT INTO twitch_quotes ({}) VALUES ({})'.format(fields, vfields), | ||
values | ||
) | ||
cmd = await get_quote(self, channel_id, c.lastrowid) | ||
self.set_status(201) | ||
self.write_object(cmd) | ||
|
||
class Handler(Api_handler): | ||
|
||
__schema__ = good.Schema(_schema, default_keys=good.Optional) | ||
|
||
@Level(1) | ||
async def get(self, channel_id, number): | ||
cmd = await get_quote(self, channel_id, number) | ||
if not cmd: | ||
self.set_status(404) | ||
self.write_object({'error': 'Unknown quote'}) | ||
else: | ||
self.write_object(cmd) | ||
|
||
@Level(1) | ||
async def put(self, channel_id, number): | ||
data = self.validate() | ||
data['updated_at'] = datetime.utcnow() | ||
if 'date' in data: | ||
if data['date']: | ||
data['created_at'] = datetime.combine(data['date'], time(0, 0, 0)) | ||
del data['date'] | ||
fields = ','.join(['{}=%s'.format(k) for k in data]) | ||
values = list(data.values()) | ||
values.append(channel_id) | ||
values.append(number) | ||
await self.db.execute( | ||
'UPDATE twitch_quotes SET {} WHERE channel_id=%s AND number=%s'.format(fields), | ||
values | ||
) | ||
cmd = await get_quote(self, channel_id, number) | ||
self.write_object(cmd) | ||
|
||
@Level(1) | ||
async def delete(self, channel_id, number): | ||
await self.db.execute(''' | ||
UPDATE twitch_quotes SET enabled=0 WHERE channel_id=%s and number=%s | ||
''', (channel_id, number,)) | ||
self.set_status(204) | ||
|
||
async def get_quote(self, channel_id, number): | ||
cmd = await self.db.fetchone(''' | ||
SELECT * | ||
FROM twitch_quotes | ||
WHERE channel_id=%s and number=%s | ||
''', (channel_id, number,)) | ||
return cmd |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import React from 'react' | ||
import {Redirect} from 'react-router' | ||
import api from 'tbot/twitch/api' | ||
import Loading from 'tbot/components/loading' | ||
import {setHeader, renderError} from 'tbot/utils' | ||
import {userLevelName, enabledWhenName} from 'tbot/twitch/utils' | ||
|
||
class Quote extends React.Component { | ||
|
||
constructor(props) { | ||
super(props) | ||
this.state = { | ||
cmd: { | ||
message: '', | ||
date: '', | ||
}, | ||
loading: true, | ||
error: null, | ||
saving: false, | ||
success: false, | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
if (this.props.match.params.number) { | ||
setHeader('Edit quote') | ||
this.getQuote() | ||
} else { | ||
setHeader('New quote') | ||
this.setState({loading: false}) | ||
} | ||
} | ||
|
||
getQuote() { | ||
const number = this.props.match.params.number | ||
api.get(`/api/twitch/channels/${managedUser.id}/quotes/${number}`).then(r => { | ||
for (let key in r.data) { | ||
if (!(key in this.state.cmd)) | ||
delete r.data[key] | ||
} | ||
this.setState({ | ||
quote: r.data, | ||
loading: false | ||
}) | ||
setHeader(`Edit quote`) | ||
}) | ||
} | ||
|
||
handleEvent = (e) => { | ||
let val = e.target.type === 'checkbox' ? e.target.checked : e.target.value | ||
this.state.quote[e.target.name] = val | ||
this.setState({quote: this.state.quote}) | ||
} | ||
|
||
handleSubmit = (e) => { | ||
e.preventDefault() | ||
this.setState({saving: true, error: null}) | ||
const number = this.props.match.params.number | ||
if (number) | ||
api.put(`/api/twitch/channels/${managedUser.id}/quotes/${number}`, this.state.quote).then(r => { | ||
this.setState({success: true}) | ||
}).catch(e => { | ||
this.setState({error: e.response.data, saving: false}) | ||
}) | ||
else | ||
api.post(`/api/twitch/channels/${managedUser.id}/quotes`, this.state.quote).then(r => { | ||
this.setState({success: true}) | ||
}).catch(e => { | ||
this.setState({error: e.response.data, saving: false}) | ||
}) | ||
} | ||
|
||
handleDelete = () => { | ||
if (!confirm(`Delete quote?`)) | ||
return | ||
this.setState({deleting: true, error: null}) | ||
let number = this.props.match.params.number | ||
api.delete(`/api/twitch/channels/${managedUser.id}/quotes/${number}`).then(r => { | ||
this.setState({ | ||
cmd: r.data, | ||
success: true, | ||
}) | ||
}).catch(e => { | ||
this.setState({error: e.response.data, deleting: false}) | ||
}) | ||
} | ||
|
||
renderButton() { | ||
if (this.state.saving) | ||
return <button className="btn btn-primary" type="button" disabled> | ||
<span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Saving... | ||
</button> | ||
return <button type="submit" className="btn btn-primary">Save quote</button> | ||
} | ||
|
||
renderDeleteButton() { | ||
if (!this.props.match.params.number) | ||
return null | ||
if (this.state.deleting) | ||
return <button className="ml-2 btn btn-danger" type="button" disabled> | ||
<span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Deleting... | ||
</button> | ||
return <button type="button" onClick={this.handleDelete} className="ml-2 btn btn-danger">Delete quote</button> | ||
} | ||
|
||
render() { | ||
if (this.state.loading) | ||
return <Loading /> | ||
if (this.state.success) | ||
return <Redirect to={`/twitch/${this.props.match.params.channel}/quotes`} /> | ||
|
||
return <div style={{maxWidth:'700px'}}> | ||
<form onSubmit={this.handleSubmit}> | ||
|
||
<div className="form-group"> | ||
<input | ||
className="form-control" | ||
id="message" | ||
name="message" | ||
value={this.state.quote.message} | ||
onChange={this.handleEvent} | ||
required | ||
/> | ||
</div> | ||
|
||
{renderError(this.state.error)} | ||
{this.renderButton()} | ||
{this.renderDeleteButton()} | ||
</form> | ||
</div> | ||
} | ||
} | ||
|
||
export default Quote |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import React from 'react' | ||
import {Link} from 'react-router-dom' | ||
import api from 'tbot/twitch/api' | ||
import Loading from 'tbot/components/loading' | ||
import {setHeader} from 'tbot/utils' | ||
|
||
class Quotes extends React.Component { | ||
|
||
constructor(props) { | ||
super(props) | ||
this.state = { | ||
quotes: [], | ||
loading: true, | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
setHeader('Quotes') | ||
this.getQuotes() | ||
} | ||
|
||
getQuotes() { | ||
api.get(`/api/twitch/channels/${managedUser.id}/quotes`).then(r => { | ||
this.setState({ | ||
quotes: r.data, | ||
loading: false | ||
}) | ||
}) | ||
} | ||
|
||
render() { | ||
if (this.state.loading) | ||
return <Loading /> | ||
return <div> | ||
<table className="table table-dark table-hover"> | ||
<thead> | ||
<tr> | ||
<th width="5px">Number</th> | ||
<th>Quote</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{this.state.quotes.length>0?this.state.quotes.map(quote => | ||
<tr key={quote.number}> | ||
<td>{quote.number}</td> | ||
<td>{quote.message}</td> | ||
<td className="text-right"><Link to={`quotes/edit/${quote.id}`}>Edit</Link></td> | ||
</tr> | ||
): <tr><td colSpan="8" className="text-center">No quotes.</td></tr>} | ||
</tbody> | ||
</table> | ||
</div> | ||
} | ||
} | ||
|
||
export default Quotes |