Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions blackboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ <h1 align="center" class="bb-blackboard-h1">
<span class="bb-hide-when-narrower">Codex</span></span> <span class="bb-hide-when-narrow">Puzzle </span><span class="bb-no-wrap">Blackboard
</span>
</h1>
{{> starred_messages canModify=canEdit}}
<table class="table table-bordered table-condensed bb-puzzle">
<thead>
{{#unless compactMode}}
Expand Down
44 changes: 31 additions & 13 deletions chat.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,38 @@
{{/if}}
</template>

<template name="starred_messages">
{{#if messages}}
<div class="bb-chat-messages bb-starred-messages {{#if canModify}}can-modify-star{{/if}}">
<h3>Starred Messages</h3>
{{#each messages}}
{{> media_message }}
{{/each}}
</div>
{{/if}}
</template>

<template name="media_message">
<div class="media{{#if message.to}} bb-message-pm{{/if}}{{#if followup}} bb-message-followup{{/if}}" data-nick="{{message.nick}}" data-pm-to="{{message.to}}">
{{gravatar id=email image="wavatar" size=48 classes="media-object pull-left img-rounded" }}
<div class="media-body bb-message-body">
<div class="pull-right timestamp">{{pretty_ts message.timestamp}}</div>
{{#unless message.to}}
{{! can't star a PM. }}
<div class="bb-message-star {{#if message.starred}}starred{{/if}}"></div>
{{/unless}}
<h5 class="media-heading"><span title="{{message.nick}}{{nickLocation message.nick}}" class="{{#if nickNear message.nick}}near{{/if}}">{{nickOrName message.nick}}</span>
{{#if message.to}}
<small>(Private message to {{nickOrName message.to}})</small>
{{/if}}
</h5>
{{body}}
</div>
</div>
</template>

<template name="messages">
<div id="messages" class="row-fluid bb-chat-messages">
<div id="messages" class="row-fluid bb-chat-messages {{#if nick}}can-modify-star{{/if}}">
{{#if ready}}{{#if prevTimestamp}}
<p align="center">
<a href="{{prevTimestamp}}" class="chat-link">See earlier messages</a>
Expand All @@ -79,18 +108,7 @@
<span title="{{message.nick}}{{nickLocation message.nick}}" class="{{#if nickNear message.nick}}near{{/if}}">{{nickOrName message.nick}}</span> {{body}}
</div>
{{else}}
<div class="media{{#if message.to}} bb-message-pm{{/if}}{{#if followup}} bb-message-followup{{/if}}" data-nick="{{message.nick}}" data-pm-to="{{message.to}}">
{{gravatar id=email image="wavatar" size=48 classes="media-object pull-left img-rounded" }}
<div class="media-body bb-message-body">
<div class="pull-right timestamp">{{pretty_ts message.timestamp}}</div>
<h5 class="media-heading"><span title="{{message.nick}}{{nickLocation message.nick}}" class="{{#if nickNear message.nick}}near{{/if}}">{{nickOrName message.nick}}</span>
{{#if message.to}}
<small>(Private message to {{nickOrName message.to}})</small>
{{/if}}
</h5>
{{body}}
</div>
</div>
{{> media_message }}
{{/if}}
{{#if isLastRead message.timestamp}}
<div class="bb-message-last-read">read</div>
Expand Down
31 changes: 31 additions & 0 deletions chat.less
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,37 @@
.media-heading { display: none; }
}
}
&.bb-starred-messages {
padding: 0px;
clear: both;
}
.bb-message-star {
display: inline-block;
width: 14px; height: 14px; margin-top: 1px; line-height: 14px;
vertical-align: text-top;
@mask_image: url("/packages/bootstrap/img/glyphicons-halflings.png");
-webkit-mask-image: @mask_image;
mask-image: @mask_image;
float: right;
&.starred {
-webkit-mask-position: -120px 0;
mask-position: -120px 0;
background: palegoldenrod;
}
&:not(.starred) {
-webkit-mask-position: -144px 0;
mask-position: -144px 0;
background: lightgrey;
}
}
&.can-modify-star .bb-message-star {
&.starred {
background: gold;
}
&:not(.starred) {
background: black;
}
}
.media {
position: relative; /* establish context for arrow placement */
margin-bottom: 6px;
Expand Down
64 changes: 44 additions & 20 deletions client/chat.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,47 @@ nickEmail = (nick) ->
n = model.Nicks.findOne canon: cn
return model.getTag(n, 'Gravatar') or "#{cn}@#{settings.DEFAULT_HOST}"

Template.starred_messages.onCreated ->
this.autorun =>
this.subscribe 'starred-messages', Session.get 'room_name'

Template.starred_messages.helpers
messages: ->
# It would be nice to write a concatenation cursor, but it seems overkill
# for the number of starred messages we're ever likely to have.
r = []
for coll in [ model.OldMessages, model.Messages ]
c = coll.find {room_name: (Session.get 'room_name'), starred: true },
sort: [['timestamp', 'asc']]
transform: messageTransform
r.push c.fetch()...
r

Template.media_message.events
'click .bb-message-star.starred': (event, template) ->
return unless $(event.target).closest('.can-modify-star').size() > 0
Meteor.call 'setStarred', this._id, false
'click .bb-message-star:not(.starred)': (event, template) ->
return unless $(event.target).closest('.can-modify-star').size() > 0
Meteor.call 'setStarred', this._id, true


messageTransform = (m) ->
_id: m._id
message: m
# optional server-side follow-up info
followup: (( model.followupStyle() is 'server') and m.followup) or false
email: nickEmail m.nick
body: ->
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this looks weird, but it works. In fact I didn't notice I had forgot the 'do' until looking at it just now.

body = m.body or ''
unless m.bodyIsHtml
body = UI._escape body
body = body.replace /\n|\r\n?/g, '<br/>'
body = convertURLsToLinksAndImages body, m._id
if doesMentionNick m
body = highlightNick body, m.bodyIsHtml
new Spacebars.SafeString(body)

# Template Binding
Template.messages.helpers
room_name: -> Session.get('room_name')
Expand All @@ -148,28 +189,11 @@ Template.messages.helpers
"/chat/#{p.room_name}/#{p.to}"
messages: ->
room_name = Session.get 'room_name'
nick = model.canonical(Session.get('nick') or '')
p = pageForTimestamp room_name, +Session.get('timestamp')
serverFollowup = model.followupStyle() is 'server'
messages = messagesForPage p,
sort: [['timestamp','asc']]
transform: (m) ->
_id: m._id
# optional server-side follow-up info
followup: (serverFollowup and m.followup) or false
message: m

email: -> nickEmail this.message.nick
body: ->
body = this.message.body or ''
unless this.message.bodyIsHtml
body = UI._escape(body)
body = body.replace(/\n|\r\n?/g, '<br/>')
body = convertURLsToLinksAndImages(body, this.message._id)
if doesMentionNick(this.message)
body = highlightNick(body, this.message.bodyIsHtml)
new Spacebars.SafeString(body)

transform: messageTransform

selfScroll = null

touchSelfScroll = ->
Expand Down Expand Up @@ -337,7 +361,7 @@ scrollMessagesView = ->
touchSelfScroll()
instachat.scrolledToBottom = true
# first try using html5, then fallback to jquery
last = document?.querySelector?('.bb-chat-messages > *:last-child')
last = document?.querySelector?('#messages > *:last-child')
if last?.scrollIntoView?
last.scrollIntoView()
else
Expand Down
23 changes: 23 additions & 0 deletions lib/model.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ if Meteor.isServer
# presence: optional string ('join'/'part' for presence-change only)
# bot_ignore: optional boolean (true for messages from e.g. email or twitter)
# to: destination of pm (optional)
# starred: boolean. Pins this message to the top of the puzzle page or blackboard.
# room_name: "<type>/<id>", ie "puzzle/1", "round/1".
# "general/0" for main chat.
# "oplog/0" for the operation log.
Expand Down Expand Up @@ -305,6 +306,8 @@ if Meteor.isServer
M._ensureIndex {to:1, room_name:1, timestamp:-1}, {}
M._ensureIndex {nick:1, room_name:1, timestamp:-1}, {}
M._ensureIndex {room_name:1, timestamp:-1}, {}
M._ensureIndex {room_name:1, starred: -1, timestamp: 1},
partialFilterExpression: starred: true
# watch messages collection and set the followup field as appropriate
# (followup field should already be set properly when the field is
# archived into the OldMessages collection)
Expand Down Expand Up @@ -1170,6 +1173,26 @@ spread_id_to_link = (id) ->
newMsg._id = Messages.insert newMsg
return newMsg

setStarred: (id, starred) ->
check id, NonEmptyString
check starred, Boolean
# Entirely premature optimization: if starring a message, assume it's
# recent; if unstarring, assume it's old.
if starred
colls = [ Messages, OldMessages]
else
colls = [ OldMessages, Messages ]
for coll in colls
num = coll.update (
_id: id
to: null
system: $in: [false, null]
action: $in: [false, null]
oplog: $in: [false, null]
presence: null
), $set: {starred: starred or null}
return if num > 0

updateLastRead: (args) ->
check args, ObjectWith
nick: NonEmptyString
Expand Down
2 changes: 1 addition & 1 deletion puzzle.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h1 class="{{#if stuck}}bb-status-stuck{{/if}}">Puzzle <small>{{puzzle.name}}
{{#with puzzle}}{{> blackboard_tags }}{{/with}}
{{#if puzzle.solved}}{{#with puzzle}}{{> puzzle_correct_answer}}{{/with}}{{/if}}
{{#with puzzle}}{{> puzzle_incorrect_answers}}{{/with}}

{{> starred_messages canModify=nick}}
{{#unless "true"}}
<ul>
<li>Round: {{link id=round._id}}</li>
Expand Down
5 changes: 5 additions & 0 deletions server/server.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ for messages in [ 'messages', 'oldmessages' ]
timestamp: cond
$or: [ { nick: nick }, { to: nick } ]

Meteor.publish 'starred-messages', (room_name) ->
for messages in [ model.OldMessages, model.Messages ]
messages.find { room_name: room_name, starred: true },
sort: [["timestamp", "asc"]]

Meteor.publish 'callins', ->
model.CallIns.find {},
sort: [["created","asc"]]
Expand Down