Removed event bus and switched to vuex. Implemented vue dropzone.
This commit is contained in:
@@ -1,42 +0,0 @@
|
|||||||
// filedropzone_controller.js
|
|
||||||
|
|
||||||
import { Controller } from '@hotwired/stimulus';
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export default class extends Controller {
|
|
||||||
connect() {
|
|
||||||
this.element.addEventListener('dropzone:connect', this._onConnect);
|
|
||||||
this.element.addEventListener('dropzone:change', this._onChange);
|
|
||||||
this.element.addEventListener('dropzone:clear', this._onClear);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
// You should always remove listeners when the controller is disconnected to avoid side-effects
|
|
||||||
this.element.removeEventListener('dropzone:connect', this._onConnect);
|
|
||||||
this.element.removeEventListener('dropzone:change', this._onChange);
|
|
||||||
this.element.removeEventListener('dropzone:clear', this._onClear);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onConnect(event) {
|
|
||||||
// The dropzone was just created
|
|
||||||
console.info('onconnect');
|
|
||||||
}
|
|
||||||
|
|
||||||
_onChange(event) {
|
|
||||||
// The dropzone just changed
|
|
||||||
console.info('onchange');
|
|
||||||
axios.post(document.URL, new FormData(event.target.closest('form')), {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
event.target.querySelector('.dropzone-preview-button').click();
|
|
||||||
window.EventBus.$emit('fileUploaded');
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_onClear(event) {
|
|
||||||
// The dropzone has just been cleared
|
|
||||||
console.info('onclear');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,8 +8,6 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
bookUpdateProgress(bookId, progress) {
|
bookUpdateProgress(bookId, progress) {
|
||||||
console.info(bookId);
|
|
||||||
console.info(progress);
|
|
||||||
return axios.post('/progress/update', {
|
return axios.post('/progress/update', {
|
||||||
bookId: bookId,
|
bookId: bookId,
|
||||||
progress: progress
|
progress: progress
|
||||||
|
|||||||
20
assets/js/api/file.js
Normal file
20
assets/js/api/file.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
deleteFile: function (fileId) {
|
||||||
|
return axios.get(window.location.origin + '/file/delete/' + fileId);
|
||||||
|
},
|
||||||
|
getFiles: function (bookId) {
|
||||||
|
return axios.get(this.getFilesEndpoint(bookId), {
|
||||||
|
headers: {
|
||||||
|
'accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getFilesEndpoint: function (bookId) {
|
||||||
|
if (bookId) {
|
||||||
|
return `/api/books/${bookId}/files`;
|
||||||
|
}
|
||||||
|
return `/api/files`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
window.EventBus = new Vue();
|
|
||||||
export const EventBus = window.EventBus;
|
|
||||||
@@ -25,54 +25,63 @@
|
|||||||
<td class="text-end">{{ file.file_size_human }}</td>
|
<td class="text-end">{{ file.file_size_human }}</td>
|
||||||
<td>{{ file.extension }}</td>
|
<td>{{ file.extension }}</td>
|
||||||
<td><a :href="'/file/'+ file.id" class="link-secondary">download</a></td>
|
<td><a :href="'/file/'+ file.id" class="link-secondary">download</a></td>
|
||||||
<td><a :href="'/file/delete/'+file.id" @click.prevent="deleteFile(file.id)" class="link-danger">remove</a>
|
<td><a @click.prevent="deleteFile(file.id)" class="link-danger cursor-pointer">remove</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<vue-dropzone v-if="bookId"
|
||||||
|
ref="myVueDropzone"
|
||||||
|
id="dropzone"
|
||||||
|
:options="dropzoneOptions"
|
||||||
|
:useCustomSlot="true"
|
||||||
|
@vdropzone-success="vdropzoneSuccess"
|
||||||
|
>
|
||||||
|
<div class="dropzone-custom-content">
|
||||||
|
<h3 class="dropzone-custom-title">Drag and drop to upload content!</h3>
|
||||||
|
<div class="subtitle">
|
||||||
|
...or click to select a file from your computer
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</vue-dropzone>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import vue2Dropzone from "vue2-dropzone";
|
||||||
import {EventBus} from "../event-bus";
|
import {mapActions, mapState} from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Files',
|
name: 'Files',
|
||||||
components: {
|
components: {
|
||||||
EventBus
|
vueDropzone: vue2Dropzone
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
files: []
|
dropzoneOptions: {
|
||||||
|
capture: 'image/',
|
||||||
|
url: '/book/' + this.bookId,
|
||||||
|
thumbnailWidth: 150,
|
||||||
|
maxFilesize: 50.5,
|
||||||
|
acceptedFiles: '.pdf, .epub, .mobi'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
bookId: {type: Number, default: null}
|
bookId: {type: Number, default: null}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.getFiles();
|
this.getFiles(this.bookId);
|
||||||
},
|
},
|
||||||
created() {
|
computed: {
|
||||||
window.EventBus.$on('fileUploaded', this.getFiles);
|
...mapState('filemodule', ['files'])
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deleteFile: function (fileId) {
|
...mapActions('filemodule', ['getFiles', 'deleteFile']),
|
||||||
axios.get(window.location.origin + '/file/delete/' + fileId).then(() => this.getFiles())
|
vdropzoneSuccess(file) {
|
||||||
},
|
this.$refs.myVueDropzone.removeFile(file);
|
||||||
getFiles: function () {
|
this.getFiles(this.bookId);
|
||||||
axios.get(this.getFilesEndpoint(), {
|
|
||||||
headers: {
|
|
||||||
'accept': 'application/json'
|
|
||||||
}
|
|
||||||
}).then(response => this.files = response.data)
|
|
||||||
},
|
|
||||||
getFilesEndpoint: function () {
|
|
||||||
if (this.bookId) {
|
|
||||||
return `/api/books/${this.bookId}/files`;
|
|
||||||
}
|
|
||||||
return `/api/files`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {EventBus} from "../event-bus";
|
|
||||||
import {mapActions, mapGetters} from "vuex";
|
import {mapActions, mapGetters} from "vuex";
|
||||||
import {TURN_ON_EDIT_MODE, TURN_OFF_EDIT_MODE} from '../store/mutation-types'
|
import {TURN_ON_EDIT_MODE, TURN_OFF_EDIT_MODE} from '../store/mutation-types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Progresseditor',
|
name: 'Progresseditor',
|
||||||
components: {
|
|
||||||
EventBus
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
totalPages: Number,
|
totalPages: Number,
|
||||||
readPages: Number,
|
readPages: Number,
|
||||||
|
|||||||
@@ -2,13 +2,15 @@ import Vue from 'vue'
|
|||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import booksmodule from './modules/books';
|
import booksmodule from './modules/books';
|
||||||
import bookprogressmodule from './modules/bookprogress';
|
import bookprogressmodule from './modules/bookprogress';
|
||||||
|
import filemodule from './modules/filemodule';
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
modules:{
|
modules:{
|
||||||
booksmodule,
|
booksmodule,
|
||||||
bookprogressmodule
|
bookprogressmodule,
|
||||||
|
filemodule
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
68
assets/js/store/modules/filemodule.js
Normal file
68
assets/js/store/modules/filemodule.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import FileApi from "../../api/file";
|
||||||
|
import {
|
||||||
|
REMOVE_FILE_FROM_LIST,
|
||||||
|
FETCHING_FILES,
|
||||||
|
FETCHING_FILES_SUCCESS,
|
||||||
|
FETCHING_FILES_ERROR
|
||||||
|
} from '../mutation-types.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: {
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
files: []
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
isLoading(state) {
|
||||||
|
return state.isLoading;
|
||||||
|
},
|
||||||
|
hasError(state) {
|
||||||
|
return state.error !== null;
|
||||||
|
},
|
||||||
|
error(state) {
|
||||||
|
return state.error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
[FETCHING_FILES](state) {
|
||||||
|
state.isLoading = true;
|
||||||
|
state.error = null;
|
||||||
|
},
|
||||||
|
[FETCHING_FILES_SUCCESS](state, files) {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.error = null;
|
||||||
|
state.files = files;
|
||||||
|
},
|
||||||
|
[FETCHING_FILES_ERROR](state, error) {
|
||||||
|
state.isLoading = false;
|
||||||
|
state.error = error;
|
||||||
|
state.files = [];
|
||||||
|
},
|
||||||
|
[REMOVE_FILE_FROM_LIST](state, fileId) {
|
||||||
|
|
||||||
|
const index = state.files.findIndex(file => file.id === fileId);
|
||||||
|
state.files.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async deleteFile({commit}, fileId) {
|
||||||
|
try {
|
||||||
|
await FileApi.deleteFile(fileId);
|
||||||
|
commit(REMOVE_FILE_FROM_LIST, fileId)
|
||||||
|
} catch (error) {
|
||||||
|
commit(FETCHING_FILES_ERROR, error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getFiles({commit}, bookId) {
|
||||||
|
commit(FETCHING_FILES);
|
||||||
|
try {
|
||||||
|
let response = await FileApi.getFiles(bookId);
|
||||||
|
commit(FETCHING_FILES_SUCCESS, response.data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
commit(FETCHING_FILES_ERROR, error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -3,5 +3,10 @@ export const
|
|||||||
UPDATING_PROGRESS_SUCCESS = "UPDATING_PROGRESS_SUCCESS",
|
UPDATING_PROGRESS_SUCCESS = "UPDATING_PROGRESS_SUCCESS",
|
||||||
UPDATING_PROGRESS_ERROR = "UPDATING_PROGRESS_ERROR",
|
UPDATING_PROGRESS_ERROR = "UPDATING_PROGRESS_ERROR",
|
||||||
TURN_ON_EDIT_MODE = "TURN_ON_EDIT_MODE",
|
TURN_ON_EDIT_MODE = "TURN_ON_EDIT_MODE",
|
||||||
TURN_OFF_EDIT_MODE = "TURN_OFF_EDIT_MODE"
|
TURN_OFF_EDIT_MODE = "TURN_OFF_EDIT_MODE",
|
||||||
|
|
||||||
|
FETCHING_FILES = "FETCHING_FILES",
|
||||||
|
FETCHING_FILES_SUCCESS = "FETCHING_FILES_SUCCESS",
|
||||||
|
FETCHING_FILES_ERROR = "FETCHING_FILES_ERROR",
|
||||||
|
REMOVE_FILE_FROM_LIST = "REMOVE_FILE_FROM_LIST"
|
||||||
;
|
;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@import "~bootstrap";
|
@import "~bootstrap";
|
||||||
|
@import "vue2-dropzone/dist/vue2Dropzone.min.css";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
//background-color: lightblue !important;
|
//background-color: lightblue !important;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"@popperjs/core": "^2.11.5",
|
"@popperjs/core": "^2.11.5",
|
||||||
"bootstrap": "^5.1.3",
|
"bootstrap": "^5.1.3",
|
||||||
"popper": "^1.0.1",
|
"popper": "^1.0.1",
|
||||||
|
"vue2-dropzone": "^3.6.0",
|
||||||
"vuex": "^3"
|
"vuex": "^3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,16 +103,14 @@ class BookController extends AbstractController
|
|||||||
): Response {
|
): Response {
|
||||||
$fileForm = $this->createForm(FileType::class);
|
$fileForm = $this->createForm(FileType::class);
|
||||||
$fileForm->handleRequest($request);
|
$fileForm->handleRequest($request);
|
||||||
|
if ($fileForm->isSubmitted()) {
|
||||||
if ($fileForm->isSubmitted() && $fileForm->isValid()) {
|
/** @var UploadedFile $ebook */
|
||||||
/** @var UploadedFile[] $ebooks */
|
$ebook = $request->files->get('file');
|
||||||
$ebook = $request->files->get('file')['file'];
|
|
||||||
$fileService->saveFile($ebook, $book);
|
$fileService->saveFile($ebook, $book);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->renderForm('book/show.html.twig', [
|
return $this->render('book/show.html.twig', [
|
||||||
'book' => $book,
|
'book' => $book,
|
||||||
'file_form' => $fileForm
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,9 +53,8 @@ class BookType extends AbstractType
|
|||||||
'required' => false,
|
'required' => false,
|
||||||
'attr' => ['accept' => ".pdf, .epub, .mobi"]
|
'attr' => ['accept' => ".pdf, .epub, .mobi"]
|
||||||
])
|
])
|
||||||
// ->add('cover', FileType::class, ['mapped' => false, 'data_class' => null, 'required' => false])
|
|
||||||
->add('cover_url', TextType::class, ['mapped' => false, 'help' => 'Fill in the field with a link to a cover image to use it as a cover for the book.', 'label'=> 'Cover url', 'required' => false])
|
->add('cover_url', TextType::class, ['mapped' => false, 'help' => 'Fill in the field with a link to a cover image to use it as a cover for the book.', 'label'=> 'Cover url', 'required' => false])
|
||||||
->add('cover', DropzoneType::class, ['mapped' => false, 'data_class' => null, 'required' => false, 'attr' => ['accept' => "image/*", 'placeholder' => 'Drag and drop or browse']])
|
->add('cover', FileType::class, ['mapped' => false, 'data_class' => null, 'required' => false, 'attr' => ['accept' => "image/*"]])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ class FileType extends AbstractType
|
|||||||
'data_class' => null,
|
'data_class' => null,
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'data-controller' => 'filedropzone',
|
|
||||||
'accept' => ".pdf, .epub, .mobi",
|
'accept' => ".pdf, .epub, .mobi",
|
||||||
'placeholder' => 'Drag and drop or browse'
|
'placeholder' => 'Drag and drop or browse'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -70,9 +70,9 @@
|
|||||||
<files :book-id="{{ book.id }}"></files>
|
<files :book-id="{{ book.id }}"></files>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ form_start(file_form) }}
|
{# {{ form_start(file_form) }}#}
|
||||||
{{ form_widget(file_form) }}
|
{# {{ form_widget(file_form) }}#}
|
||||||
{{ form_end(file_form) }}
|
{# {{ form_end(file_form) }}#}
|
||||||
|
|
||||||
<a href="{{ path('app_book_index') }}">back to list</a>
|
<a href="{{ path('app_book_index') }}">back to list</a>
|
||||||
|
|
||||||
|
|||||||
12
yarn.lock
12
yarn.lock
@@ -3188,6 +3188,11 @@ domutils@^2.5.2, domutils@^2.8.0:
|
|||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
domhandler "^4.2.0"
|
domhandler "^4.2.0"
|
||||||
|
|
||||||
|
dropzone@^5.5.1:
|
||||||
|
version "5.9.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-5.9.3.tgz#b3070ae090fa48cbc04c17535635537ca72d70d6"
|
||||||
|
integrity sha512-Azk8kD/2/nJIuVPK+zQ9sjKMRIpRvNyqn9XwbBHNq+iNuSccbJS6hwm1Woy0pMST0erSo0u4j+KJaodndDk4vA==
|
||||||
|
|
||||||
duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
|
duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
|
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
|
||||||
@@ -7426,6 +7431,13 @@ vue-template-es2015-compiler@^1.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
|
||||||
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
|
||||||
|
|
||||||
|
vue2-dropzone@^3.6.0:
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue2-dropzone/-/vue2-dropzone-3.6.0.tgz#b4bb4b64de1cbbb3b88f04b24878e06780a51546"
|
||||||
|
integrity sha512-YXC1nCWIZvfa98e/i6h+EshZCkFSxFEh0Sxr9ODfThAPPDVhAzLLlz/4XIx0NGO1QeSy6htwSstte47R7vVhLQ==
|
||||||
|
dependencies:
|
||||||
|
dropzone "^5.5.1"
|
||||||
|
|
||||||
vue@^2.5:
|
vue@^2.5:
|
||||||
version "2.6.14"
|
version "2.6.14"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
|
||||||
|
|||||||
Reference in New Issue
Block a user