diff --git a/.example-env b/.example-env
index 1afb010b..96b60f55 100644
--- a/.example-env
+++ b/.example-env
@@ -1,10 +1,14 @@
+# Node JS env
+export NODE_REALTIME_PORT='5000' # should match REALTIME_SERVER, below
+
+# Rails env
export DB_USERNAME='postgres'
export DB_PASSWORD='3112'
export DB_HOST='localhost'
export DB_PORT='5432'
export DB_NAME='metamap002'
-export REALTIME_SERVER='http://localhost:5001'
+export REALTIME_SERVER='http://localhost:5000'
export MAILER_DEFAULT_URL='localhost:3000'
export DEVISE_MAILER_SENDER='team@metamaps.cc'
diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb
index 302e9385..c48ac418 100644
--- a/app/controllers/access_controller.rb
+++ b/app/controllers/access_controller.rb
@@ -32,7 +32,7 @@ class AccessController < ApplicationController
# POST maps/:id/access
def access
- user_ids = params[:access] || []
+ user_ids = params[:access].to_a.map(&:to_i) || []
@map.add_new_collaborators(user_ids).each do |user_id|
# add_new_collaborators returns array of added users,
diff --git a/app/controllers/hacks_controller.rb b/app/controllers/hacks_controller.rb
index 1abe3e60..22df9cbb 100644
--- a/app/controllers/hacks_controller.rb
+++ b/app/controllers/hacks_controller.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
# bad code that should be checked over before entering one of the
# nice files from the right side of this repo
class HacksController < ApplicationController
@@ -11,7 +12,7 @@ class HacksController < ApplicationController
response, url = get_with_redirects(url)
title = get_encoded_title(response)
render json: { success: true, title: title, url: url }
- rescue StandardError => e
+ rescue StandardError
render json: { success: false }
end
@@ -28,7 +29,13 @@ class HacksController < ApplicationController
end
def get_encoded_title(http_response)
- title = http_response.body.sub(/.*
(.*)<\/title>.*/m, '\1')
+ # ensure there's actually an html title tag
+ title = http_response.body.sub(%r{.*(.*).*}m, '\1')
+ return '' unless title.starts_with?('')
+ return '' unless title.ends_with?('')
+ title = title.sub('', '').sub(%r{$}, '')
+
+ # encode and trim the title to 140 usable characters
charset = http_response['content-type'].sub(/.*charset=(.*);?.*/, '\1')
charset = nil if charset == 'text/html'
title = title.force_encoding(charset) if charset
diff --git a/doc/production/first-deploy.md b/doc/production/first-deploy.md
index 413717dd..994e68c2 100644
--- a/doc/production/first-deploy.md
+++ b/doc/production/first-deploy.md
@@ -93,10 +93,10 @@ server to see what problems show up:
#### Realtime server:
sudo npm install -g forever
- (crontab -u metamaps -l 2>/dev/null; echo "@reboot $(which forever) --append -l /home/metamaps/logs/forever.realtime.log start /home/metamaps/metamaps/realtime/realtime-server.js") | crontab -u metamaps -
+ (crontab -u metamaps -l 2>/dev/null; echo "@reboot env NODE_REALTIME_PORT=5000 $(which forever) --append -l /home/metamaps/logs/forever.realtime.log start /home/metamaps/metamaps/realtime/realtime-server.js") | crontab -u metamaps -
mkdir -p /home/metamaps/logs
- forever --append \
+ env NODE_REALTIME_PORT=5000 forever --append \
-c /home/metamaps/metamaps/node_modules/.bin/babel-node \
-l /home/metamaps/logs/forever.realtime.log \
start /home/metamaps/metamaps/realtime/realtime-server.js
diff --git a/frontend/src/Metamaps/DataModel/Synapse.js b/frontend/src/Metamaps/DataModel/Synapse.js
index e5002824..5f2a6b88 100644
--- a/frontend/src/Metamaps/DataModel/Synapse.js
+++ b/frontend/src/Metamaps/DataModel/Synapse.js
@@ -38,6 +38,7 @@ const Synapse = Backbone.Model.extend({
newOptions.success = function(model, response, opt) {
if (s) s(model, response, opt)
+ model.set('calculated_permission', model.get('permission'))
model.trigger('saved')
if (permBefore === 'private' && model.get('permission') !== 'private') {
diff --git a/frontend/src/Metamaps/DataModel/Topic.js b/frontend/src/Metamaps/DataModel/Topic.js
index 1e27d138..dff635f2 100644
--- a/frontend/src/Metamaps/DataModel/Topic.js
+++ b/frontend/src/Metamaps/DataModel/Topic.js
@@ -37,8 +37,8 @@ const Topic = Backbone.Model.extend({
newOptions.success = function(model, response, opt) {
if (s) s(model, response, opt)
- model.trigger('saved')
model.set('calculated_permission', model.get('permission'))
+ model.trigger('saved')
if (permBefore === 'private' && model.get('permission') !== 'private') {
model.trigger('noLongerPrivate')
diff --git a/frontend/src/Metamaps/Import.js b/frontend/src/Metamaps/Import.js
index cde37a73..7d1b3aa3 100644
--- a/frontend/src/Metamaps/Import.js
+++ b/frontend/src/Metamaps/Import.js
@@ -375,6 +375,7 @@ const Import = {
$.get('/hacks/load_url_title', {
url
}, function success(data, textStatus) {
+ if (data.trim() === '') return
var selector = '#showcard #topic_' + topic.get('id') + ' .best_in_place'
if ($(selector).find('form').length > 0) {
$(selector).find('textarea, input').val(data.title)
diff --git a/frontend/src/Metamaps/PasteInput.js b/frontend/src/Metamaps/PasteInput.js
index f8c93c3f..6227b23b 100644
--- a/frontend/src/Metamaps/PasteInput.js
+++ b/frontend/src/Metamaps/PasteInput.js
@@ -25,7 +25,7 @@ const PasteInput = {
self.handleFile(e.dataTransfer.files[0], coords)
}
// OMG import bookmarks 😍
- if (e.dataTransfer.items.length > 0) {
+ if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
e.dataTransfer.items[0].getAsString(function(text) {
if (text.match(self.URL_REGEX)) {
self.handle(text, coords)
diff --git a/frontend/src/Metamaps/Realtime/index.js b/frontend/src/Metamaps/Realtime/index.js
index db1334de..952b835c 100644
--- a/frontend/src/Metamaps/Realtime/index.js
+++ b/frontend/src/Metamaps/Realtime/index.js
@@ -172,7 +172,11 @@ let Realtime = {
room: 'global',
$video: self.localVideo.$video,
myVideoView: self.localVideo.view,
- config: { DOUBLE_CLICK_TOLERANCE: 200 }
+ config: { DOUBLE_CLICK_TOLERANCE: 200 },
+ soundUrls: [
+ serverData['sounds/MM_sounds.mp3'],
+ serverData['sounds/MM_sounds.ogg']
+ ]
})
self.room.videoAdded(self.handleVideoAdded)
diff --git a/frontend/src/Metamaps/Views/ChatView.js b/frontend/src/Metamaps/Views/ChatView.js
index 4a549648..fd77864f 100644
--- a/frontend/src/Metamaps/Views/ChatView.js
+++ b/frontend/src/Metamaps/Views/ChatView.js
@@ -226,7 +226,7 @@ var Handlers = {
}
}
-const ChatView = function(messages, mapper, room) {
+const ChatView = function(messages, mapper, room, opts = {}) {
this.room = room
this.mapper = mapper
this.messages = messages // backbone collection
@@ -243,7 +243,7 @@ const ChatView = function(messages, mapper, room) {
Private.attachElements.call(this)
Private.addEventListeners.call(this)
Private.initialMessages.call(this)
- Private.initializeSounds.call(this, room.soundUrls)
+ Private.initializeSounds.call(this, opts.soundUrls)
this.$container.css({
right: '-300px'
})
diff --git a/frontend/src/Metamaps/Views/Room.js b/frontend/src/Metamaps/Views/Room.js
index f3327c6d..a3a79cc8 100644
--- a/frontend/src/Metamaps/Views/Room.js
+++ b/frontend/src/Metamaps/Views/Room.js
@@ -26,10 +26,11 @@ const Room = function(opts = {}) {
this.messages = new Backbone.Collection()
this.currentMapper = new Backbone.Model({ name: opts.username, image: opts.image })
- this.chat = new ChatView(this.messages, this.currentMapper, this.room)
+ this.chat = new ChatView(this.messages, this.currentMapper, this.room, {
+ soundUrls: opts.soundUrls
+ })
this.videos = {}
- this.soundUrls = opts.soundUrls
this.init()
}
diff --git a/realtime/realtime-server.js b/realtime/realtime-server.js
index ad5a1900..0150e84d 100644
--- a/realtime/realtime-server.js
+++ b/realtime/realtime-server.js
@@ -15,4 +15,4 @@ signalling(io, stunservers, store)
junto(io, store)
map(io, store)
-io.listen(5001)
+io.listen(parseInt(process.env.NODE_REALTIME_PORT) || 5000)
diff --git a/realtime/reducer.js b/realtime/reducer.js
index ae2b7488..9b50d3a0 100644
--- a/realtime/reducer.js
+++ b/realtime/reducer.js
@@ -1,4 +1,4 @@
-const { omit, omitBy, isNil, mapValues } = require('lodash')
+const { find, omit, mapValues, values } = require('lodash')
const {
JOIN_MAP,
LEAVE_MAP,
@@ -9,7 +9,16 @@ const {
const NOT_IN_CONVERSATION = 0
const IN_CONVERSATION = 1
-const addMapperToMap = (map, userId) => { return Object.assign({}, map, { [userId]: NOT_IN_CONVERSATION }) }
+const addMapperToMap = (map, userId) => Object.assign({}, map, { [userId]: NOT_IN_CONVERSATION })
+const userStillPresent = (userId, liveMaps) => {
+ if (!userId) return false
+ let stillPresent = false
+ const userIdString = userId.toString()
+ values(liveMaps).forEach(presentUsers => {
+ if (find(Object.keys(presentUsers), id => id === userIdString)) stillPresent = true
+ })
+ return stillPresent
+}
const reducer = (state = { connectedPeople: {}, liveMaps: {} }, action) => {
const { type, payload } = action
@@ -37,10 +46,13 @@ const reducer = (state = { connectedPeople: {}, liveMaps: {} }, action) => {
const newLiveMaps = mapWillEmpty
? omit(liveMaps, payload.mapid)
: Object.assign({}, liveMaps, { [payload.mapid]: omit(map, payload.userid) })
+ delete newLiveMaps[undefined]
+ delete newLiveMaps[null]
+ const updateConnectedPeople = userStillPresent(payload.userid, newLiveMaps) ? connectedPeople : omit(connectedPeople, payload.userid)
return {
- connectedPeople: omit(connectedPeople, payload.userid),
- liveMaps: omitBy(newLiveMaps, isNil)
+ connectedPeople: updateConnectedPeople,
+ liveMaps: newLiveMaps
}
case JOIN_CALL:
// update the user (payload.id is user id) in the given map to be marked in the conversation
@@ -57,15 +69,18 @@ const reducer = (state = { connectedPeople: {}, liveMaps: {} }, action) => {
: Object.assign({}, map, { [payload.userid]: NOT_IN_CONVERSATION })
return Object.assign({}, state, {
- liveMaps: Object.assign({}, liveMaps, { map: newMap })
+ liveMaps: Object.assign({}, liveMaps, { [payload.mapid]: newMap })
})
case 'DISCONNECT':
const mapWithoutUser = omit(map, payload.userid)
const newMapWithoutUser = callWillFinish ? mapValues(mapWithoutUser, () => NOT_IN_CONVERSATION) : mapWithoutUser
const newLiveMapsWithoutUser = mapWillEmpty ? omit(liveMaps, payload.mapid) : Object.assign({}, liveMaps, { [payload.mapid]: newMapWithoutUser })
+ delete newLiveMapsWithoutUser[undefined]
+ delete newLiveMapsWithoutUser[null]
+ const newConnectedPeople = userStillPresent(payload.userid, newLiveMapsWithoutUser) ? connectedPeople : omit(connectedPeople, payload.userid)
return {
- connectedPeople: omit(connectedPeople, payload.userid),
- liveMaps: omitBy(newLiveMapsWithoutUser, isNil)
+ connectedPeople: newConnectedPeople,
+ liveMaps: newLiveMapsWithoutUser
}
default:
return state