2020-10-31 23:42:28 +00:00
|
|
|
/*
|
2021-02-04 22:04:35 +00:00
|
|
|
* Copyright (c) 2010-2021 Tim Düsterhus.
|
2020-10-31 23:42:28 +00:00
|
|
|
*
|
|
|
|
* Use of this software is governed by the Business Source License
|
|
|
|
* included in the LICENSE file.
|
|
|
|
*
|
2022-10-13 11:41:16 +00:00
|
|
|
* Change Date: 2026-10-13
|
2020-10-31 23:42:28 +00:00
|
|
|
*
|
|
|
|
* On the date above, in accordance with the Business Source
|
|
|
|
* License, use of this software will be governed by version 2
|
|
|
|
* or later of the General Public License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
define([
|
2020-11-02 17:54:43 +00:00
|
|
|
'WoltLabSuite/Core/Core',
|
2020-10-31 23:42:28 +00:00
|
|
|
'WoltLabSuite/Core/Language',
|
|
|
|
'WoltLabSuite/Core/Upload',
|
|
|
|
'WoltLabSuite/Core/Dom/Change/Listener',
|
|
|
|
'WoltLabSuite/Core/Dom/Util',
|
|
|
|
'WoltLabSuite/Core/Ui/Dialog',
|
|
|
|
'../../DataStructure/EventEmitter',
|
|
|
|
], function (
|
2020-11-02 17:54:43 +00:00
|
|
|
Core,
|
2020-10-31 23:42:28 +00:00
|
|
|
Language,
|
|
|
|
Upload,
|
|
|
|
DomChangeListener,
|
|
|
|
DomUtil,
|
|
|
|
Dialog,
|
|
|
|
EventEmitter
|
|
|
|
) {
|
|
|
|
'use strict'
|
2020-11-01 16:41:19 +00:00
|
|
|
|
2020-10-31 23:42:28 +00:00
|
|
|
const DIALOG_BUTTON_ID = 'chatAttachmentUploadButton'
|
|
|
|
const DIALOG_CONTAINER_ID = 'chatAttachmentUploadDialog'
|
|
|
|
|
2020-11-01 16:06:02 +00:00
|
|
|
const DEPENDENCIES = ['UiInput', 'Room']
|
2020-10-31 23:42:28 +00:00
|
|
|
class UiAttachmentUpload extends Upload {
|
2020-11-01 16:06:02 +00:00
|
|
|
constructor(input, room) {
|
2020-10-31 23:42:28 +00:00
|
|
|
const buttonContainer = document.querySelector(
|
|
|
|
`#${DIALOG_CONTAINER_ID} > .upload`
|
|
|
|
)
|
|
|
|
const buttonContainerId = DomUtil.identify(buttonContainer)
|
|
|
|
|
|
|
|
const previewContainer = document.querySelector(
|
|
|
|
`#${DIALOG_CONTAINER_ID} > .attachmentPreview`
|
|
|
|
)
|
|
|
|
const previewContainerId = DomUtil.identify(previewContainer)
|
|
|
|
|
|
|
|
super(buttonContainerId, previewContainerId, {
|
|
|
|
className: 'wcf\\data\\attachment\\AttachmentAction',
|
|
|
|
})
|
|
|
|
|
2020-11-01 16:06:02 +00:00
|
|
|
this.input = input
|
2020-10-31 23:42:28 +00:00
|
|
|
this.room = room
|
|
|
|
this.previewContainer = previewContainer
|
2020-11-01 11:15:06 +00:00
|
|
|
this.tmpHash = undefined
|
2020-10-31 23:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bootstrap() {
|
|
|
|
this.uploadDescription = document.querySelector(
|
|
|
|
`#${DIALOG_CONTAINER_ID} > small`
|
|
|
|
)
|
|
|
|
|
|
|
|
const button = document.getElementById(DIALOG_BUTTON_ID)
|
|
|
|
const container = document.getElementById(DIALOG_CONTAINER_ID)
|
|
|
|
|
|
|
|
elHide(container)
|
|
|
|
container.classList.remove('jsStaticDialogContent')
|
|
|
|
container.dataset.isStaticDialog = 'true'
|
|
|
|
|
|
|
|
if (button) {
|
|
|
|
button.addEventListener('click', (event) => {
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
Dialog.openStatic(container.id, null, {
|
|
|
|
title: elData(container, 'title'),
|
|
|
|
onShow: () => this.showDialog(),
|
2020-11-02 17:54:43 +00:00
|
|
|
onClose: () => this.onClose(),
|
2020-10-31 23:42:28 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
const deleteAction = new WCF.Action.Delete(
|
|
|
|
'wcf\\data\\attachment\\AttachmentAction',
|
|
|
|
`#${this.previewContainer.id} > p`
|
|
|
|
)
|
2020-11-02 17:54:43 +00:00
|
|
|
deleteAction.setCallback(() => this.onAbort())
|
2020-11-01 16:06:02 +00:00
|
|
|
|
|
|
|
this.input.on('input', (event) => {
|
|
|
|
if (event.target.input.value.length == 0) {
|
|
|
|
button.classList.remove('disabled')
|
|
|
|
} else {
|
|
|
|
button.classList.add('disabled')
|
|
|
|
}
|
|
|
|
})
|
2020-10-31 23:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 17:54:43 +00:00
|
|
|
/**
|
|
|
|
* Called by the WCF.Action.Delete callback.
|
|
|
|
*/
|
|
|
|
onAbort() {
|
|
|
|
this.deleteOnClose = false
|
|
|
|
this.closeDialog()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when the dialog has been closed.
|
|
|
|
*/
|
|
|
|
onClose() {
|
|
|
|
if (this.deleteOnClose) {
|
|
|
|
Core.triggerEvent(this.cancelButton, 'click')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closes the dialog safely by checking if it has been created properly before.
|
|
|
|
*/
|
2020-10-31 23:42:28 +00:00
|
|
|
closeDialog() {
|
|
|
|
if (Dialog.getDialog(DIALOG_CONTAINER_ID)) {
|
|
|
|
Dialog.close(DIALOG_CONTAINER_ID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-02 17:54:43 +00:00
|
|
|
/**
|
|
|
|
* Prepares the initial dialog content.
|
|
|
|
*/
|
2020-10-31 23:42:28 +00:00
|
|
|
showDialog() {
|
2020-11-02 17:54:43 +00:00
|
|
|
this.deleteOnClose = false
|
|
|
|
|
2020-10-31 23:42:28 +00:00
|
|
|
if (this._button.parentNode) {
|
|
|
|
this._removeButton()
|
|
|
|
}
|
|
|
|
|
|
|
|
this._target.innerHTML = ''
|
|
|
|
this._createButton()
|
|
|
|
elShow(this.uploadDescription)
|
|
|
|
}
|
|
|
|
|
2020-11-02 17:54:43 +00:00
|
|
|
/**
|
|
|
|
* Creates the dialog form buttons (send and cancel).
|
|
|
|
*/
|
|
|
|
createButtons(uploadId, objectId, tmpHash) {
|
|
|
|
const formSubmit = elCreate('div')
|
|
|
|
formSubmit.classList.add('formSubmit', 'dialogFormSubmit')
|
|
|
|
|
|
|
|
this.sendButton = document.createElement('button')
|
2022-10-13 11:41:16 +00:00
|
|
|
this.sendButton.classList.add('button', 'buttonPrimary')
|
2020-11-02 17:54:43 +00:00
|
|
|
this.sendButton.innerText = Language.get('wcf.global.button.submit')
|
|
|
|
this.sendButton.addEventListener('click', (e) => this.send(tmpHash, e))
|
|
|
|
formSubmit.appendChild(this.sendButton)
|
|
|
|
|
|
|
|
this.cancelButton = document.createElement('button')
|
2022-10-13 11:41:16 +00:00
|
|
|
this.cancelButton.classList.add('button', 'jsDeleteButton')
|
2020-11-02 17:54:43 +00:00
|
|
|
this.cancelButton.dataset.objectId = objectId
|
|
|
|
this.cancelButton.dataset.eventName = 'attachment'
|
|
|
|
this.cancelButton.innerText = Language.get('wcf.global.button.cancel')
|
|
|
|
formSubmit.appendChild(this.cancelButton)
|
|
|
|
|
|
|
|
const target = this._fileElements[uploadId][0]
|
|
|
|
target.appendChild(formSubmit)
|
|
|
|
|
|
|
|
DomChangeListener.trigger()
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tells the system to send a chat message with the
|
|
|
|
* given attachment (identified by its temporary hash).
|
|
|
|
*/
|
2020-11-01 11:15:06 +00:00
|
|
|
async send(tmpHash, event) {
|
2020-10-31 23:42:28 +00:00
|
|
|
event.preventDefault()
|
2020-11-01 10:57:30 +00:00
|
|
|
const parameters = { promise: Promise.resolve(), tmpHash }
|
2020-10-31 23:42:28 +00:00
|
|
|
this.emit('send', parameters)
|
|
|
|
|
|
|
|
try {
|
|
|
|
await parameters.promise
|
2020-11-02 17:54:43 +00:00
|
|
|
this.deleteOnClose = false
|
2020-10-31 23:42:28 +00:00
|
|
|
this.closeDialog()
|
|
|
|
} catch (error) {
|
|
|
|
console.error(error)
|
2020-11-02 20:27:08 +00:00
|
|
|
|
|
|
|
let container = this._target.querySelector('.error')
|
|
|
|
if (!container) {
|
|
|
|
container = document.createElement('div')
|
|
|
|
container.classList.add('error')
|
|
|
|
this._target.insertBefore(container, this._target.firstChild)
|
|
|
|
}
|
|
|
|
container.innerText = error.message
|
|
|
|
Dialog.rebuild(DIALOG_CONTAINER_ID)
|
2020-10-31 23:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see WoltLabSuite/Core/Upload#_getParameters
|
|
|
|
*/
|
|
|
|
_getParameters() {
|
2020-11-01 11:15:06 +00:00
|
|
|
this.tmpHash = [...crypto.getRandomValues(new Uint8Array(20))]
|
2020-10-31 23:42:28 +00:00
|
|
|
.map((m) => ('0' + m.toString(16)).slice(-2))
|
|
|
|
.join('')
|
|
|
|
|
|
|
|
return {
|
|
|
|
objectType: 'be.bastelstu.chat.message',
|
|
|
|
parentObjectID: this.room.roomID,
|
2020-11-01 11:15:06 +00:00
|
|
|
tmpHash: this.tmpHash,
|
2020-10-31 23:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see WoltLabSuite/Core/Upload#_success
|
|
|
|
*/
|
|
|
|
_success(uploadId, data, responseText, xhr, requestOptions) {
|
2021-02-04 21:45:02 +00:00
|
|
|
const target = this._fileElements[uploadId][0]
|
|
|
|
const progress = target.querySelector(':scope > progress')
|
|
|
|
|
|
|
|
if (data.returnValues.errors && data.returnValues.errors[uploadId]) {
|
|
|
|
const error = data.returnValues.errors[uploadId]
|
2020-10-31 23:42:28 +00:00
|
|
|
|
|
|
|
elInnerError(
|
|
|
|
this._button,
|
|
|
|
Language.get(`wcf.attachment.upload.error.${error.errorType}`, {
|
|
|
|
filename: error.filename,
|
|
|
|
})
|
2020-11-01 16:41:19 +00:00
|
|
|
)
|
2020-11-01 16:19:13 +00:00
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
progress.remove()
|
|
|
|
|
2020-11-01 16:19:13 +00:00
|
|
|
return
|
2020-10-31 23:42:28 +00:00
|
|
|
} else {
|
|
|
|
elInnerError(this._button, '')
|
|
|
|
}
|
|
|
|
|
2020-11-01 16:19:13 +00:00
|
|
|
if (
|
|
|
|
data.returnValues.attachments &&
|
|
|
|
data.returnValues.attachments[uploadId]
|
|
|
|
) {
|
2020-11-02 17:54:43 +00:00
|
|
|
// An attachment was uploaded successfully, which
|
|
|
|
// means that we should delete it when the dialog gets closed,
|
|
|
|
// unless the user used the send or cancel button explicitly.
|
|
|
|
this.deleteOnClose = true
|
|
|
|
|
2020-10-31 23:42:28 +00:00
|
|
|
this._removeButton()
|
|
|
|
elHide(this.uploadDescription)
|
|
|
|
|
|
|
|
const attachment = data.returnValues.attachments[uploadId]
|
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
if (attachment.isImage) {
|
|
|
|
const url =
|
|
|
|
attachment.thumbnailURL || attachment.tinyURL || attachment.url
|
2020-10-31 23:42:28 +00:00
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
if (!url) {
|
|
|
|
throw new Error('Missing image URL')
|
|
|
|
}
|
2020-10-31 23:42:28 +00:00
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
const img = document.createElement('img')
|
|
|
|
img.setAttribute('src', url)
|
|
|
|
img.setAttribute('alt', '')
|
2020-10-31 23:42:28 +00:00
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
if (url === attachment.tinyURL) {
|
|
|
|
img.classList.add('attachmentTinyThumbnail')
|
|
|
|
} else {
|
|
|
|
img.classList.add('attachmentThumbnail')
|
|
|
|
}
|
2020-10-31 23:42:28 +00:00
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
img.dataset.width = attachment.width
|
|
|
|
img.dataset.height = attachment.height
|
2020-10-31 23:42:28 +00:00
|
|
|
|
2021-02-04 21:45:02 +00:00
|
|
|
DomUtil.replaceElement(progress, img)
|
|
|
|
} else {
|
|
|
|
const anchor = document.createElement('a')
|
|
|
|
anchor.setAttribute('href', attachment.url)
|
|
|
|
anchor.innerText = attachment.filename
|
|
|
|
|
|
|
|
DomUtil.replaceElement(progress, anchor)
|
|
|
|
}
|
2020-10-31 23:42:28 +00:00
|
|
|
|
2020-11-02 17:54:43 +00:00
|
|
|
this.createButtons(uploadId, attachment.attachmentID, this.tmpHash)
|
|
|
|
|
|
|
|
Dialog.rebuild(DIALOG_CONTAINER_ID)
|
2020-11-01 16:19:13 +00:00
|
|
|
} else {
|
|
|
|
console.error('Received neither an error nor an attachment response')
|
|
|
|
console.error(data.returnValues)
|
|
|
|
}
|
2020-10-31 23:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
UiAttachmentUpload.DEPENDENCIES = DEPENDENCIES
|
|
|
|
EventEmitter(UiAttachmentUpload.prototype)
|
|
|
|
|
|
|
|
return UiAttachmentUpload
|
|
|
|
})
|