summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2021-03-16 18:19:26 +0100
committerJules Laplace <julescarbon@gmail.com>2021-03-16 18:19:26 +0100
commit15d9d864b539e221c6494b3535abef724517f207 (patch)
tree1867734ad4740625848c69cd05f5497c21282b0c
parent901beb4df2c074ba54fedc91dd6e780cebe093d1 (diff)
uploading audio files and displaying them in a list
-rw-r--r--cli/app/sql/models/upload.py4
-rw-r--r--frontend/app/types.js1
-rw-r--r--frontend/app/views/graph/components/audio.list.js150
-rw-r--r--frontend/app/views/graph/components/graph.header.js1
-rw-r--r--frontend/app/views/graph/graph.actions.js4
-rw-r--r--frontend/app/views/graph/graph.container.js2
-rw-r--r--frontend/app/views/graph/graph.css53
-rw-r--r--frontend/app/views/graph/graph.reducer.js31
-rwxr-xr-xstatic/img/icons_pause_white.svg6
-rwxr-xr-xstatic/img/icons_play_white.svg6
10 files changed, 256 insertions, 2 deletions
diff --git a/cli/app/sql/models/upload.py b/cli/app/sql/models/upload.py
index 30e53dc..d9307ff 100644
--- a/cli/app/sql/models/upload.py
+++ b/cli/app/sql/models/upload.py
@@ -37,5 +37,5 @@ class Upload(Base):
def url(self):
if self.tag:
- return join('/static/data_store/uploads', str(self.graph_id), self.tag, self.fn)
- return join('/static/data_store/uploads', str(self.graph_id), self.fn)
+ return join('/static/uploads', str(self.graph_id), self.tag, self.fn)
+ return join('/static/uploads', str(self.graph_id), self.fn)
diff --git a/frontend/app/types.js b/frontend/app/types.js
index 7120a91..19d1e69 100644
--- a/frontend/app/types.js
+++ b/frontend/app/types.js
@@ -6,6 +6,7 @@ export const graph = crud_type('graph', [
'show_add_page_form', 'hide_add_page_form', 'toggle_add_page_form',
'show_edit_page_form', 'hide_edit_page_form', 'toggle_edit_page_form',
'update_graph_page',
+ 'toggle_audio_list',
])
export const page = crud_type('page', [
diff --git a/frontend/app/views/graph/components/audio.list.js b/frontend/app/views/graph/components/audio.list.js
new file mode 100644
index 0000000..bd8fe16
--- /dev/null
+++ b/frontend/app/views/graph/components/audio.list.js
@@ -0,0 +1,150 @@
+import React, { Component } from 'react'
+import { Link } from 'react-router-dom'
+import { connect } from 'react-redux'
+
+import { history } from 'app/store'
+import actions from 'app/actions'
+
+class AudioList extends Component {
+ state = {
+ playing: false,
+ play_id: -1,
+ }
+
+ constructor(props) {
+ super(props)
+ this.toggleAudio = this.toggleAudio.bind(this)
+ this.upload = this.upload.bind(this)
+ this.audioDidEnd = this.audioDidEnd.bind(this)
+ }
+
+ componentDidMount() {
+ this.audioElement = document.createElement('audio')
+ this.audioElement.addEventListener('ended', this.audioDidEnd)
+ }
+
+ componentWillUnmount() {
+ this.audioElement.removeEventListener('ended', this.audioDidEnd)
+ this.audioElement.pause()
+ this.audioElement = null
+ }
+
+ audioDidEnd() {
+ this.setState({ playing: false })
+ }
+
+ upload(e) {
+ e.preventDefault()
+ document.body.className = ''
+ const files = e.dataTransfer ? e.dataTransfer.files : e.target.files
+ let i
+ if (!files.length) return
+ Array.from(files).forEach(file => this.uploadTaggedFile(file, 'audio', file.filename))
+ }
+
+ uploadTaggedFile(file, tag, fn) {
+ return new Promise((resolve, reject) => {
+ this.setState({ status: "Uploading " + tag + "..." })
+ const uploadData = {
+ tag,
+ file,
+ __file_filename: fn,
+ graph_id: this.props.graph.id,
+ username: 'swimmer',
+ }
+ // console.log(uploadData)
+ return actions.upload.upload(uploadData).then(data => {
+ // console.log(data)
+ resolve({
+ ...data.res,
+ })
+ })
+ })
+ }
+
+ destroyFile(upload) {
+ return new Promise((resolve, reject) => {
+ actions.upload.destroy(upload)
+ .then(() => {
+ console.log('Destroy successful')
+ resolve()
+ })
+ .catch(() => {
+ console.log('Error deleting the file')
+ reject()
+ })
+ })
+ }
+
+ toggleAudio(upload) {
+ console.log(upload)
+ let playing = false
+ if (this.state.play_id === upload.id && this.state.playing) {
+ this.audioElement.pause()
+ } else {
+ this.audioElement.src = upload.url
+ this.audioElement.currentTime = 0
+ this.audioElement.play()
+ playing = true
+ }
+ this.setState({
+ playing,
+ play_id: upload.id,
+ })
+ }
+
+ render() {
+ const { playing, play_id } = this.state
+ const { graph } = this.props
+ // console.log(graph.uploads)
+ console.log(playing, play_id)
+ return (
+ <div className='box audioList'>
+ <div className="uploadButton">
+ <button>
+ <span>
+ {"Upload an audio file"}
+ </span>
+ </button>
+ <input
+ type="file"
+ accept="audio/mp3"
+ onChange={this.upload}
+ required={this.props.required}
+ />
+ </div>
+ {graph.uploads.map(upload => (
+ <div className='audioItem' key={upload.id} onClick={() => this.toggleAudio(upload)} >
+ <img
+ className='playButton'
+ src={
+ (playing && play_id === upload.id)
+ ? "/static/img/icons_pause_white.svg"
+ : "/static/img/icons_play_white.svg"
+ }
+ />
+ <div className='title'>
+ <div>{unslugify(upload.fn)}</div>
+ </div>
+ </div>
+ ))}
+ </div>
+ )
+ }
+}
+
+const unslugify = fn => fn.replace(/-/g, ' ').replace(/_/g, ' ').replace('.mp3', '')
+
+const mapStateToProps = state => ({
+ graph: state.graph.show.res,
+})
+
+const mapDispatchToProps = dispatch => ({
+})
+
+export default connect(mapStateToProps, mapDispatchToProps)(AudioList)
+
+
+/*
+ - upload new audio file
+ */ \ No newline at end of file
diff --git a/frontend/app/views/graph/components/graph.header.js b/frontend/app/views/graph/components/graph.header.js
index b969400..0766580 100644
--- a/frontend/app/views/graph/components/graph.header.js
+++ b/frontend/app/views/graph/components/graph.header.js
@@ -14,6 +14,7 @@ function GraphHeader(props) {
</div>
<div>
<button onClick={() => props.graphActions.toggleAddPageForm()}>+ Add page</button>
+ <button onClick={() => props.graphActions.toggleAudioList()}>+ Audio</button>
</div>
</header>
)
diff --git a/frontend/app/views/graph/graph.actions.js b/frontend/app/views/graph/graph.actions.js
index a24ccc2..eba3f92 100644
--- a/frontend/app/views/graph/graph.actions.js
+++ b/frontend/app/views/graph/graph.actions.js
@@ -25,6 +25,10 @@ export const toggleEditPageForm = () => dispatch => {
dispatch({ type: types.graph.toggle_edit_page_form })
}
+export const toggleAudioList = () => dispatch => {
+ dispatch({ type: types.graph.toggle_audio_list })
+}
+
export const updateGraphPage = page => dispatch => {
dispatch({ type: types.graph.update_graph_page, page })
}
diff --git a/frontend/app/views/graph/graph.container.js b/frontend/app/views/graph/graph.container.js
index 9e354fc..34c3d9d 100644
--- a/frontend/app/views/graph/graph.container.js
+++ b/frontend/app/views/graph/graph.container.js
@@ -15,6 +15,7 @@ import PageEdit from './components/page.edit'
import GraphHeader from './components/graph.header'
import GraphEditor from './components/graph.editor'
+import AudioList from './components/audio.list'
class GraphContainer extends Component {
componentDidMount() {
@@ -63,6 +64,7 @@ class GraphContainer extends Component {
<div className='sidebar'>
{this.props.graph.editor.addingPage && <PageNew />}
{this.props.graph.editor.editingPage && <PageEdit />}
+ {this.props.graph.editor.showingAudio && <AudioList />}
</div>
</div>
</div>
diff --git a/frontend/app/views/graph/graph.css b/frontend/app/views/graph/graph.css
index 2805cb0..c6ef115 100644
--- a/frontend/app/views/graph/graph.css
+++ b/frontend/app/views/graph/graph.css
@@ -146,6 +146,59 @@
width: 5.5rem;
}
+/* Upload area */
+
+.box .uploadButton {
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 0.5rem;
+ margin-bottom: 0.5rem;
+}
+.uploadButton input[type=file] {
+ position: absolute;
+ top: 0; left: 0;
+ width: 100%; height: 100%;
+}
+.audioList .audioItem {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ cursor: pointer;
+ padding: 0.125rem 0;
+}
+.audioList .playButton {
+ background: transparent;
+ border: 0;
+ width: 1.5rem;
+ height: 1.5rem;
+ margin-right: 0.5rem;
+ opacity: 0.8;
+}
+.audioList .title {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ overflow: hidden;
+ flex: 1;
+}
+.audioList .title div {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: pre;
+ width: 100%;
+}
+.audioList .audioItem:hover {
+ background: rgba(255,255,255,0.2);
+}
+.audioList .audioItem:hover .title {
+ color: #fff;
+}
+.audioList .audioItem:hover .playButton {
+ opacity: 1.0;
+}
+
/* Graph handles */
.handle {
diff --git a/frontend/app/views/graph/graph.reducer.js b/frontend/app/views/graph/graph.reducer.js
index 6be5089..30049b5 100644
--- a/frontend/app/views/graph/graph.reducer.js
+++ b/frontend/app/views/graph/graph.reducer.js
@@ -7,6 +7,7 @@ const initialState = crudState('graph', {
editor: {
addingPage: false,
editingPage: false,
+ showingAudio: false,
},
options: {
}
@@ -36,6 +37,19 @@ export default function graphReducer(state = initialState, action) {
}
}
+ case types.upload.upload_complete:
+ console.log(action)
+ return {
+ ...state,
+ show: {
+ ...state.show,
+ res: {
+ ...state.show.res,
+ uploads: state.show.res.uploads.concat(action.data.res)
+ }
+ }
+ }
+
case types.graph.show_add_page_form:
return {
...state,
@@ -43,6 +57,7 @@ export default function graphReducer(state = initialState, action) {
...state.editor,
addingPage: true,
editingPage: false,
+ showingAudio: false,
}
}
@@ -52,6 +67,7 @@ export default function graphReducer(state = initialState, action) {
editor: {
...state.editor,
addingPage: false,
+ showingAudio: false,
}
}
@@ -62,6 +78,7 @@ export default function graphReducer(state = initialState, action) {
...state.editor,
addingPage: !state.editor.addingPage,
editingPage: false,
+ showingAudio: false,
}
}
@@ -72,6 +89,7 @@ export default function graphReducer(state = initialState, action) {
...state.editor,
addingPage: false,
editingPage: true,
+ showingAudio: false,
}
}
@@ -81,6 +99,7 @@ export default function graphReducer(state = initialState, action) {
editor: {
...state.editor,
editingPage: false,
+ showingAudio: false,
}
}
@@ -91,6 +110,18 @@ export default function graphReducer(state = initialState, action) {
...state.editor,
addingPage: false,
editingPage: !state.editor.editingPage,
+ showingAudio: false,
+ }
+ }
+
+ case types.graph.toggle_audio_list:
+ return {
+ ...state,
+ editor: {
+ ...state.editor,
+ addingPage: false,
+ editingPage: false,
+ showingAudio: !state.editor.showingAudio,
}
}
diff --git a/static/img/icons_pause_white.svg b/static/img/icons_pause_white.svg
new file mode 100755
index 0000000..59c7e60
--- /dev/null
+++ b/static/img/icons_pause_white.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
+<path fill='#ffffff' d="M14.34,12.5h4.16v15h-4.16V12.5z M21.5,27.5h4.17v-15H21.5V27.5z"/>
+</svg>
diff --git a/static/img/icons_play_white.svg b/static/img/icons_play_white.svg
new file mode 100755
index 0000000..78ff002
--- /dev/null
+++ b/static/img/icons_play_white.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
+<polygon fill='#ffffff' points="12.5,11 12.5,29 27.5,20 "/>
+</svg>