Implement attachment upload UI skeleton

This commit is contained in:
Maximilian Mader 2020-11-01 00:42:28 +01:00 committed by Tim Düsterhus
parent d07d18a0cb
commit d5195c0562
Signed by: TimWolla
GPG Key ID: 8FF75566094168AF
7 changed files with 238 additions and 4 deletions

View File

@ -25,6 +25,13 @@ use \wcf\system\WCF;
class RoomPage extends \wcf\page\AbstractPage {
use TConfiguredPage;
/**
* Almost dummy attachment handler (used in language variable)
*
* @var \wcf\system\attachment\AttachmentHandler
*/
public $attachmentHandler;
/**
* @inheritDoc
*/
@ -65,7 +72,7 @@ class RoomPage extends \wcf\page\AbstractPage {
*/
public function checkPermissions() {
parent::checkPermissions();
$package = \wcf\data\package\PackageCache::getInstance()->getPackageByIdentifier('be.bastelstu.chat');
if (stripos($package->packageVersion, 'Alpha') !== false) {
$sql = "SELECT COUNT(*) FROM wcf".WCF_N."_user";
@ -91,6 +98,9 @@ class RoomPage extends \wcf\page\AbstractPage {
parent::readData();
// This attachment handler gets only used for the language variable `wcf.attachment.upload.limits`!
$this->attachmentHandler = new \wcf\system\attachment\AttachmentHandler('be.bastelstu.chat.message', 0, 'DEADC0DE00000000DEADC0DE00000000DEADC0DE', $this->room->roomID);
$pushHandler = \wcf\system\push\PushHandler::getInstance();
$pushHandler->joinChannel('be.bastelstu.chat');
$pushHandler->joinChannel('be.bastelstu.chat.room-'.$this->room->roomID);
@ -104,6 +114,7 @@ class RoomPage extends \wcf\page\AbstractPage {
WCF::getTPL()->assign([ 'room' => $this->room
, 'config' => $this->getConfig()
, 'attachmentHandler' => $this->attachmentHandler
]);
}
}

View File

@ -27,6 +27,7 @@ define([ './Chat/console'
, './Chat/ProfileStore'
, './Chat/Room'
, './Chat/Template'
, './Chat/Ui/Attachment/Upload'
, './Chat/Ui/AutoAway'
, './Chat/Ui/Chat'
, './Chat/Ui/ConnectionWarning'
@ -43,7 +44,7 @@ define([ './Chat/console'
, './Chat/Ui/UserActionDropdownHandler'
, './Chat/Ui/UserList'
], function (console, Bottle, Push, Core, Language, RepeatingTimer, CoreUser, Autocompleter,
CommandHandler, Throttle, Message, Messenger, ParseError, ProfileStore, Room, Template, UiAutoAway, Ui,
CommandHandler, Throttle, Message, Messenger, ParseError, ProfileStore, Room, Template, UiAttachmentUpload, UiAutoAway, Ui,
UiConnectionWarning, ErrorDialog, UiInput, UiInputAutocompleter, UiMessageStream, UiMessageActionDelete, UiMobile, UiNotification,
UiReadMarker, UiSettings, UiTopic, UiUserActionDropdownHandler, UiUserList) {
"use strict";
@ -85,6 +86,7 @@ define([ './Chat/console'
this.service('UiTopic', UiTopic)
this.service('UiUserActionDropdownHandler', UiUserActionDropdownHandler)
this.service('UiUserList', UiUserList)
this.service('UiAttachmentUpload', UiAttachmentUpload)
// Register Models
this.bottle.instanceFactory('Message', (container, m) => {

View File

@ -0,0 +1,195 @@
/*
* Copyright (c) 2010-2020 Tim Düsterhus.
*
* Use of this software is governed by the Business Source License
* included in the LICENSE file.
*
* Change Date: 2024-11-01
*
* 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([ 'WoltLabSuite/Core/Language'
, 'WoltLabSuite/Core/Upload'
, 'WoltLabSuite/Core/Dom/Change/Listener'
, 'WoltLabSuite/Core/Dom/Util'
, 'WoltLabSuite/Core/Ui/Dialog'
, '../../DataStructure/EventEmitter'
], function(Language, Upload, DomChangeListener, DomUtil, Dialog, EventEmitter) {
"use strict";
const DIALOG_BUTTON_ID = 'chatAttachmentUploadButton'
const DIALOG_CONTAINER_ID = 'chatAttachmentUploadDialog'
const DEPENDENCIES = [ 'Room' ];
class UiAttachmentUpload extends Upload {
constructor(room) {
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',
acceptableFiles: [ '.png', '.gif', '.jpg', '.jpeg' ]
})
this.room = room
this.previewContainer = previewContainer
}
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()
})
})
const deleteAction = new WCF.Action.Delete('wcf\\data\\attachment\\AttachmentAction', `#${this.previewContainer.id} > p`)
deleteAction.setCallback(() => this.closeDialog())
}
}
closeDialog() {
if (Dialog.getDialog(DIALOG_CONTAINER_ID)) {
Dialog.close(DIALOG_CONTAINER_ID)
}
}
showDialog() {
if (this._button.parentNode) {
this._removeButton()
}
this._target.innerHTML = ''
this._createButton()
elShow(this.uploadDescription)
}
async send(event) {
event.preventDefault()
const parameters = { promise: Promise.resolve() }
this.emit('send', parameters)
try {
await parameters.promise
this.closeDialog()
}
catch (error) {
// TODO: Error handling
console.error(error)
}
}
createButtonGroup(uploadId, objectID) {
const buttonGroup = document.createElement('ul')
buttonGroup.classList.add('buttonGroup')
let li = document.createElement('li')
const cancelButton = document.createElement('span')
cancelButton.classList.add('button', 'jsDeleteButton')
cancelButton.dataset.objectId = objectID
cancelButton.dataset.eventName = 'attachment'
cancelButton.innerText = Language.get('wcf.global.button.cancel')
li.appendChild(cancelButton)
buttonGroup.appendChild(li)
li = document.createElement('li')
const sendButton = document.createElement('span')
sendButton.classList.add('button')
sendButton.dataset.objectId = objectID
sendButton.innerText = Language.get('wcf.global.button.submit')
sendButton.addEventListener('click', (e) => this.send(e))
li.appendChild(sendButton)
buttonGroup.appendChild(li)
const target = this._fileElements[uploadId][0]
target.appendChild(buttonGroup)
DomChangeListener.trigger()
}
/**
* @see WoltLabSuite/Core/Upload#_getParameters
*/
_getParameters() {
const hash = [ ...crypto.getRandomValues(new Uint8Array(20)) ]
.map(m => ('0' + m.toString(16)).slice(-2))
.join('')
return { objectType: "be.bastelstu.chat.message"
, parentObjectID: this.room.roomID
, tmpHash: hash
}
}
/**
* @see WoltLabSuite/Core/Upload#_success
*/
_success(uploadId, data, responseText, xhr, requestOptions) {
if (data.returnValues?.errors?.[0]) {
const error = data.returnValues.errors[0]
elInnerError(this._button, Language.get(`wcf.attachment.upload.error.${error.errorType}`, {
filename: error.filename
}))
}
else {
elInnerError(this._button, '')
}
if (data.returnValues?.attachments?.[uploadId]) {
this._removeButton()
elHide(this.uploadDescription)
const attachment = data.returnValues.attachments[uploadId]
const url = attachment.thumbnailURL || attachment.tinyURL || attachment.url
if (!url) {
throw new Error('Missing image URL')
}
const target = this._fileElements[uploadId][0]
const progress = target.querySelector(':scope > progress')
const img = document.createElement('img')
img.setAttribute('src', url)
img.setAttribute('alt', '')
if (url === attachment.thumbnailURL) {
img.classList.add('attachmentThumbnail')
}
else if (url === attachment.tinyURL) {
img.classList.add('attachmentTinyThumbnail')
}
img.dataset.width = attachment.width
img.dataset.height = attachment.height
DomUtil.replaceElement(progress, img)
this.createButtonGroup(uploadId, attachment.attachmentID)
}
}
}
UiAttachmentUpload.DEPENDENCIES = DEPENDENCIES
EventEmitter(UiAttachmentUpload.prototype)
return UiAttachmentUpload
})

View File

@ -14,7 +14,8 @@
define([ '../Ui' ], function (Ui) {
"use strict";
const DEPENDENCIES = [ 'UiAutoAway'
const DEPENDENCIES = [ 'UiAttachmentUpload'
, 'UiAutoAway'
, 'UiConnectionWarning'
, 'UiInput'
, 'UiInputAutocompleter'
@ -29,7 +30,7 @@ define([ '../Ui' ], function (Ui) {
, 'UiUserList'
]
class Chat extends Ui {
constructor(autoAway, connectionWarning, input, autocompleter, messageStream, messageActionDelete, mobile, notification, readMarker, settings, topic, userActionDropdownHandler, userList) {
constructor(attachmentUpload, autoAway, connectionWarning, input, autocompleter, messageStream, messageActionDelete, mobile, notification, readMarker, settings, topic, userActionDropdownHandler, userList) {
super()
this.actionDropdownHandler = userActionDropdownHandler
@ -45,6 +46,7 @@ define([ '../Ui' ], function (Ui) {
this.settings = settings
this.topic = topic
this.userList = userList
this.attachmentUpload = attachmentUpload
}
bootstrap() {
@ -61,6 +63,7 @@ define([ '../Ui' ], function (Ui) {
this.settings.bootstrap()
this.topic.bootstrap()
this.userList.bootstrap()
this.attachmentUpload.bootstrap()
}
}
Chat.DEPENDENCIES = DEPENDENCIES

View File

@ -0,0 +1,19 @@
<div id="chatAttachmentUploadDialog" class="jsStaticDialogContent" data-title="{lang}wcf.attachment.attachments{/lang}">
<div class="attachmentPreview"></div>
{* placeholder for the upload button *}
<div class="upload"></div>
<small>{lang}wcf.attachment.upload.limits{/lang}</small>
</div>
<script data-relocate="true">
require([ 'Language' ], function (Language) {
Language.addObject({
'wcf.attachment.upload.error.invalidExtension': '{lang}wcf.attachment.upload.error.invalidExtension{/lang}',
'wcf.attachment.upload.error.tooLarge': '{lang}wcf.attachment.upload.error.tooLarge{/lang}',
'wcf.attachment.upload.error.reachedLimit': '{lang}wcf.attachment.upload.error.reachedLimit{/lang}',
'wcf.attachment.upload.error.reachedRemainingLimit': '{lang}wcf.attachment.upload.error.reachedRemainingLimit{/lang}',
'wcf.attachment.upload.error.uploadFailed': '{lang}wcf.attachment.upload.error.uploadFailed{/lang}',
'wcf.attachment.upload.error.uploadPhpLimit': '{lang}wcf.attachment.upload.error.uploadPhpLimit{/lang}',
})
})
</script>

View File

@ -4,6 +4,9 @@
<li><a class="button" href="#" data-module="Bastelstu.be/Chat/Ui/Settings/FullscreenButton"><span class="icon icon16 fa-arrows-alt"></span> <span>{lang}chat.room.button.fullscreen{/lang}</span></a></li>
<li><a class="button" href="#" data-module="Bastelstu.be/Chat/Ui/Settings/NotificationsButton"><span class="icon icon16 fa-bell-o"></span> <span>{lang}chat.room.button.notifications{/lang}</span></a></li>
<li><a class="button" href="#" data-module="Bastelstu.be/Chat/Ui/Settings/AutoscrollButton"><span class="icon icon16 fa-arrow-down"></span> <span>{lang}chat.room.button.autoscroll{/lang}</span></a></li>
{if $__wcf->getSession()->getPermission('user.chat.canAttach')}
<li><a id="chatAttachmentUploadButton" class="button" href="#"><span class="icon icon16 fa-paperclip"></span> <span>{lang}wcf.attachment.attachments{/lang}</span></a>
{/if}
{event name='buttons'}
</ul>
</nav>

View File

@ -71,6 +71,7 @@
{include file='messageTypes' application='chat'}
{include file='userList' application='chat'}
{include file='userListDropdownMenuItems' application='chat'}
{include file='__attachmentDialog' application='chat'}
{if !ENABLE_DEBUG_MODE}{js application='wcf' file='Bastelstu.be.Chat'}{/if}
<script data-relocate="true">