feat: promote an attachment to task cover image
This commit is contained in:
parent
054d70cbe5
commit
877e425055
3 changed files with 53 additions and 25 deletions
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue