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

Add visual notification if auto-scrolling is disabled

Closes #23
This commit is contained in:
Maximilian Mader 2013-05-30 19:03:37 +02:00
parent a7e63a5954
commit be1479940f
2 changed files with 60 additions and 38 deletions

View File

@ -31,6 +31,7 @@ exposed by a function if necessary.
isActive = true isActive = true
newMessageCount = 0 newMessageCount = 0
scrollUpNotifications = off
chatSession = Date.now() chatSession = Date.now()
errorVisible = false errorVisible = false
@ -57,7 +58,6 @@ exposed by a function if necessary.
titleTemplate: null titleTemplate: null
messageTemplate: null messageTemplate: null
userTemplate: null userTemplate: null
config: null config: null
Initialize **Tims Chat**. Bind needed DOM events and initialize data structures. Initialize **Tims Chat**. Bind needed DOM events and initialize data structures.
@ -73,7 +73,7 @@ Initialize **Tims Chat**. Bind needed DOM events and initialize data structures.
v.userTemplate = userTemplate v.userTemplate = userTemplate
console.log 'Initializing' console.log 'Initializing'
When **Tims Chat** becomes focused mark the chat as active and remove the number of new messages from the title. When **Tims Chat** becomes focused mark the chat as active and remove the number of new messages from the title.
$(window).focus -> $(window).focus ->
@ -87,7 +87,7 @@ When **Tims Chat** loses the focus mark the chat as inactive.
$(window).blur -> $(window).blur ->
isActive = false isActive = false
Make the user leave the chat when **Tims Chat** is about to be unloaded. Make the user leave the chat when **Tims Chat** is about to be unloaded.
$(window).on 'beforeunload', -> $(window).on 'beforeunload', ->
@ -102,18 +102,18 @@ Make the user leave the chat when **Tims Chat** is about to be unloaded.
async: false async: false
suppressErrors: true suppressErrors: true
undefined undefined
Insert the appropriate smiley code into the input when a smiley is clicked. Insert the appropriate smiley code into the input when a smiley is clicked.
$('#smilies').on 'click', 'img', -> $('#smilies').on 'click', 'img', ->
insertText ' ' + $(@).attr('alt') + ' ' insertText ' ' + $(@).attr('alt') + ' '
Handle submitting the form. The message will be validated by some basic checks, passed to the `submit` eventlisteners Handle submitting the form. The message will be validated by some basic checks, passed to the `submit` eventlisteners
and afterwards sent to the server by an AJAX request. and afterwards sent to the server by an AJAX request.
$('#timsChatForm').submit (event) -> $('#timsChatForm').submit (event) ->
event.preventDefault() event.preventDefault()
text = $('#timsChatInput').val().trim() text = $('#timsChatInput').val().trim()
$('#timsChatInput').val('').focus().keyup() $('#timsChatInput').val('').focus().keyup()
@ -121,14 +121,14 @@ and afterwards sent to the server by an AJAX request.
# Free the fish! # Free the fish!
freeTheFish() if text.toLowerCase() is '/free the fish' freeTheFish() if text.toLowerCase() is '/free the fish'
text = do (text) -> text = do (text) ->
obj = obj =
text: text text: text
events.submit.fire obj events.submit.fire obj
obj.text obj.text
new WCF.Action.Proxy new WCF.Action.Proxy
autoSend: true autoSend: true
data: data:
@ -145,13 +145,13 @@ and afterwards sent to the server by an AJAX request.
return true unless (data?.returnValues?.errorType?) or (data?.message?) return true unless (data?.returnValues?.errorType?) or (data?.message?)
$('#timsChatInputContainer').addClass('formError').find('.innerError').show().html (data?.returnValues?.errorType) ? data.message $('#timsChatInputContainer').addClass('formError').find('.innerError').show().html (data?.returnValues?.errorType) ? data.message
setTimeout -> setTimeout ->
$('#timsChatInputContainer').removeClass('formError').find('.innerError').hide() $('#timsChatInputContainer').removeClass('formError').find('.innerError').hide()
, 5e3 , 5e3
false false
Autocomplete a username when TAB is pressed. The name to autocomplete is based on the current caret position. Autocomplete a username when TAB is pressed. The name to autocomplete is based on the current caret position.
The the word the caret is in will be passed to `autocomplete` and replaced if a match was found. The the word the caret is in will be passed to `autocomplete` and replaced if a match was found.
@ -159,7 +159,7 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a
if event.keyCode is $.ui.keyCode.TAB if event.keyCode is $.ui.keyCode.TAB
input = $(event.currentTarget) input = $(event.currentTarget)
event.preventDefault() event.preventDefault()
autocomplete.value ?= input.val() autocomplete.value ?= input.val()
autocomplete.caret ?= input.getCaret() autocomplete.caret ?= input.getCaret()
@ -179,17 +179,17 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a
regex = new RegExp "^#{WCF.String.escapeRegExp(toComplete)}", "i" regex = new RegExp "^#{WCF.String.escapeRegExp(toComplete)}", "i"
users = (username for user in $('.timsChatUser') when regex.test(username = $(user).data('username'))) users = (username for user in $('.timsChatUser') when regex.test(username = $(user).data('username')))
toComplete = users[autocomplete.offset++ % users.length] + ', ' if users.length isnt 0 toComplete = users[autocomplete.offset++ % users.length] + ', ' if users.length isnt 0
input.val "#{beforeComplete}#{toComplete}#{afterComplete}" input.val "#{beforeComplete}#{toComplete}#{afterComplete}"
input.setCaret (beforeComplete + toComplete).length input.setCaret (beforeComplete + toComplete).length
Reset autocompleter to default status, when a key is pressed that is not TAB. Reset autocompleter to default status, when a key is pressed that is not TAB.
else else
$('#timsChatInput').click() $('#timsChatInput').click()
Reset autocompleter to default status, when the input is `click`ed, as the position of the caret may have changed. Reset autocompleter to default status, when the input is `click`ed, as the position of the caret may have changed.
$('#timsChatInput').click -> $('#timsChatInput').click ->
@ -197,7 +197,7 @@ Reset autocompleter to default status, when the input is `click`ed, as the posit
offset: 0 offset: 0
value: null value: null
caret: null caret: null
Refresh the room list when the associated button is `click`ed. Refresh the room list when the associated button is `click`ed.
$('#timsChatRoomList button').click -> $('#timsChatRoomList button').click ->
@ -209,7 +209,7 @@ Clear the chat by removing every single message once the clear button is `clicke
event.preventDefault() event.preventDefault()
$('.timsChatMessage').remove() $('.timsChatMessage').remove()
$('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight') $('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight')
Handle toggling of the toggleable buttons. Handle toggling of the toggleable buttons.
$('.timsChatToggle').click (event) -> $('.timsChatToggle').click (event) ->
@ -256,13 +256,13 @@ Visibly mark the message once the associated checkbox is checked.
$(@).parents('.timsChatMessage').addClass 'jsMarked' $(@).parents('.timsChatMessage').addClass 'jsMarked'
else else
$(@).parents('.timsChatMessage').removeClass 'jsMarked' $(@).parents('.timsChatMessage').removeClass 'jsMarked'
Scroll down when autoscroll is being activated. Scroll down when autoscroll is being activated.
$('#timsChatAutoscroll').click (event) -> $('#timsChatAutoscroll').click (event) ->
if $('#timsChatAutoscroll').data 'status' if $('#timsChatAutoscroll').data 'status'
$('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight') $('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight')
$('#timsChatMessageContainer').on 'scroll', (event) -> $('#timsChatMessageContainer').on 'scroll', (event) ->
element = $ @ element = $ @
scrollTop = element.scrollTop() scrollTop = element.scrollTop()
@ -271,10 +271,13 @@ Scroll down when autoscroll is being activated.
if scrollTop < scrollHeight - height - 25 if scrollTop < scrollHeight - height - 25
if $('#timsChatAutoscroll').data('status') is 1 if $('#timsChatAutoscroll').data('status') is 1
scrollUpNotifications = on
$('#timsChatAutoscroll').click() $('#timsChatAutoscroll').click()
if scrollTop > scrollHeight - height - 10 if scrollTop > scrollHeight - height - 10
if $('#timsChatAutoscroll').data('status') is 0 if $('#timsChatAutoscroll').data('status') is 0
scrollUpNotifications = off
$(@).removeClass 'notification'
$('#timsChatAutoscroll').click() $('#timsChatAutoscroll').click()
Enable duplicate tab detection. Enable duplicate tab detection.
@ -284,7 +287,7 @@ Enable duplicate tab detection.
if event.originalEvent.key is 'be.bastelstu.chat.session' if event.originalEvent.key is 'be.bastelstu.chat.session'
if parseInt(event.originalEvent.newValue) isnt chatSession if parseInt(event.originalEvent.newValue) isnt chatSession
showError WCF.Language.get 'chat.error.duplicateTab' showError WCF.Language.get 'chat.error.duplicateTab'
Ask for permissions to use Desktop notifications when notifications are activated. Ask for permissions to use Desktop notifications when notifications are activated.
if window.Notification? if window.Notification?
@ -293,9 +296,9 @@ Ask for permissions to use Desktop notifications when notifications are activate
if window.Notification.permission isnt 'granted' if window.Notification.permission isnt 'granted'
window.Notification.requestPermission (permission) -> window.Notification.requestPermission (permission) ->
window.Notification.permission ?= permission window.Notification.permission ?= permission
events.newMessage.add notify
events.newMessage.add notify
Initialize the `PeriodicalExecuter`s Initialize the `PeriodicalExecuter`s
pe.refreshRoomList = new WCF.PeriodicalExecuter refreshRoomList, 60e3 pe.refreshRoomList = new WCF.PeriodicalExecuter refreshRoomList, 60e3
@ -304,7 +307,7 @@ Initialize the `PeriodicalExecuter`s
Initialize the [**nodePush**](https://github.com/wbbaddons/nodePush) integration of **Tims Chat**. Once 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 the browser is connected to **nodePush** periodic message loading will be disabled and **Tims Chat** will
load messages if the appropriate event arrives. load messages if the appropriate event arrives.
do -> do ->
be.bastelstu.wcf.nodePush.onConnect -> be.bastelstu.wcf.nodePush.onConnect ->
console.log 'Disabling periodic loading' console.log 'Disabling periodic loading'
@ -322,9 +325,9 @@ Finished! Enable the input now and join the chat.
join roomID join roomID
$('#timsChatInput').enable().jCounter().focus(); $('#timsChatInput').enable().jCounter().focus();
console.log "Finished initializing" console.log "Finished initializing"
true true
Free the fish. Free the fish.
@ -380,7 +383,6 @@ Fetch new messages from the server and pass them to `handleMessages`. The userli
console.error 'To many failures, aborting' console.error 'To many failures, aborting'
showError WCF.Language.get 'chat.error.onMessageLoad' showError WCF.Language.get 'chat.error.onMessageLoad'
complete: -> complete: ->
loading = false loading = false
@ -395,10 +397,10 @@ Insert the given messages into the chat stream.
handleMessages = (messages) -> handleMessages = (messages) ->
$('#timsChatMessageContainer').trigger 'scroll' $('#timsChatMessageContainer').trigger 'scroll'
for message in messages for message in messages
events.newMessage.fire message events.newMessage.fire message
output = v.messageTemplate.fetch message output = v.messageTemplate.fetch message
li = $ '<li></li>' li = $ '<li></li>'
li.addClass 'timsChatMessage' li.addClass 'timsChatMessage'
@ -408,14 +410,14 @@ Insert the given messages into the chat stream.
li.append output li.append output
li.appendTo $ '#timsChatMessageContainer > ul' li.appendTo $ '#timsChatMessageContainer > ul'
$('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight') if $('#timsChatAutoscroll').data('status') is 1 $('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight') if $('#timsChatAutoscroll').data('status') is 1
Rebuild the userlist based on the given `users`. Rebuild the userlist based on the given `users`.
handleUsers = (users) -> handleUsers = (users) ->
foundUsers = { } foundUsers = { }
for user in users for user in users
id = "timsChatUser#{user.userID}" id = "timsChatUser#{user.userID}"
@ -464,7 +466,7 @@ Build HTML of the user and insert it into the list, if the users was not found i
menu.append $ "<li><a>#{WCF.Language.get('chat.general.kick')}</a></li>" menu.append $ "<li><a>#{WCF.Language.get('chat.general.kick')}</a></li>"
menu.append $ "<li><a>#{WCF.Language.get('chat.general.ban')}</a></li>" menu.append $ "<li><a>#{WCF.Language.get('chat.general.ban')}</a></li>"
menu.append $ """<li><a href="#{user.link}">#{WCF.Language.get('chat.general.profile')}</a></li>""" menu.append $ """<li><a href="#{user.link}">#{WCF.Language.get('chat.general.profile')}</a></li>"""
events.userMenu.fire user, menu events.userMenu.fire user, menu
li.append menu li.append menu
@ -500,10 +502,12 @@ the existing text. If `options.submit` is true the message will be sent to the s
else else
$('#timsChatInput').focus() $('#timsChatInput').focus()
Send out notifications for the given `message`. The number of unread messages will be prepended to `document.title` and if available desktop notifications will be sent. Send out notifications for the given `message`. The number of unread messages will be prepended to `document.title` and if available desktop notifications will be sent.
notify = (message) -> notify = (message) ->
if scrollUpNotifications
$('#timsChatMessageContainer').addClass 'notification'
return if isActive or $('#timsChatNotify').data('status') is 0 return if isActive or $('#timsChatNotify').data('status') is 0
document.title = v.titleTemplate.fetch document.title = v.titleTemplate.fetch
@ -544,12 +548,12 @@ Fetch the roomlist from the server and update it in the GUI.
li.addClass 'active' if room.active li.addClass 'active' if room.active
$("""<a href="#{room.link}">#{room.title}</a>""").addClass('timsChatRoom').data('roomID', room.roomID).appendTo li $("""<a href="#{room.link}">#{room.title}</a>""").addClass('timsChatRoom').data('roomID', room.roomID).appendTo li
$('#timsChatRoomList ul').append li $('#timsChatRoomList ul').append li
if window.history?.replaceState? if window.history?.replaceState?
$('.timsChatRoom').click (event) -> $('.timsChatRoom').click (event) ->
event.preventDefault() event.preventDefault()
target = $(@) target = $(@)
window.history.replaceState {}, '', target.attr 'href' window.history.replaceState {}, '', target.attr 'href'
join target.data 'roomID' join target.data 'roomID'
@ -612,7 +616,7 @@ Joins a room.
refreshRoomList() refreshRoomList()
failure: -> failure: ->
showError WCF.Language.get 'chat.error.join' showError WCF.Language.get 'chat.error.join'
Bind the given callback to the given event. Bind the given callback to the given event.
addListener = (event, callback) -> addListener = (event, callback) ->
@ -624,11 +628,11 @@ Remove the given callback from the given event.
removeListener = (event, callback) -> removeListener = (event, callback) ->
return false unless events[event]? return false unless events[event]?
events[event].remove callback events[event].remove callback
And finally export the public methods and variables. And finally export the public methods and variables.
Chat = Chat =
init: init init: init
getMessages: getMessages getMessages: getMessages
@ -639,8 +643,7 @@ And finally export the public methods and variables.
listener: listener:
add: addListener add: addListener
remove: removeListener remove: removeListener
window.be ?= {} window.be ?= {}
be.bastelstu ?= {} be.bastelstu ?= {}
window.be.bastelstu.Chat = Chat window.be.bastelstu.Chat = Chat

View File

@ -6,6 +6,16 @@
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode> * @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.wcf.chat * @package be.bastelstu.wcf.chat
*/ */
@keyframes timsChatNotify {
from {
border-color: @wcfContainerBorderColor;
}
to {
border-color: @wcfInputHoverBorderColor;
}
}
#tplChat { #tplChat {
#main > div { #main > div {
overflow: hidden; overflow: hidden;
@ -251,6 +261,15 @@
background-repeat: no-repeat; background-repeat: no-repeat;
min-height: 150px; min-height: 150px;
} }
.notification {
animation-duration: .2s;
animation-name: timsChatNotify;
animation-iteration-count: 5;
animation-direction: alternate;
animation-timing-function: linear;
border-color: @wcfInputHoverBorderColor;
}
} }
html.fullscreen { html.fullscreen {