1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2024-12-21 21:30:08 +00:00

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 @@
class RoomPage extends \wcf\page\AbstractPage { class RoomPage extends \wcf\page\AbstractPage {
use TConfiguredPage; use TConfiguredPage;
/**
* Almost dummy attachment handler (used in language variable)
*
* @var \wcf\system\attachment\AttachmentHandler
*/
public $attachmentHandler;
/** /**
* @inheritDoc * @inheritDoc
*/ */
@ -91,6 +98,9 @@ public function readData() {
parent::readData(); 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 = \wcf\system\push\PushHandler::getInstance();
$pushHandler->joinChannel('be.bastelstu.chat'); $pushHandler->joinChannel('be.bastelstu.chat');
$pushHandler->joinChannel('be.bastelstu.chat.room-'.$this->room->roomID); $pushHandler->joinChannel('be.bastelstu.chat.room-'.$this->room->roomID);
@ -104,6 +114,7 @@ public function assignVariables() {
WCF::getTPL()->assign([ 'room' => $this->room WCF::getTPL()->assign([ 'room' => $this->room
, 'config' => $this->getConfig() , 'config' => $this->getConfig()
, 'attachmentHandler' => $this->attachmentHandler
]); ]);
} }
} }

View File

@ -27,6 +27,7 @@ define([ './Chat/console'
, './Chat/ProfileStore' , './Chat/ProfileStore'
, './Chat/Room' , './Chat/Room'
, './Chat/Template' , './Chat/Template'
, './Chat/Ui/Attachment/Upload'
, './Chat/Ui/AutoAway' , './Chat/Ui/AutoAway'
, './Chat/Ui/Chat' , './Chat/Ui/Chat'
, './Chat/Ui/ConnectionWarning' , './Chat/Ui/ConnectionWarning'
@ -43,7 +44,7 @@ define([ './Chat/console'
, './Chat/Ui/UserActionDropdownHandler' , './Chat/Ui/UserActionDropdownHandler'
, './Chat/Ui/UserList' , './Chat/Ui/UserList'
], function (console, Bottle, Push, Core, Language, RepeatingTimer, CoreUser, Autocompleter, ], 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, UiConnectionWarning, ErrorDialog, UiInput, UiInputAutocompleter, UiMessageStream, UiMessageActionDelete, UiMobile, UiNotification,
UiReadMarker, UiSettings, UiTopic, UiUserActionDropdownHandler, UiUserList) { UiReadMarker, UiSettings, UiTopic, UiUserActionDropdownHandler, UiUserList) {
"use strict"; "use strict";
@ -85,6 +86,7 @@ define([ './Chat/console'
this.service('UiTopic', UiTopic) this.service('UiTopic', UiTopic)
this.service('UiUserActionDropdownHandler', UiUserActionDropdownHandler) this.service('UiUserActionDropdownHandler', UiUserActionDropdownHandler)
this.service('UiUserList', UiUserList) this.service('UiUserList', UiUserList)
this.service('UiAttachmentUpload', UiAttachmentUpload)
// Register Models // Register Models
this.bottle.instanceFactory('Message', (container, m) => { 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) { define([ '../Ui' ], function (Ui) {
"use strict"; "use strict";
const DEPENDENCIES = [ 'UiAutoAway' const DEPENDENCIES = [ 'UiAttachmentUpload'
, 'UiAutoAway'
, 'UiConnectionWarning' , 'UiConnectionWarning'
, 'UiInput' , 'UiInput'
, 'UiInputAutocompleter' , 'UiInputAutocompleter'
@ -29,7 +30,7 @@ define([ '../Ui' ], function (Ui) {
, 'UiUserList' , 'UiUserList'
] ]
class Chat extends Ui { 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() super()
this.actionDropdownHandler = userActionDropdownHandler this.actionDropdownHandler = userActionDropdownHandler
@ -45,6 +46,7 @@ define([ '../Ui' ], function (Ui) {
this.settings = settings this.settings = settings
this.topic = topic this.topic = topic
this.userList = userList this.userList = userList
this.attachmentUpload = attachmentUpload
} }
bootstrap() { bootstrap() {
@ -61,6 +63,7 @@ define([ '../Ui' ], function (Ui) {
this.settings.bootstrap() this.settings.bootstrap()
this.topic.bootstrap() this.topic.bootstrap()
this.userList.bootstrap() this.userList.bootstrap()
this.attachmentUpload.bootstrap()
} }
} }
Chat.DEPENDENCIES = DEPENDENCIES 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/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/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> <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'} {event name='buttons'}
</ul> </ul>
</nav> </nav>

View File

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