feat: promote an attachment to task cover image

This commit is contained in:
kolaente 2022-10-02 13:40:39 +02:00
parent 054d70cbe5
commit 877e425055
No known key found for this signature in database
GPG key ID: F40E70337AB24C9B
3 changed files with 53 additions and 25 deletions

View file

@ -9,7 +9,7 @@
<input <input
v-if="editEnabled" v-if="editEnabled"
:disabled="attachmentService.loading || undefined" :disabled="loading || undefined"
@change="uploadNewAttachment()" @change="uploadNewAttachment()"
id="files" id="files"
multiple multiple
@ -78,6 +78,13 @@
> >
{{ $t('misc.delete') }} {{ $t('misc.delete') }}
</BaseButton> </BaseButton>
<BaseButton
v-if="editEnabled"
class="attachment-info-meta-button"
@click.prevent.stop="setCoverImage(task.coverImageAttachmentId === a.id ? null : a)"
>
{{ task.coverImageAttachmentId === a.id ? $t('task.attachment.unsetAsCover') : $t('task.attachment.setAsCover') }}
</BaseButton>
</p> </p>
</div> </div>
</a> </a>
@ -85,7 +92,7 @@
<x-button <x-button
v-if="editEnabled" v-if="editEnabled"
:disabled="attachmentService.loading" :disabled="loading"
@click="filesRef?.click()" @click="filesRef?.click()"
class="mb-4" class="mb-4"
icon="cloud-upload-alt" icon="cloud-upload-alt"
@ -156,25 +163,30 @@ import {uploadFiles, generateAttachmentUrl} from '@/helpers/attachments'
import {getHumanSize} from '@/helpers/getHumanSize' import {getHumanSize} from '@/helpers/getHumanSize'
import {useCopyToClipboard} from '@/composables/useCopyToClipboard' import {useCopyToClipboard} from '@/composables/useCopyToClipboard'
import {error, success} from '@/message' import {error, success} from '@/message'
import {useTaskStore} from '@/stores/tasks'
import {useI18n} from 'vue-i18n'
const props = defineProps({ const taskStore = useTaskStore()
taskId: { const {t} = useI18n()
type: Number as PropType<ITask['id']>,
required: true, const props = withDefaults(defineProps<{
}, task: ITask,
initialAttachments: { initialAttachments?: IAttachment[],
type: Array, editEnabled: boolean,
}, }>(), {
editEnabled: { editEnabled: true,
default: true,
},
}) })
// FIXME: this should go through the store
const emit = defineEmits(['task-changed'])
const attachmentService = shallowReactive(new AttachmentService()) const attachmentService = shallowReactive(new AttachmentService())
const attachmentStore = useAttachmentStore() const attachmentStore = useAttachmentStore()
const attachments = computed(() => attachmentStore.attachments) const attachments = computed(() => attachmentStore.attachments)
const loading = computed(() => attachmentService.loading || taskStore.isLoading)
function onDrop(files: File[] | null) { function onDrop(files: File[] | null) {
if (files && files.length !== 0) { if (files && files.length !== 0) {
uploadFilesToTask(files) uploadFilesToTask(files)
@ -188,6 +200,7 @@ function downloadAttachment(attachment: IAttachment) {
} }
const filesRef = ref<HTMLInputElement | null>(null) const filesRef = ref<HTMLInputElement | null>(null)
function uploadNewAttachment() { function uploadNewAttachment() {
const files = filesRef.value?.files const files = filesRef.value?.files
@ -199,7 +212,7 @@ function uploadNewAttachment() {
} }
function uploadFilesToTask(files: File[] | FileList) { function uploadFilesToTask(files: File[] | FileList) {
uploadFiles(attachmentService, props.taskId, files) uploadFiles(attachmentService, props.task.id, files)
} }
const attachmentToDelete = ref<AttachmentModel | null>(null) const attachmentToDelete = ref<AttachmentModel | null>(null)
@ -224,6 +237,7 @@ async function deleteAttachment() {
} }
const attachmentImageBlobUrl = ref<string | null>(null) const attachmentImageBlobUrl = ref<string | null>(null)
async function viewOrDownload(attachment: AttachmentModel) { async function viewOrDownload(attachment: AttachmentModel) {
if (SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix))) { if (SUPPORTED_IMAGE_SUFFIX.some((suffix) => attachment.file.name.endsWith(suffix))) {
attachmentImageBlobUrl.value = await attachmentService.getBlobUrl(attachment) attachmentImageBlobUrl.value = await attachmentService.getBlobUrl(attachment)
@ -233,8 +247,18 @@ async function viewOrDownload(attachment: AttachmentModel) {
} }
const copy = useCopyToClipboard() const copy = useCopyToClipboard()
function copyUrl(attachment: IAttachment) { function copyUrl(attachment: IAttachment) {
copy(generateAttachmentUrl(props.taskId, attachment.id)) copy(generateAttachmentUrl(props.task.id, attachment.id))
}
async function setCoverImage(attachment: IAttachment | null) {
const task = await taskStore.update({
...props.task,
coverImageAttachmentId: attachment ? attachment.id : 0,
})
emit('task-changed', task)
success({message: t('task.attachment.successfullyChangedCoverImage')})
} }
</script> </script>

View file

@ -693,7 +693,10 @@
"deleteTooltip": "Delete this attachment", "deleteTooltip": "Delete this attachment",
"deleteText1": "Are you sure you want to delete the attachment {filename}?", "deleteText1": "Are you sure you want to delete the attachment {filename}?",
"copyUrl": "Copy URL", "copyUrl": "Copy URL",
"copyUrlTooltip": "Copy the url of this attachment for usage in text" "copyUrlTooltip": "Copy the url of this attachment for usage in text",
"setAsCover": "Set as cover image",
"unsetAsCover": "Unset as cover image",
"successfullyChangedCoverImage": "The cover image was successfully changed."
}, },
"comment": { "comment": {
"title": "Comments", "title": "Comments",

View file

@ -218,7 +218,8 @@
<div class="content attachments" v-if="activeFields.attachments || hasAttachments"> <div class="content attachments" v-if="activeFields.attachments || hasAttachments">
<attachments <attachments
:edit-enabled="canWrite" :edit-enabled="canWrite"
:task-id="taskId" :task="task"
@task-changed="({coverImageAttachmentId}) => task.coverImageAttachmentId = coverImageAttachmentId"
ref="attachments" ref="attachments"
/> />
</div> </div>
@ -500,7 +501,7 @@ const attachmentStore = useAttachmentStore()
const taskStore = useTaskStore() const taskStore = useTaskStore()
const kanbanStore = useKanbanStore() const kanbanStore = useKanbanStore()
const task = reactive(new TaskModel()) const task = reactive<ITask>(new TaskModel())
useTitle(toRef(task, 'title')) useTitle(toRef(task, 'title'))
// We doubled the task color property here because verte does not have a real change property, leading // We doubled the task color property here because verte does not have a real change property, leading