Skip to content

Commit

Permalink
Merge pull request #1797 from c2corg/rotate-image
Browse files Browse the repository at this point in the history
Allow to rotate an image being uploaded
  • Loading branch information
brunobesson authored Apr 12, 2021
2 parents 288ea9e + 286a143 commit af7e0f6
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 22 deletions.
64 changes: 60 additions & 4 deletions src/components/images-uploader/ImageUploader.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
<template>
<div class="card">
<div class="card-image img-container" :style="dataUrl ? 'background-image: url(' + dataUrl + ')' : ''">
<delete-button class="delete-button" @click="$emit('delete-image')" />

<div class="action-buttons">
<delete-button @click="$emit('delete-image')" :title="$gettext('Delete image')" />
<rotate-button
@click="applyRotation(90)"
:disabled="rotateClicked || disabled"
:title="$gettext('Rotate image right')"
v-if="canRotate"
/>
<rotate-button
direction="left"
@click="applyRotation(-90)"
:disabled="rotateClicked || disabled"
:title="$gettext('Rotate image left')"
v-if="canRotate"
/>
</div>
<progress
v-if="isSaving || isFailed"
class="progress is-large"
Expand Down Expand Up @@ -64,9 +78,14 @@

<script>
// https://github.com/c2corg/v6_ui/blob/master/c2corg_ui/static/js/imageuploader.js
import RotateButton from './RotateButton';
import constants from '@/js/constants';
export default {
components: {
RotateButton,
},
props: {
categoriesEdition: {
type: Boolean,
Expand Down Expand Up @@ -104,6 +123,10 @@ export default {
type: Boolean,
required: true,
},
canRotate: {
type: Boolean,
default: false,
},
},
data() {
Expand All @@ -112,6 +135,8 @@ export default {
['collaborative', this.$gettext('collab')],
['personal', this.$gettext('personal')],
]),
rotate: 0,
rotateClicked: false,
};
if (this.$user.isModerator) {
Expand All @@ -125,6 +150,19 @@ export default {
imageCategories() {
return constants.objectDefinitions.image.fields.categories.values;
},
disabled() {
return this.isSaving || this.isFailed;
},
},
watch: {
isSaving: function () {
this.rotateClicked = false;
},
isFailed: function () {
this.rotateClicked = false;
},
},
methods: {
Expand All @@ -139,6 +177,18 @@ export default {
this.document.categories = newValue;
},
applyRotation(angle) {
if (this.rotateClicked || this.disabled) {
return;
}
this.rotateClicked = true;
this.rotate = (this.rotate + angle) % 360;
if (this.rotate < 0) {
this.rotate += 360;
}
this.$emit('rotate-image', this.rotate);
},
},
};
</script>
Expand Down Expand Up @@ -227,11 +277,17 @@ export default {
}
}
.delete-button {
.action-buttons {
position: absolute;
top: -1rem;
right: -1rem;
font-size: 3rem;
font-size: 1.5rem;
display: flex;
flex-direction: column;
> * {
margin-bottom: 5px;
}
}
.buttons-if-failed {
Expand Down
31 changes: 15 additions & 16 deletions src/components/images-uploader/ImagesUploader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
:is-saving="image.status === 'SAVING'"
:is-success="image.status === 'SUCCESS'"
:is-failed="image.status === 'FAILED'"
:can-rotate="image.file && image.file.name && !image.file.name.endsWith('.svg')"
@delete-image="onDeleteImage(image)"
@rotate-image="onRotateImage(image, $event)"
@retry-upload="startUpload(image)"
/>
</div>
Expand Down Expand Up @@ -126,12 +128,12 @@ export default {
},
watch: {
// this component svaes it states. It allow an user to close window
// this component saves its state. It allows an user to close window
// with unsaved images, and re-open it without loosing its images
//
// Here is the issue :
// Here is the issue:
// if the user starts an image load, then closes the window without saving
// and go to another page, the component won't be reloaded
// and goes to another page, the component won't be reloaded
// and loaded images could be accessible from the next document
// so, if $route changes, we must clean
$route: 'clean',
Expand Down Expand Up @@ -171,7 +173,7 @@ export default {
},
onDeleteImage(image) {
if (this.images[image.key] !== undefined) {
if (this.images[image.key]) {
this.$delete(this.images, image.key);
}
Expand Down Expand Up @@ -223,12 +225,13 @@ export default {
};
},
startUpload(image) {
startUpload(image, angle = 0) {
image.status = 'INITIAL';
image.percentCompleted = 0;
uploadFile(
image.file,
angle,
(dataUrl) => {
image.dataUrl = dataUrl;
},
Expand Down Expand Up @@ -264,18 +267,14 @@ export default {
image.errorMessage = event?.message ?? this.$gettext('Image could not be processed');
},
onRotateImage(image, angle) {
this.startUpload(image, angle);
this.computeReadyForSaving();
},
computeReadyForSaving() {
if (this.documents.length === 0) {
this.readyForSaving = false;
} else {
this.readyForSaving = true;
for (const document of this.documents) {
if (!document.filename) {
this.readyForSaving = false;
}
}
}
const images = Object.values(this.images);
this.readyForSaving = images.length && images.every((image) => image.status === 'SUCCESS');
},
save() {
Expand Down
44 changes: 44 additions & 0 deletions src/components/images-uploader/RotateButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<fa-layers class="fa-lg" @click="click" :class="[direction, { disabled: disabled }]">
<fa-icon icon="circle" />
<fa-icon :icon="direction == 'right' ? 'redo-alt' : 'undo-alt'" transform="shrink-6" :style="{ color: 'white' }" />
</fa-layers>
</template>

<script>
export default {
props: {
disabled: {
type: Boolean,
default: false,
},
direction: {
type: String,
default: 'right',
},
},
methods: {
click() {
if (this.disabled) {
return;
}
this.$emit('click');
},
},
};
</script>

<style scoped lang="scss">
@import '~bulma/sass/utilities/initial-variables.sass';
div {
cursor: pointer;
color: $primary;
&.disabled {
cursor: not-allowed;
color: $primary-light;
}
}
</style>
10 changes: 8 additions & 2 deletions src/js/upload-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,17 @@ const preProcess = async (file, document, orientation, onDataUrlReady) => {
}
};

const uploadFile = async (file, onDataUrlReady, onUploadProgress, onSuccess, onFailure) => {
const orientations = [1, 6, 3, 8];

// angle should be one of 0, 90, 180, 270 (in degrees)
const uploadFile = async (file, angle, onDataUrlReady, onUploadProgress, onSuccess, onFailure) => {
try {
const document = {};
const metaData = await loadImage.parseMetaData(file);
const orientation = await parseMetaData(document, metaData);
let orientation = await parseMetaData(document, metaData);
if (angle) {
orientation = orientations[(orientations.indexOf(orientation) + angle / 90) % 4];
}
const data = await preProcess(file, document, orientation, onDataUrlReady);
// do the upload
worker.push(
Expand Down
4 changes: 4 additions & 0 deletions src/js/vue-plugins/font-awesome-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import { faPen } from '@fortawesome/free-solid-svg-icons/faPen';
import { faPlus } from '@fortawesome/free-solid-svg-icons/faPlus';
import { faPlusCircle } from '@fortawesome/free-solid-svg-icons/faPlusCircle';
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons/faQuestionCircle';
import { faRedoAlt } from '@fortawesome/free-solid-svg-icons/faRedoAlt';
import { faRobot } from '@fortawesome/free-solid-svg-icons/faRobot';
import { faRoute } from '@fortawesome/free-solid-svg-icons/faRoute';
import { faSearch } from '@fortawesome/free-solid-svg-icons/faSearch';
Expand All @@ -92,6 +93,7 @@ import { faTh } from '@fortawesome/free-solid-svg-icons/faTh';
import { faThLarge } from '@fortawesome/free-solid-svg-icons/faThLarge';
import { faThList } from '@fortawesome/free-solid-svg-icons/faThList';
import { faTrash } from '@fortawesome/free-solid-svg-icons/faTrash';
import { faUndoAlt } from '@fortawesome/free-solid-svg-icons/faUndoAlt';
import { faUnlock } from '@fortawesome/free-solid-svg-icons/faUnlock';
import { faUpload } from '@fortawesome/free-solid-svg-icons/faUpload';
import { faUser } from '@fortawesome/free-solid-svg-icons/faUser';
Expand Down Expand Up @@ -284,6 +286,7 @@ export default function install(Vue) {
faPlus,
faPlusCircle,
faQuestionCircle,
faRedoAlt,
faRobot,
faRoute,
faSearch,
Expand All @@ -301,6 +304,7 @@ export default function install(Vue) {
faThList,
faTrash,
// faTrashAlt,
faUndoAlt,
faUnlock,
faUpload,
faUser,
Expand Down

0 comments on commit af7e0f6

Please sign in to comment.