diff --git a/Gemfile.lock b/Gemfile.lock
index d44f02fa..4cfa192f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -42,14 +42,8 @@ GEM
aws-sdk-v1 (1.66.0)
json (~> 1.4)
nokogiri (>= 1.4.4)
- aws-sdk (2.1.19)
- aws-sdk-resources (= 2.1.19)
- aws-sdk-core (2.1.19)
- jmespath (~> 1.0)
- aws-sdk-resources (2.1.19)
- aws-sdk-core (= 2.1.19)
bcrypt (3.1.10)
- best_in_place (3.0.3)
+ best_in_place (3.1.0)
actionpack (>= 3.2)
railties (>= 3.2)
better_errors (2.1.1)
@@ -59,12 +53,11 @@ GEM
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
builder (3.2.2)
- byebug (4.0.5)
- columnize (= 0.9.0)
- cancancan (1.12.0)
+ byebug (8.2.1)
+ cancancan (1.13.1)
climate_control (0.0.3)
activesupport (>= 3.0)
- cocaine (0.5.7)
+ cocaine (0.5.8)
climate_control (>= 0.0.3, < 1.0)
coderay (1.1.0)
coffee-rails (4.1.0)
@@ -73,8 +66,8 @@ GEM
coffee-script (2.4.1)
coffee-script-source
execjs
- coffee-script-source (1.9.1.1)
- columnize (0.9.0)
+ coffee-script-source (1.10.0)
+ concurrent-ruby (1.0.0)
debug_inspector (0.0.2)
devise (3.5.2)
bcrypt (~> 3.0)
@@ -83,7 +76,7 @@ GEM
responders
thread_safe (~> 0.1)
warden (~> 1.2.3)
- dotenv (2.0.0)
+ dotenv (2.0.2)
erubis (2.7.0)
execjs (2.6.0)
ezcrypto (0.7.2)
@@ -94,7 +87,7 @@ GEM
globalid (0.3.6)
activesupport (>= 4.1.0)
i18n (0.7.0)
- jbuilder (2.3.1)
+ jbuilder (2.3.2)
activesupport (>= 3.0.0, < 5)
multi_json (~> 1.2)
jquery-rails (4.0.5)
@@ -112,28 +105,28 @@ GEM
mail (2.6.3)
mime-types (>= 1.16, < 3)
method_source (0.8.2)
- mime-types (2.6.1)
+ mime-types (2.99)
mimemagic (0.3.0)
- mini_portile (0.6.2)
- minitest (5.8.0)
+ mini_portile2 (2.0.0)
+ minitest (5.8.3)
multi_json (1.11.2)
- nokogiri (1.6.6.2)
- mini_portile (~> 0.6.0)
+ nokogiri (1.6.7)
+ mini_portile2 (~> 2.0.0.rc2)
oauth (0.4.7)
orm_adapter (0.5.0)
- paperclip (4.3.0)
+ paperclip (4.3.2)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (= 0.3.0)
- pg (0.18.3)
- pry (0.10.1)
+ pg (0.18.4)
+ pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
- pry-byebug (3.1.0)
- byebug (~> 4.0)
+ pry-byebug (3.3.0)
+ byebug (~> 8.0)
pry (~> 0.10)
pry-rails (0.3.4)
pry (>= 0.9.10)
@@ -174,10 +167,10 @@ GEM
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (10.4.2)
- redis (3.2.1)
+ redis (3.2.2)
responders (2.1.0)
railties (>= 4.2.0, < 5)
- sass (3.4.18)
+ sass (3.4.19)
sass-rails (5.0.4)
railties (>= 4.0.0, < 5.0)
sass (~> 3.1)
@@ -185,8 +178,9 @@ GEM
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
slop (3.6.0)
- sprockets (3.3.4)
- rack (~> 1.0)
+ sprockets (3.5.0)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
sprockets-rails (2.3.3)
actionpack (>= 3.0)
activesupport (>= 3.0)
diff --git a/app/assets/javascripts/src/Metamaps.Backbone.js.erb b/app/assets/javascripts/src/Metamaps.Backbone.js.erb
index 352ca65f..55ba8107 100644
--- a/app/assets/javascripts/src/Metamaps.Backbone.js.erb
+++ b/app/assets/javascripts/src/Metamaps.Backbone.js.erb
@@ -206,6 +206,22 @@ Metamaps.Backbone.MapsCollection = Backbone.Collection.extend({
}
});
+Metamaps.Backbone.Message = Backbone.Model.extend({
+ urlRoot: '/messages',
+ blacklist: ['created_at', 'updated_at'],
+ toJSON: function (options) {
+ return _.omit(this.attributes, this.blacklist);
+ },
+ prepareLiForFilter: function () {
+ /*var li = '';
+ li += '
" +
- "
" +
+ "
" +
"
<%= message %>
" +
"
<%= timestamp %>
" +
"
" +
@@ -46,9 +46,9 @@ Metamaps.Views.chatView = (function () {
this.$container.append(this.$juntoHeader);
this.$container.append(this.$participants);
this.$container.append(this.$chatHeader);
- this.$container.append(this.$messageInput);
this.$container.append(this.$button);
this.$container.append(this.$messages);
+ this.$container.append(this.$messageInput);
},
addEventListeners: function() {
var self = this;
@@ -116,12 +116,12 @@ Metamaps.Views.chatView = (function () {
var m = _.clone(message.attributes);
var today = new Date();
- m.timestamp = new Date(m.timestamp);
+ m.timestamp = new Date(m.created_at);
var date = (m.timestamp.getMonth() + 1) + '/' + m.timestamp.getDate();
date += " " + addZero(m.timestamp.getHours()) + ":" + addZero(m.timestamp.getMinutes());
m.timestamp = date;
- m.image = m.image || 'http://www.hotpepper.ca/wp-content/uploads/2014/11/default_profile_1_200x200.png';
+ m.image = m.user_image || 'http://www.hotpepper.ca/wp-content/uploads/2014/11/default_profile_1_200x200.png';
m.message = linker.link(m.message);
var $html = $(this.messageTemplate(m));
this.$messages.append($html);
@@ -138,9 +138,6 @@ Metamaps.Views.chatView = (function () {
handleInputMessage: function() {
var message = {
message: this.$messageInput.val(),
- timestamp: Date.now(),
- user: this.mapper.get('name'),
- image: this.mapper.get('image')
};
this.$messageInput.val('');
$(document).trigger(chatView.events.message + '-' + this.room, [message]);
@@ -247,6 +244,12 @@ Metamaps.Views.chatView = (function () {
}, duration);
}
+ chatView.prototype.clearMessages = function () {
+ this.unreadMessages = 0;
+ this.$unread.hide();
+ this.$messages.empty();
+ }
+
chatView.prototype.close = function () {
this.$container.css({
right: '-300px'
diff --git a/app/assets/javascripts/src/views/room.js b/app/assets/javascripts/src/views/room.js
index 2003fe83..de920fc3 100644
--- a/app/assets/javascripts/src/views/room.js
+++ b/app/assets/javascripts/src/views/room.js
@@ -40,6 +40,8 @@ Metamaps.Views.room = (function () {
this.isActiveRoom = false;
this.webrtc.leaveRoom();
this.chat.removeParticipants();
+ this.chat.clearMessages();
+ this.messages.reset();
}
room.prototype.setPeopleCount = function(count) {
@@ -76,6 +78,55 @@ Metamaps.Views.room = (function () {
}
});
+ this.webrtc.on('mute', function (data) {
+ var v = self.videos[data.id];
+ if (!v) return;
+
+ if (data.name === 'audio') {
+ v.audioStatus = false;
+ }
+ else if (data.name === 'video') {
+ v.videoStatus = false;
+ v.$avatar.show();
+ }
+ if (!v.audioStatus && !v.videoStatus) v.$container.hide();
+ });
+ this.webrtc.on('unmute', function (data) {
+ var v = self.videos[data.id];
+ if (!v) return;
+
+ if (data.name === 'audio') {
+ v.audioStatus = true;
+ }
+ else if (data.name === 'video') {
+ v.videoStatus = true;
+ v.$avatar.hide();
+ }
+ v.$container.show();
+ });
+
+ this.socket.on('addVideo', function (data) {
+ var existingPeer = self.webrtc.webrtc.peers.find(function(p) { return p.id === data.id; });
+ if (!existingPeer) {
+ var peer = self.webrtc.webrtc.createPeer({
+ id: data.id,
+ type: 'video',
+ enableDataChannels: true,
+ receiveMedia: {
+ mandatory: {
+ OfferToReceiveAudio: true,
+ OfferToReceiveVideo: true
+ }
+ }
+ });
+ peer.avatar = data.avatar;
+ self.webrtc.emit('createdPeer', peer);
+ peer.start();
+
+ // the rest will happen automatically through the 'peerStreamAdded' event and associated event handlers
+ }
+ });
+
var sendChatMessage = function (event, data) {
self.sendChatMessage(data);
};
@@ -90,22 +141,46 @@ Metamaps.Views.room = (function () {
var
id = this.webrtc.getDomId(peer),
video = attachMediaStream(peer.stream);
- v = new VideoView(video, null, id, false, { DOUBLE_CLICK_TOLERANCE: 200 });
+ v = new VideoView(video, null, id, false, { DOUBLE_CLICK_TOLERANCE: 200, avatar: peer.avatar });
if (this._videoAdded) this._videoAdded(v);
this.videos[peer.id] = v;
}
room.prototype.removeVideo = function (peer) {
- console.log(peer);
+ console.log('removeVideo', peer);
var id = typeof peer == 'string' ? peer : peer.id;
this.videos[id].remove();
delete this.videos[id];
}
room.prototype.sendChatMessage = function (data) {
+ var self = this;
//this.roomRef.child('messages').push(data);
+ console.log(data);
+ var m = new Metamaps.Backbone.Message({
+ message: data.message,
+ resource_id: Metamaps.Active.Map.id,
+ resource_type: "Map"
+ });
+ m.save(null, {
+ success: function (model, response) {
+ self.messages.add(model);
+ $(document).trigger(room.events.newMessage, [model]);
+ },
+ error: function (model, response) {
+ console.log('error!', response);
+ }
+ });
}
+ /**
+ * @class
+ * @static
+ */
+ room.events = {
+ newMessage: "Room:newMessage"
+ };
+
return room;
})();
\ No newline at end of file
diff --git a/app/assets/javascripts/src/views/videoView.js b/app/assets/javascripts/src/views/videoView.js
index 8d6bf23a..7f35dee8 100644
--- a/app/assets/javascripts/src/views/videoView.js
+++ b/app/assets/javascripts/src/views/videoView.js
@@ -127,7 +127,8 @@ Metamaps.Views.videoView = (function () {
$vidContainer.addClass('video-cutoff');
$vidContainer.append(this.video);
- this.$avatar = $('
');
+ this.avatar = config.avatar;
+ this.$avatar = $('
');
$vidContainer.append(this.$avatar);
this.$container.append($vidContainer);
@@ -157,6 +158,11 @@ Metamaps.Views.videoView = (function () {
});
}
+ videoView.prototype.setAvatar = function (src) {
+ this.$avatar.attr('src', src);
+ this.avatar = src;
+ }
+
videoView.prototype.remove = function () {
this.$container.off();
if (this.$parent) this.$parent.off('.video' + this.id);
diff --git a/app/assets/stylesheets/junto.css b/app/assets/stylesheets/junto.css
index 5aff8095..6807424e 100644
--- a/app/assets/stylesheets/junto.css
+++ b/app/assets/stylesheets/junto.css
@@ -69,6 +69,8 @@
}
.chat-box {
position: relative;
+ display: flex;
+ flex-direction: column;
z-index: 1;
width: 300px;
float: right;
@@ -141,7 +143,7 @@
}
.chat-box .participants {
width: 100%;
- height: 150px;
+ min-height: 150px;
padding: 16px 0px 0px 0px;
text-align: left;
color: #f5f5f5;
@@ -203,10 +205,7 @@
background-position-y: -24px;
}
.chat-box .chat-input {
- position: absolute;
- bottom: 0;
- left: 0;
- height: 80px;
+ min-height: 80px;
width: 100%;
padding: 8px 8px 8px 8px;
font-size: 13px;
@@ -214,7 +213,6 @@
}
.chat-box .chat-messages {
width: 100%;
- height: 196px;
padding: 16px 0px 0px 0px;
overflow-y: auto;
}
diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb
index add8a0c1..39a012d1 100644
--- a/app/controllers/maps_controller.rb
+++ b/app/controllers/maps_controller.rb
@@ -78,8 +78,9 @@ class MapsController < ApplicationController
object = m.mappable
!object || (object.permission == "private" && (!authenticated? || (authenticated? && @current.id != object.user_id)))
}
+ @allmessages = @map.messages
- respond_with(@allmappers, @allmappings, @allsynapses, @alltopics, @map)
+ respond_with(@allmappers, @allmappings, @allsynapses, @alltopics, @allmessages, @map)
}
format.json { render json: @map }
end
@@ -109,6 +110,7 @@ class MapsController < ApplicationController
@json['synapses'] = @allsynapses
@json['mappings'] = @allmappings
@json['mappers'] = @allmappers
+ @json['messages'] = @map.messages
respond_to do |format|
format.json { render json: @json }
diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
new file mode 100644
index 00000000..e47f9e38
--- /dev/null
+++ b/app/controllers/messages_controller.rb
@@ -0,0 +1,62 @@
+class MessagesController < ApplicationController
+
+ before_filter :require_user, except: [:show]
+
+ # GET /messages/1.json
+ def show
+ @message = Message.find(params[:id])
+
+ respond_to do |format|
+ format.json { render json: @message }
+ end
+ end
+
+ # POST /messages
+ # POST /messages.json
+ def create
+ @message = Message.new(message_params)
+
+ @message.user = current_user
+
+ respond_to do |format|
+ if @message.save
+ format.json { render json: @message, status: :created, location: messages_url }
+ else
+ format.json { render json: @message.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # PUT /messages/1
+ # PUT /messages/1.json
+ def update
+ @message = Message.find(params[:id])
+
+ respond_to do |format|
+ if @message.update_attributes(message_params)
+ format.json { head :no_content }
+ else
+ format.json { render json: @message.errors, status: :unprocessable_entity }
+ end
+ end
+ end
+
+ # DELETE /messages/1
+ # DELETE /messages/1.json
+ def destroy
+ @message = Message.find(params[:id])
+ @message.destroy
+
+ respond_to do |format|
+ format.json { head :no_content }
+ end
+ end
+
+ private
+
+ # Never trust parameters from the scary internet, only allow the white list through.
+ def message_params
+ #params.require(:message).permit(:id, :resource_id, :message)
+ params.permit(:id, :resource_id, :resource_type, :message)
+ end
+end
diff --git a/app/models/map.rb b/app/models/map.rb
index e6a10605..ca447894 100644
--- a/app/models/map.rb
+++ b/app/models/map.rb
@@ -6,6 +6,7 @@ class Map < ActiveRecord::Base
has_many :synapsemappings, -> { Mapping.synapsemapping }, class_name: :Mapping, dependent: :destroy
has_many :topics, through: :topicmappings, source: :mappable, source_type: "Topic"
has_many :synapses, through: :synapsemappings, source: :mappable, source_type: "Synapse"
+ has_many :messages, as: :resource, dependent: :destroy
# This method associates the attribute ":image" with a file attachment
has_attached_file :screenshot, :styles => {
diff --git a/app/models/message.rb b/app/models/message.rb
new file mode 100644
index 00000000..ca9d8553
--- /dev/null
+++ b/app/models/message.rb
@@ -0,0 +1,54 @@
+class Message < ActiveRecord::Base
+
+ belongs_to :user
+ belongs_to :resource, polymorphic: true
+
+ def user_name
+ self.user.name
+ end
+
+ def user_image
+ self.user.image.url
+ end
+
+ def as_json(options={})
+ json = super(:methods =>[:user_name, :user_image])
+ json
+ end
+
+ ##### PERMISSIONS ######
+
+ def authorize_to_delete(user)
+ if (self.user != user)
+ return false
+ end
+ return self
+ end
+
+ # returns false if user not allowed to 'show' Topic, Synapse, or Map
+ def authorize_to_show(user)
+ if (self.resource && self.resource.permission == "private" && self.resource.user != user)
+ return false
+ end
+ return self
+ end
+
+ # returns false if user not allowed to 'edit' Topic, Synapse, or Map
+ def authorize_to_edit(user)
+ if !user
+ return false
+ elsif (self.user != user)
+ return false
+ end
+ return self
+ end
+
+ # returns Boolean if user allowed to view Topic, Synapse, or Map
+ def authorize_to_view(user)
+ if (self.resource && self.resource.permission == "private" && self.resource.user != user)
+ return false
+ end
+ return true
+ end
+
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 16b31872..0969a369 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -10,9 +10,9 @@ class User < ActiveRecord::Base
before_create :generate_code
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :registerable
-
+
serialize :settings, UserPreference
-
+
validates :password, :presence => true,
:length => { :within => 8..40 },
:on => :create
@@ -27,7 +27,7 @@ class User < ActiveRecord::Base
validates_uniqueness_of :email # done by devise
validates :joinedwithcode, :presence => true, :inclusion => { :in => $codes, :message => "%{value} is not valid" }, :on => :create
-
+
# This method associates the attribute ":image" with a file attachment
has_attached_file :image, :styles => {
:thirtytwo => ['32x32#', :png],
@@ -36,7 +36,7 @@ class User < ActiveRecord::Base
:onetwentyeight => ['128x128#', :png]
},
:default_url => 'https://s3.amazonaws.com/metamaps-assets/site/user.png'
-
+
# Validate the attached image is image/jpg, image/png, etc
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
@@ -61,7 +61,7 @@ class User < ActiveRecord::Base
json['rtype'] = "mapper"
json
end
-
+
#generate a random 8 letter/digit code that they can use to invite people
def generate_code
self.code = rand(36**8).to_s(36)
@@ -78,10 +78,10 @@ class User < ActiveRecord::Base
if code == joinedwithcode
update(generation: 0)
else
- update(generation: User.find_by_code(joinedwithcode) + 1)
+ update(generation: User.find_by_code(joinedwithcode).generation + 1)
end
end
-
+
def settings
# make sure we always return a UserPreference instance
if read_attribute(:settings).nil?
@@ -89,7 +89,7 @@ class User < ActiveRecord::Base
end
read_attribute :settings
end
-
+
def settings=(val)
write_attribute :settings, val
end
diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb
index 4d5bf6e5..55a619cc 100644
--- a/app/views/layouts/_upperelements.html.erb
+++ b/app/views/layouts/_upperelements.html.erb
@@ -20,6 +20,7 @@