1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2024-10-31 14:10:08 +00:00

Join rooms via JavaScript (so JS errors don't lead to a join)

Closes #19
This commit is contained in:
Tim Düsterhus 2013-05-26 17:19:04 +02:00
parent eec0eefe0b
commit 11084c4bac
8 changed files with 178 additions and 153 deletions

View File

@ -63,7 +63,7 @@ exposed by a function if necessary.
Initialize **Tims Chat**. Bind needed DOM events and initialize data structures.
initialized = false
init = (config, titleTemplate, messageTemplate, userTemplate) ->
init = (roomID, config, titleTemplate, messageTemplate, userTemplate) ->
return false if initialized
initialized = true
@ -240,7 +240,6 @@ Toggle fullscreen mode.
$('html').addClass 'fullscreen'
else
$('html').removeClass 'fullscreen'
Toggle checkboxes
@ -254,9 +253,9 @@ Visibly mark the message once the associated checkbox is checked.
$(document).on 'click', '.timsChatMessage :checkbox', (event) ->
if $(@).is ':checked'
$(@).parents('.timsChatMessage').addClass('jsMarked')
$(@).parents('.timsChatMessage').addClass 'jsMarked'
else
$(@).parents('.timsChatMessage').removeClass('jsMarked')
$(@).parents('.timsChatMessage').removeClass 'jsMarked'
Scroll down when autoscroll is being activated.
@ -297,13 +296,10 @@ Ask for permissions to use Desktop notifications when notifications are activate
events.newMessage.add notify
Initialize the `PeriodicalExecuter`s and run them once.
Initialize the `PeriodicalExecuter`s
pe.refreshRoomList = new WCF.PeriodicalExecuter refreshRoomList, 60e3
pe.getMessages = new WCF.PeriodicalExecuter getMessages, v.config.reloadTime * 1e3
refreshRoomList()
getMessages()
Initialize the [**nodePush**](https://github.com/wbbaddons/nodePush) integration of **Tims Chat**. Once
the browser is connected to **nodePush** periodic message loading will be disabled and **Tims Chat** will
@ -322,8 +318,9 @@ load messages if the appropriate event arrives.
be.bastelstu.wcf.nodePush.onMessage 'be.bastelstu.chat.newMessage', getMessages
be.bastelstu.wcf.nodePush.onMessage 'be.bastelstu.wcf.nodePush.tick60', getMessages
Finished! Enable the input now.
Finished! Enable the input now and join the chat.
join roomID
$('#timsChatInput').enable().jCounter().focus();
console.log "Finished initializing"
@ -531,7 +528,6 @@ Fetch the roomlist from the server and update it in the GUI.
refreshRoomList = ->
console.log 'Refreshing the roomlist'
$('#toggleRooms .ajaxLoad').show()
new WCF.Action.Proxy
autoSend: true
@ -541,14 +537,13 @@ Fetch the roomlist from the server and update it in the GUI.
showLoadingOverlay: false
suppressErrors: true
success: (data) ->
$('#timsChatRoomList li').remove()
$('#toggleRooms .ajaxLoad').hide()
$('.timsChatRoom').remove()
$('#toggleRooms .badge').text data.returnValues.length
for room in data.returnValues
li = $ '<li></li>'
li.addClass 'active' if room.active
$("""<a href="#{room.link}">#{room.title}</a>""").addClass('timsChatRoom').appendTo li
$("""<a href="#{room.link}">#{room.title}</a>""").addClass('timsChatRoom').data('roomID', room.roomID).appendTo li
$('#timsChatRoomList ul').append li
if window.history?.replaceState?
@ -557,43 +552,10 @@ Fetch the roomlist from the server and update it in the GUI.
target = $(@)
window.history.replaceState {}, '', target.attr 'href'
$.ajax target.attr('href'),
dataType: 'json'
data:
ajax: 1
type: 'POST'
success: (data, textStatus, jqXHR) ->
loading = false
target.parent().removeClass 'loading'
$('.active .timsChatRoom').parent().removeClass 'active'
target.parent().addClass 'active'
$('#timsChatTopic').text data.topic
if data.topic is ''
$('#timsChatTopic').addClass 'empty'
else
$('#timsChatTopic').removeClass 'empty'
$('.timsChatMessage').addClass 'unloaded'
handleMessages data.messages
document.title = v.titleTemplate.fetch data
Reload the whole page when an error occurs. The users thus sees the error message (usually `PermissionDeniedException`)
error: ->
window.location.reload true
Show loading icon and prevent switching the room in parallel.
beforeSend: ->
return false if target.parent().hasClass('loading') or target.parent().hasClass 'active'
loading = true
target.parent().addClass 'loading'
join target.data 'roomID'
$('#timsChatRoomList .active').removeClass 'active'
target.parent().addClass 'active'
console.log "Found #{data.returnValues.length} rooms"
@ -621,8 +583,37 @@ Shows an unrecoverable error with the given text.
$('#timsChatLoadingErrorDialog').wcfDialog
closable: false
title: WCF.Language.get('wcf.global.error.title')
title: WCF.Language.get 'wcf.global.error.title'
Joins a room.
join = (roomID) ->
loading = true
new WCF.Action.Proxy
autoSend: true
data:
actionName: 'join'
className: 'chat\\data\\room\\RoomAction'
parameters:
roomID: roomID
success: (data) ->
loading = false
$('#timsChatTopic').text data.returnValues.topic
if data.topic is ''
$('#timsChatTopic').addClass 'empty'
else
$('#timsChatTopic').removeClass 'empty'
$('.timsChatMessage').addClass 'unloaded'
document.title = v.titleTemplate.fetch data.returnValues
handleMessages data.returnValues.messages
getMessages()
refreshRoomList()
failure: ->
showError WCF.Language.get 'chat.error.join'
Bind the given callback to the given event.
addListener = (event, callback) ->
@ -645,7 +636,7 @@ And finally export the public methods and variables.
refreshRoomList: refreshRoomList
insertText: insertText
freeTheFish: freeTheFish
handleMessages: handleMessages
join: join
listener:
add: addListener
remove: removeListener

View File

@ -93,7 +93,7 @@ public function canWrite(\wcf\data\user\User $user = null) {
}
/**
* Returns the ID of this chat-room.
* Returns the ID of this chatroom.
*
* @see \wcf\system\request\IRouteController
*/
@ -102,7 +102,7 @@ public function getID() {
}
/**
* Returns the name of this chat-room.
* Returns the name of this chatroom.
*
* @see \wcf\system\request\IRouteController
*/
@ -110,6 +110,15 @@ public function getTitle() {
return \wcf\system\WCF::getLanguage()->get($this->title);
}
/**
* Returns the topic of this chat room
*
* @return string
*/
public function getTopic() {
return \wcf\system\WCF::getLanguage()->get($this->topic);
}
/**
* Returns the number of users currently active in this room.
*

View File

@ -1,6 +1,8 @@
<?php
namespace chat\data\room;
use \chat\data\message;
use \chat\util\ChatUtil;
use \wcf\system\exception;
use \wcf\system\WCF;
/**
@ -93,11 +95,11 @@ public function validateUpdatePosition() {
WCF::getSession()->checkPermissions($this->permissionsUpdate);
}
else {
throw new \wcf\system\exception\PermissionDeniedException();
throw new exception\PermissionDeniedException();
}
if (!isset($this->parameters['data']['structure'])) {
throw new \wcf\system\exception\UserInputException('structure');
throw new exception\UserInputException('structure');
}
}
@ -124,10 +126,10 @@ public function updatePosition() {
* Validates parameters and permissions.
*/
public function validateGetRoomList() {
if (!MODULE_CHAT) throw new \wcf\system\exception\IllegalLinkException();
if (!MODULE_CHAT) throw new exception\IllegalLinkException();
$this->parameters['room'] = RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID);
if ($this->parameters['room'] === null) throw new \wcf\system\exception\IllegalLinkException();
if ($this->parameters['room'] === null) throw new exception\IllegalLinkException();
}
/**
@ -146,6 +148,7 @@ public function getRoomList() {
'application' => 'chat',
'object' => $room
)),
'roomID' => $room->roomID,
'active' => $room->roomID == $this->parameters['room']->roomID
);
}
@ -153,39 +156,138 @@ public function getRoomList() {
return $result;
}
/**
* Validates parameters and permissions.
*/
public function validateJoin() {
if (!MODULE_CHAT) throw new exception\IllegalLinkException();
unset($this->parameters['user']);
$this->readInteger('roomID');
$room = RoomCache::getInstance()->getRoom($this->parameters['roomID']);
if ($room === null) throw new exception\UserInputException();
if (!$room->canEnter()) throw new exception\PermissionDeniedException();
}
/**
* Joins the room.
*/
public function join() {
// user cannot be set during an AJAX request but may be set by the chat itself
if (!isset($this->parameters['user'])) {
$this->parameters['user'] = WCF::getUser();
}
$room = RoomCache::getInstance()->getRoom($this->parameters['roomID']);
if ($room === null) throw new exception\UserInputException();
if (CHAT_DISPLAY_JOIN_LEAVE) {
if ($this->parameters['user']->chatRoomID) {
// leave message
$messageAction = new message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->parameters['user']->chatRoomID,
'sender' => $this->parameters['user']->userID,
'username' => $this->parameters['user']->username,
'time' => TIME_NOW,
'type' => message\Message::TYPE_LEAVE,
'message' => serialize(array('room' => $room)),
'color1' => $this->parameters['user']->chatColor1,
'color2' => $this->parameters['user']->chatColor2
)
));
$messageAction->executeAction();
}
$ipAddress = '';
if ($this->parameters['user']->userID == WCF::getUser()->userID) $ipAddress = \wcf\util\UserUtil::convertIPv6To4(\wcf\util\UserUtil::getIpAddress());
// join message
$messageAction = new message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $room->roomID,
'sender' => $this->parameters['user']->userID,
'username' => $this->parameters['user']->username,
'time' => TIME_NOW,
'type' => message\Message::TYPE_JOIN,
'message' => serialize(array('ipAddress' => $ipAddress)),
'color1' => $this->parameters['user']->chatColor1,
'color2' => $this->parameters['user']->chatColor2
)
));
$messageAction->executeAction();
}
$newestMessages = message\ViewableMessageList::getNewestMessages($room, CHAT_LASTMESSAGES);
try {
$lastSeen = end($newestMessages)->messageID;
}
catch (\wcf\system\exception\SystemException $e) {
$lastSeen = 0;
}
$editor = new \wcf\data\user\UserEditor($this->parameters['user']);
$editor->update(array(
'chatRoomID' => $room->roomID,
'chatAway' => null,
'chatLastActivity' => TIME_NOW,
'chatLastSeen' => $lastSeen
));
// add activity points
$microtime = microtime(true) * 1000;
$result = $microtime & 0xFFFFFFFF;
if ($result > 0x7FFFFFFF) $result -= 0x80000000;
\wcf\system\user\activity\point\UserActivityPointHandler::getInstance()->fireEvent('be.bastelstu.chat.activityPointEvent.join', $result, WCF::getUser()->userID);
// break if not using ajax
\wcf\system\nodePush\NodePushHandler::getInstance()->sendMessage('be.bastelstu.chat.join');
$messages = array();
foreach ($newestMessages as $message) $messages[] = $message->jsonify(true);
return array(
'title' => (string) $room,
'topic' => $room->getTopic(),
'messages' => $messages
);
}
/**
* Validates parameters and permissions.
*/
public function validateLeave() {
if (!MODULE_CHAT) throw new \wcf\system\exception\IllegalLinkException();
if (!MODULE_CHAT) throw new exception\IllegalLinkException();
unset($this->parameters['user']);
if (RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID) === null) throw new \wcf\system\exception\IllegalLinkException();
if (RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID) === null) throw new exception\IllegalLinkException();
}
/**
* Leaves the room.
*/
public function leave() {
// user cannot be set during an AJAX request may be set by the chat itself
// user cannot be set during an AJAX request but may be set by the chat itself
if (!isset($this->parameters['user'])) {
$this->parameters['user'] = WCF::getUser();
}
$room = RoomCache::getInstance()->getRoom($this->parameters['user']->chatRoomID);
if ($room === null) throw new \wcf\system\exception\UserInputException();
if ($room === null) throw new exception\UserInputException();
if (CHAT_DISPLAY_JOIN_LEAVE) {
// leave message
$messageAction = new \chat\data\message\MessageAction(array(), 'create', array(
$messageAction = new message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $room->roomID,
'sender' => $this->parameters['user']->userID,
'username' => $this->parameters['user']->username,
'time' => TIME_NOW,
'type' => \chat\data\message\Message::TYPE_LEAVE,
'message' => '',
'type' => message\Message::TYPE_LEAVE,
'message' => serialize(array('room' => null)),
'color1' => $this->parameters['user']->chatColor1,
'color2' => $this->parameters['user']->chatColor2
)

View File

@ -1,6 +1,7 @@
<?php
namespace chat\page;
use \chat\data;
use \wcf\system\exception;
use \wcf\system\WCF;
/**
@ -28,13 +29,6 @@ class ChatPage extends \wcf\page\AbstractPage {
*/
public $neededPermissions = array();
/**
* The last X messages for the current room.
*
* @var array<\chat\data\message\Message>
*/
public $newestMessages = array();
/**
* The current room.
*
@ -72,13 +66,6 @@ class ChatPage extends \wcf\page\AbstractPage {
*/
public $smileyCategories = array();
/**
* Values read from the UserStorage of the current user.
*
* @var array
*/
public $userData = array();
/**
* @see wcf\page\AbstractPage::$enableTracking
*/
@ -91,7 +78,6 @@ public function assignVariables() {
parent::assignVariables();
WCF::getTPL()->assign(array(
'newestMessages' => $this->newestMessages,
'room' => $this->room,
'roomID' => $this->roomID,
'rooms' => $this->rooms,
@ -110,39 +96,6 @@ public function readData() {
$this->readRoom();
if (CHAT_DISPLAY_JOIN_LEAVE) {
$messageAction = new data\message\MessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->room->roomID,
'sender' => WCF::getUser()->userID,
'username' => WCF::getUser()->username,
'time' => TIME_NOW,
'type' => \chat\data\message\Message::TYPE_JOIN,
'message' => serialize(array('ipAddress' => \wcf\util\UserUtil::convertIPv6To4(\wcf\util\UserUtil::getIpAddress()))),
'color1' => WCF::getUser()->chatColor1,
'color2' => WCF::getUser()->chatColor2
)
));
$messageAction->executeAction();
$messageAction->getReturnValues();
}
$this->newestMessages = data\message\ViewableMessageList::getNewestMessages($this->room, CHAT_LASTMESSAGES);
try {
$lastSeen = end($this->newestMessages)->messageID;
}
catch (\wcf\system\exception\SystemException $e) {
$lastSeen = 0;
}
$editor = new \wcf\data\user\UserEditor(WCF::getUser());
$editor->update(array(
'chatRoomID' => $this->room->roomID,
'chatAway' => null,
'chatLastActivity' => TIME_NOW,
'chatLastSeen' => $lastSeen
));
// get default smilies
if (MODULE_SMILEY) {
$this->smileyCategories = \wcf\data\smiley\SmileyCache::getInstance()->getCategories();
@ -169,7 +122,6 @@ public function readParameters() {
parent::readParameters();
if (isset($_REQUEST['id'])) $this->roomID = (int) $_REQUEST['id'];
if (isset($_REQUEST['ajax'])) $this->useTemplate = false;
}
/**
@ -183,7 +135,7 @@ public function readRoom() {
$room = reset($this->rooms);
if ($room === null) {
// no valid room found
throw new \wcf\system\exception\IllegalLinkException();
throw new exception\IllegalLinkException();
}
// redirect to first chat-room
\wcf\util\HeaderUtil::redirect(\wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array(
@ -192,9 +144,9 @@ public function readRoom() {
exit;
}
if (!isset($this->rooms[$this->roomID])) throw new \wcf\system\exception\IllegalLinkException();
if (!isset($this->rooms[$this->roomID])) throw new exception\IllegalLinkException();
$this->room = $this->rooms[$this->roomID];
if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
if (!$this->room->canEnter()) throw new exception\PermissionDeniedException();
}
/**
@ -207,26 +159,6 @@ public function show() {
WCF::getBreadcrumbs()->remove(0);
parent::show();
// add activity points
$microtime = microtime(true) * 1000;
$result = $microtime & 0xFFFFFFFF;
if ($result > 0x7FFFFFFF) $result -= 0x80000000;
\wcf\system\user\activity\point\UserActivityPointHandler::getInstance()->fireEvent('be.bastelstu.chat.activityPointEvent.join', $result, WCF::getUser()->userID);
// break if not using ajax
\wcf\system\nodePush\NodePushHandler::getInstance()->sendMessage('be.bastelstu.chat.join');
if ($this->useTemplate) exit;
@header('Content-type: application/json');
$messages = array();
foreach ($this->newestMessages as $message) $messages[] = $message->jsonify(true);
echo \wcf\util\JSON::encode(array(
'title' => $this->room->getTitle(),
'topic' => WCF::getLanguage()->get($this->room->topic),
'messages' => $messages
));
exit;
}
/**

View File

@ -59,6 +59,7 @@
<item name="chat.error.userNotFound"><![CDATA[Der Benutzer „{$exception->getUsername()}“ wurde nicht gefunden.]]></item>
<item name="chat.error.permissionDenied"><![CDATA[Sie dürfen diesen Befehl nicht verwenden.]]></item>
<item name="chat.error.duplicateTab"><![CDATA[Der Chat wurde in einem weiteren Tab geöffnet.]]></item>
<item name="chat.error.join"><![CDATA[Der Chatraum konnte nicht betreten werden.]]></item>
<item name="chat.error.reload"><![CDATA[Neu laden]]></item>
</category>
@ -103,9 +104,9 @@
<category name="chat.message">
<!-- 1 = TYPE_JOIN -->
<item name="chat.message.1"><![CDATA[hat den Chat betreten.{if $__wcf->session->getPermission('admin.user.canViewIpAddress')} ({$ipAddress}){/if}]]></item>
<item name="chat.message.1"><![CDATA[hat den Chat betreten.{if $__wcf->session->getPermission('admin.user.canViewIpAddress') && $ipAddress} ({$ipAddress}){/if}]]></item>
<!-- 2 = TYPE_LEAVE -->
<item name="chat.message.2"><![CDATA[hat den Chat verlassen.]]></item>
<item name="chat.message.2"><![CDATA[{if $room == null}hat den Chat verlassen.{else}ist in den Raum „{$room}“ gegangen.{/if}]]></item>
<!-- 3 = TYPE_AWAY -->
<item name="chat.message.3"><![CDATA[ist jetzt abwesend{if $message}: {$message}{else}.{/if}]]></item>
<!-- 4 = TYPE_BACK -->

View File

@ -5,7 +5,7 @@
<packagedescription><![CDATA[Chat for WoltLab Community Framework™.]]></packagedescription>
<packagedescription language="de"><![CDATA[Chat für WoltLab Community Framework™.]]></packagedescription>
<isapplication>1</isapplication>
<version>3.0.0 Alpha 34</version><!-- Codename: Codenames are overrated -->
<version>3.0.0 Alpha 55</version><!-- Codename: Codenames are overrated -->
<date>2011-11-26</date>
</packageinformation>

View File

@ -17,6 +17,7 @@
'chat.general.notify.title': '{lang}chat.general.notify.title{/lang}',
'chat.error.onMessageLoad': '{lang}chat.error.onMessageLoad{/lang}',
'chat.error.duplicateTab': '{lang}chat.error.duplicateTab{/lang}',
'chat.error.join': '{lang}chat.error.join{/lang}',
'chat.error.reload': '{lang}chat.error.reload{/lang}'
});
@ -29,6 +30,7 @@
{capture assign='userTemplate'}{include application='chat' file='userListUser'}{/capture}
be.bastelstu.Chat.init(
{$roomID},
{
reloadTime: {@CHAT_RELOADTIME},
messageURL: '{link application="chat" controller="NewMessages"}{/link}'
@ -40,11 +42,6 @@
{event name='afterInit'}
// show the last X messages
be.bastelstu.Chat.handleMessages([
{implode from=$newestMessages item='message'}{@$message->jsonify()}{/implode}
]);
$('#timsChatCopyright').click(function (event) {
event.preventDefault();
if (!$.wcfIsset('timsChatCopyrightDialog')) $('<div id="timsChatCopyrightDialog"></div>').appendTo('body');

View File

@ -2,7 +2,7 @@
<nav class="menu chatSidebarMenu">
<ul>
<li id="toggleUsers" class="ui-state-active"><a href="{@$__wcf->getAnchor('timsChatUserList')}" title="{lang}chat.general.users{/lang}">{lang}chat.general.users{/lang} <span class="badge">0</span></a></li>
<li id="toggleRooms"><a href="{@$__wcf->getAnchor('timsChatRoomList')}" title="{lang}chat.general.rooms{/lang}">{lang}chat.general.rooms{/lang} <span class="badge">{#$rooms|count}</span><span class="ajaxLoad icon icon32 icon-spinner"></span></a></li>
<li id="toggleRooms"><a href="{@$__wcf->getAnchor('timsChatRoomList')}" title="{lang}chat.general.rooms{/lang}">{lang}chat.general.rooms{/lang} <span class="badge">0</span></a></li>
</ul>
</nav>
@ -18,13 +18,6 @@
<nav id="timsChatRoomList" style="display: none;">
<div>
<ul>
{foreach from=$rooms item='roomListRoom'}
{if $roomListRoom->canEnter()}
<li{if $roomListRoom->roomID == $room->roomID} class="active"{/if}>
<a href="{link application='chat' controller='Chat' object=$roomListRoom}{/link}" class="timsChatRoom">{$roomListRoom}</a>
</li>
{/if}
{/foreach}
</ul>
<div><button type="button">{lang}chat.general.forceRefresh{/lang}</button></div>
</div>