From ad2ba33db66c3c8269fd77b9b5a4d7d9b90acdf3 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Sat, 16 Sep 2017 13:41:50 -0700 Subject: [PATCH 01/40] downgrade redis to 3.3.3 This fixes the following error seen on metamaps.cc: > *A* `Gem::LoadError` *occurred while* `POST ` *was processed > by* `mappings#create`: Specified 'redis' for Action Cable pubsub > adapter, but the gem is not loaded. Add `gem 'redis'` to your Gemfile > (and ensure its version is at the minimum required by Action Cable). Backtrace ---------------- ```/usr/local/rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/monitor.rb:214:in `mon_synchronize' /home/metamaps/metamaps.cc/app/models/mapping.rb:32:in `after_created' /home/metamaps/metamaps.cc/app/controllers/mappings_controller.rb:24:in `create'``` --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index be366c49..831c5ffb 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem 'pundit' gem 'pundit_extra' gem 'rack-attack' gem 'rack-cors' -gem 'redis' +gem 'redis', '~> 3.3.3' gem 'slack-notifier' gem 'snorlax' gem 'sucker_punch' diff --git a/Gemfile.lock b/Gemfile.lock index fe922d7b..c1b98be7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -220,7 +220,7 @@ GEM rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - redis (4.0.0) + redis (3.3.3) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) @@ -331,7 +331,7 @@ DEPENDENCIES rack-attack rack-cors rails (~> 5.0.0) - redis + redis (~> 3.3.3) rspec-rails rubocop sass-rails @@ -348,4 +348,4 @@ RUBY VERSION ruby 2.3.0p0 BUNDLED WITH - 1.14.6 + 1.15.4 From 3886e62a7b4d0e9627e66a3376215a4feca1589c Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sun, 17 Sep 2017 09:01:44 -0400 Subject: [PATCH 02/40] make desc edit area larger and resizable --- app/assets/stylesheets/base.scss.erb | 12 ++++-------- frontend/src/components/TopicCard/Desc.js | 1 + 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/base.scss.erb b/app/assets/stylesheets/base.scss.erb index 6415d535..3871fd18 100644 --- a/app/assets/stylesheets/base.scss.erb +++ b/app/assets/stylesheets/base.scss.erb @@ -139,14 +139,12 @@ $mid-gray-opacity: rgba(66, 66, 66, 0.6); font-family: helvetica, sans-serif; color: #424242; padding: 0; - width: 100%; + width: 258px; margin: 0; border: 0; outline: none; - font-size: 12px; - line-height: 15px; background: none; - resize: none; + overflow-y: scroll; } /* @@ -180,11 +178,9 @@ $mid-gray-opacity: rgba(66, 66, 66, 0.6); .CardOnGraph .riek_desc { display:block; - margin-top:2px; - padding-right: 18px; - margin-right: 8px; + padding-right: 26px; } -.canEdit .CardOnGraph .riek_desc:hover { +.canEdit .CardOnGraph .riek_desc:not(.riek-editing):hover { background-image: url(<%= asset_data_uri('edit.png') %>); background-position: top right; background-repeat: no-repeat; diff --git a/frontend/src/components/TopicCard/Desc.js b/frontend/src/components/TopicCard/Desc.js index 9529cbfe..fa3383e8 100644 --- a/frontend/src/components/TopicCard/Desc.js +++ b/frontend/src/components/TopicCard/Desc.js @@ -38,6 +38,7 @@ class Desc extends Component { change={this.props.onChange} className="riek_desc" classEditing="riek-editing" + rows="6" editProps={{ onKeyPress: e => { const ENTER = 13 From 77846cfcb792e00f95b99253633f043e4ecf0db2 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 18 Sep 2017 16:59:52 -0400 Subject: [PATCH 03/40] missing period for consistency --- app/views/users/edit.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 4ea6130f..1ee406e7 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -52,7 +52,7 @@ <% end %> <%= settings.label :follow_topic_on_contributed, class: 'firstFieldText' do %> <%= settings.check_box :follow_topic_on_contributed, class: 'inline' %> - Auto-follow topics you edit + Auto-follow topics you edit. <% end %> <%= settings.label :follow_map_on_created, class: 'firstFieldText' do %> <%= settings.check_box :follow_map_on_created, class: 'inline' %> From 6e1878413f9384444ef4d1138fd96b716e4df5b3 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 12 Sep 2017 15:17:15 -0400 Subject: [PATCH 04/40] wip right click in react --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 2 +- frontend/src/Metamaps/JIT.js | 216 +----------------- frontend/src/Metamaps/Views/ContextMenu.js | 113 +++++++++ frontend/src/Metamaps/Views/index.js | 4 +- frontend/src/components/common/ContextMenu.js | 77 +++++++ 5 files changed, 196 insertions(+), 216 deletions(-) create mode 100644 frontend/src/Metamaps/Views/ContextMenu.js create mode 100644 frontend/src/components/common/ContextMenu.js diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index db850091..3393a951 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -9,7 +9,7 @@ import { notifyUser } from './index.js' import ImportDialog from './ImportDialog' import Active from '../Active' import DataModel from '../DataModel' -import { ExploreMaps, ChatView, TopicCard } from '../Views' +import { ExploreMaps, ChatView, TopicCard, ContextMenu } from '../Views' import Filter from '../Filter' import JIT from '../JIT' import Realtime from '../Realtime' diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index ffaab923..7116854f 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -11,6 +11,7 @@ import $jit from '../patched/JIT' import MetacodeSelect from '../components/MetacodeSelect' import Active from './Active' +import ContextMenu from './Views/ContextMenu' import Control from './Control' import Create from './Create' import DataModel from './DataModel' @@ -1354,222 +1355,9 @@ const JIT = { // select the node Control.selectNode(node, e) + ContextMenu.selectNode(node, {x: e.clientX, y: e.clientY}) - // delete old right click menu - $('.rightclickmenu').remove() - // create new menu for clicked on node - const rightclickmenu = document.createElement('div') - rightclickmenu.className = 'rightclickmenu' - // prevent the custom context menu from immediately opening the default context menu as well - rightclickmenu.setAttribute('oncontextmenu', 'return false') - - // add the proper options to the menu - let menustring = '
    ' - - const authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper) - - const disabled = authorized ? '' : 'disabled' - - if (Active.Map) menustring += '
  • Hide until refresh
    Ctrl+H
  • ' - if (Active.Map && Active.Mapper) menustring += '
  • Remove from map
    Ctrl+M
  • ' - if (Active.Topic) menustring += '
  • Remove from view
    Ctrl+M
  • ' - if (Active.Map && Active.Mapper) menustring += '
  • Delete
    Ctrl+D
  • ' - - if (Active.Topic) { - menustring += '
  • Center this topic
    Alt+E
  • ' - } - - menustring += '
  • Open in new tab
  • ' - - if (Active.Mapper) { - const options = outdent` -
      -
    • commons
    • -
    • public
    • -
    • private
    • -
    ` - - menustring += '
  • ' - - menustring += outdent` -
  • -
    - Change permissions - ${options} -
    -
  • ` - - menustring += '
  • Change metacode
  • ' - } - if (Active.Topic) { - if (!Active.Mapper) { - menustring += '
  • ' - } - - // set up the get sibling menu as a "lazy load" - // only fill in the submenu when they hover over the get siblings list item - const siblingMenu = outdent` -
      -
    • All
      Alt+R
    • -
    • -
    ` - menustring += '
  • Reveal siblings' + siblingMenu + '
  • ' - } - - menustring += '
' - rightclickmenu.innerHTML = menustring - - // position the menu where the click happened - const position = {} - const RIGHTCLICK_WIDTH = 300 - const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static - const SUBMENUS_WIDTH = 256 - const MAX_SUBMENU_HEIGHT = 270 - const windowWidth = $(window).width() - const windowHeight = $(window).height() - - if (windowWidth - e.clientX < SUBMENUS_WIDTH) { - position.right = windowWidth - e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { - position.right = windowWidth - e.clientX - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { - position.left = e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else { - position.left = e.clientX - } - - if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { - position.bottom = windowHeight - e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { - position.top = e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else { - position.top = e.clientY - } - - $(rightclickmenu).css(position) - // add the menu to the page - $('#wrapper').append(rightclickmenu) - - ReactDOM.render( - React.createElement(MetacodeSelect, { - onMetacodeSelect: metacodeId => { - if (Selected.Nodes.length > 1) { - // batch update multiple topics - Control.updateSelectedMetacodes(metacodeId) - } else { - const topic = DataModel.Topics.get(node.id) - topic.save({ - metacode_id: metacodeId - }) - } - $(rightclickmenu).remove() - }, - metacodeSets: ReactApp.metacodeSets - }), - document.getElementById('metacodeOptionsWrapper') - ) - - // attach events to clicks on the list items - - // delete the selected things from the database - if (authorized) { - $('.rc-delete').click(function() { - $('.rightclickmenu').remove() - Control.deleteSelected() - }) - } - - // remove the selected things from the map - if (Active.Topic || authorized) { - $('.rc-remove').click(function() { - $('.rightclickmenu').remove() - Control.removeSelectedEdges() - Control.removeSelectedNodes() - }) - } - - // hide selected nodes and synapses until refresh - $('.rc-hide').click(function() { - $('.rightclickmenu').remove() - Control.hideSelectedEdges() - Control.hideSelectedNodes() - }) - - // when in radial, center on the topic you picked - $('.rc-center').click(function() { - $('.rightclickmenu').remove() - Topic.centerOn(node.id) - }) - - // open the entity in a new tab - $('.rc-popout').click(function() { - $('.rightclickmenu').remove() - const win = window.open('/topics/' + node.id, '_blank') - win.focus() - }) - - // change the permission of all the selected nodes and synapses that you were the originator of - $('.rc-permission li').click(function() { - $('.rightclickmenu').remove() - // $(this).text() will be 'commons' 'public' or 'private' - Control.updateSelectedPermissions($(this).text()) - }) - - // fetch relatives - let fetchSent = false - $('.rc-siblings').hover(function() { - if (!fetchSent) { - JIT.populateRightClickSiblings(node) - fetchSent = true - } - }) - $('.rc-siblings .fetchAll').click(function() { - $('.rightclickmenu').remove() - // data-id is a metacode id - Topic.fetchRelatives(node) - }) }, // selectNodeOnRightClickHandler, - populateRightClickSiblings: function(node) { - // depending on how many topics are selected, do different things - const topic = node.getData('topic') - - // add a loading icon for now - const loader = new CanvasLoader('loadingSiblings') - loader.setColor('#4FC059') // default is '#000000' - loader.setDiameter(15) // default is 40 - loader.setDensity(41) // default is 40 - loader.setRange(0.9) // default is 1.3 - loader.show() // Hidden by default - - const topics = DataModel.Topics.map(function(t) { return t.id }) - const topicsString = topics.join() - - const successCallback = function(data) { - $('#loadingSiblings').remove() - - for (var key in data) { - const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` - $('#fetchSiblingList').append(`
  • ${string}
  • `) - } - - $('.rc-siblings .getSiblings').click(function() { - $('.rightclickmenu').remove() - // data-id is a metacode id - Topic.fetchRelatives(node, $(this).attr('data-id')) - }) - } - - $.ajax({ - type: 'GET', - url: '/topics/' + topic.id + '/relative_numbers.json?network=' + topicsString, - success: successCallback, - error: function() {} - }) - }, selectEdgeOnClickHandler: function(adj, e) { if (Visualize.mGraph.busy) return diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js new file mode 100644 index 00000000..be799b4b --- /dev/null +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -0,0 +1,113 @@ +import Control from '../Control' +import DataModel from '../DataModel' +import ReactApp from '../GlobalUI/ReactApp' +import Selected from '../Selected' +import Topic from '../Topic' + +const ContextMenu = { + clickedNode: null, + clickedEdge: null, + pos: {x: 0, y: 0}, + siblingsData: null, + selectNode: (node, pos) => { + ContextMenu.pos = pos + ContextMenu.clickedNode = node + ContextMenu.clickedEdge = null + ContextMenu.siblingsData = null + ReactApp.render() + }, + selectEdge: (edge, pos) => { + ContextMenu.pos = pos + ContextMenu.clickedNode = null + ContextMenu.clickedEdge = edge + ContextMenu.siblingsData = null + ReactApp.render() + }, + reset: () => { + ContextMenu.siblingsData = null + ContextMenu.clickedNode = null + ContextMenu.clickedEdge = null + ReactApp.render() + }, + delete: Control.deleteSelected, + remove: () => { + Control.removeSelectedEdges() + Control.removeSelectedNodes() + ContextMenu.reset() + }, + hide: () => { + Control.hideSelectedEdges() + Control.hideSelectedNodes() + ContextMenu.reset() + }, + centerOn: (id) => { + Topic.centerOn(id) + ContextMenu.reset() + }, + popoutTopic: (id) => { + ContextMenu.reset() + const win = window.open('/topics/' + id, '_blank') + win.focus() + }, + updatePermissions: (permission) => { + // will be 'commons' 'public' or 'private' + Control.updateSelectedPermissions(permission) + ContextMenu.reset() + }, + onMetacodeSelect: (id, metacodeId) => { + if (Selected.Nodes.length > 1) { + // batch update multiple topics + Control.updateSelectedMetacodes(metacodeId) + } else { + const topic = DataModel.Topics.get(id) + topic.save({ + metacode_id: metacodeId + }) + } + ContextMenu.reset() + }, + fetchRelatives: (node, metacodeId) => { + Topic.fetchRelatives(node, metacodeId) + ContextMenu.reset() + }, + populateSiblings: function(id) { + // depending on how many topics are selected, do different things + + // add a loading icon for now + /*const loader = new CanvasLoader('loadingSiblings') + loader.setColor('#4FC059') // default is '#000000' + loader.setDiameter(15) // default is 40 + loader.setDensity(41) // default is 40 + loader.setRange(0.9) // default is 1.3 + loader.show() // Hidden by default*/ + + const topics = DataModel.Topics.map(function(t) { return t.id }) + const topicsString = topics.join() + + const successCallback = function(data) { + ContextMenu.siblingsData = data + ReactApp.render() + /*$('#loadingSiblings').remove() + + for (var key in data) { + const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` + $('#fetchSiblingList').append(`
  • ${string}
  • `) + } + + $('.rc-siblings .getSiblings').click(function() { + $('.rightclickmenu').remove() + // data-id is a metacode id + Topic.fetchRelatives(node, $(this).attr('data-id')) + })*/ + } + + $.ajax({ + type: 'GET', + url: '/topics/' + id + '/relative_numbers.json?network=' + topicsString, + success: successCallback, + error: function() {} + }) + } +} + +export default ContextMenu diff --git a/frontend/src/Metamaps/Views/index.js b/frontend/src/Metamaps/Views/index.js index 0f7cf566..de9d5aab 100644 --- a/frontend/src/Metamaps/Views/index.js +++ b/frontend/src/Metamaps/Views/index.js @@ -1,5 +1,6 @@ /* global $ */ +import ContextMenu from './ContextMenu' import ExploreMaps from './ExploreMaps' import ChatView from './ChatView' import VideoView from './VideoView' @@ -12,6 +13,7 @@ const Views = { $(document).on(JUNTO_UPDATED, () => ExploreMaps.render()) ChatView.init([serverData['sounds/MM_sounds.mp3'], serverData['sounds/MM_sounds.ogg']]) }, + ContextMenu, ExploreMaps, ChatView, VideoView, @@ -19,5 +21,5 @@ const Views = { TopicCard } -export { ExploreMaps, ChatView, VideoView, Room, TopicCard } +export { ContextMenu, ExploreMaps, ChatView, VideoView, Room, TopicCard } export default Views diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js new file mode 100644 index 00000000..abc0bad4 --- /dev/null +++ b/frontend/src/components/common/ContextMenu.js @@ -0,0 +1,77 @@ +import React, { Component, PropTypes } from 'react' + +class ContextMenu extends Component { + static propTypes = { + node: PropTypes.object, + edge: PropTypes.object + } + + render () { + const style = { + position: 'absolute', + top: '10px', + left: '10px' + } + return
    +
      +
    • +
      Hide until refresh
      Ctrl+H
      +
    • +
    • +
      Remove from map
      Ctrl+M
      +
    • +
    • +
      Delete
      Ctrl+D
      +
    • +
    • +
      Open in new tab +
    • +
    • +
    • +
      Change permissions +
        +
      • commons
      • +
      • public
      • +
      • private
      • +
      +
      +
    • +
    • +
    • +
    +
    + + // position the menu where the click happened + /*const position = {} + const RIGHTCLICK_WIDTH = 300 + const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static + const SUBMENUS_WIDTH = 256 + const MAX_SUBMENU_HEIGHT = 270 + const windowWidth = $(window).width() + const windowHeight = $(window).height() + + if (windowWidth - e.clientX < SUBMENUS_WIDTH) { + position.right = windowWidth - e.clientX + $(rightclickmenu).addClass('moveMenusToLeft') + } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { + position.right = windowWidth - e.clientX + } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { + position.left = e.clientX + $(rightclickmenu).addClass('moveMenusToLeft') + } else { + position.left = e.clientX + } + + if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { + position.bottom = windowHeight - e.clientY + $(rightclickmenu).addClass('moveMenusUp') + } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { + position.top = e.clientY + $(rightclickmenu).addClass('moveMenusUp') + } else { + position.top = e.clientY + }*/ + } +} + +export default ContextMenu From e290244404ebb8f81eb3f1a9ee8909aaa0c6ef7e Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 18 Sep 2017 23:30:33 -0400 Subject: [PATCH 05/40] beginning to come together --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 24 +++++ frontend/src/Metamaps/Listeners.js | 4 +- frontend/src/Metamaps/Topic.js | 6 +- frontend/src/Metamaps/Views/ContextMenu.js | 43 ++++---- frontend/src/components/MapView/index.js | 9 +- frontend/src/components/TopicView/index.js | 5 +- frontend/src/components/common/ContextMenu.js | 99 ++++++++++++++++--- 7 files changed, 144 insertions(+), 46 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 3393a951..5290fe12 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -113,6 +113,7 @@ const ReactApp = { self.getFilterProps(), self.getCommonProps(), self.getMapsProps(), + self.getContextMenuProps(), self.getTopicCardProps(), self.getChatProps()) }, @@ -155,6 +156,29 @@ const ReactApp = { onTopicFollow: Topic.onTopicFollow } }, + getContextMenuProps: function() { + const self = ReactApp + return { + // values + contextMenu: !!(ContextMenu.clickedNode || ContextMenu.clickedEdge), + contextNode: ContextMenu.clickedNode, + contextEdge: ContextMenu.clickedEdge, + contextPos: ContextMenu.pos, + contextFetchingSiblingsData: ContextMenu.fetchingSiblingsData, + contextSiblingsData: ContextMenu.siblingsData, + // functions + contextReset: ContextMenu.reset, + contextDelete: ContextMenu.delete, + contextRemove: ContextMenu.remove, + contextHide: ContextMenu.hide, + contextCenterOn: ContextMenu.centerOn, + contextPopoutTopic: ContextMenu.popoutTopic, + contextUpdatePermissions: ContextMenu.updatePermissions, + contextOnMetacodeSelect: ContextMenu.onMetacodeSelect, + contextFetchSiblings: ContextMenu.fetchSiblings, + contextPopulateSiblings: ContextMenu.populateSiblings + } + }, getTopicProps: function() { const self = ReactApp return { diff --git a/frontend/src/Metamaps/Listeners.js b/frontend/src/Metamaps/Listeners.js index 352b5f7f..a5dc630f 100644 --- a/frontend/src/Metamaps/Listeners.js +++ b/frontend/src/Metamaps/Listeners.js @@ -152,12 +152,12 @@ const Listeners = { var node = nodes[nodes.length - 1] if (opts.center && opts.reveal) { Topic.centerOn(node.id, function() { - Topic.fetchRelatives(nodes) + Topic.fetchSiblings(nodes) }) } else if (opts.center) { Topic.centerOn(node.id) } else if (opts.reveal) { - Topic.fetchRelatives(nodes) + Topic.fetchSiblings(nodes) } } } diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 0054ad55..16b28570 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -74,7 +74,7 @@ const Topic = { } }, centerOn: function(nodeid, callback) { - // don't clash with fetchRelatives + // don't clash with fetchSiblings if (!Visualize.mGraph.busy) { Visualize.mGraph.onClick(nodeid, { hideLabels: false, @@ -100,7 +100,7 @@ const Topic = { } ReactApp.render() }, - fetchRelatives: function(nodes, metacodeId) { + fetchSiblings: function(nodes, metacodeId) { var self = this var node = $.isArray(nodes) ? nodes[0] : nodes @@ -156,7 +156,7 @@ const Topic = { }) }) if ($.isArray(nodes) && nodes.length > 1) { - self.fetchRelatives(nodes.slice(1), metacodeId) + self.fetchSiblings(nodes.slice(1), metacodeId) } } diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index be799b4b..427a50f6 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -1,6 +1,6 @@ import Control from '../Control' import DataModel from '../DataModel' -import ReactApp from '../GlobalUI/ReactApp' +import { ReactApp } from '../GlobalUI' import Selected from '../Selected' import Topic from '../Topic' @@ -8,11 +8,13 @@ const ContextMenu = { clickedNode: null, clickedEdge: null, pos: {x: 0, y: 0}, + fetchingSiblingsData: false, siblingsData: null, selectNode: (node, pos) => { ContextMenu.pos = pos ContextMenu.clickedNode = node ContextMenu.clickedEdge = null + ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ReactApp.render() }, @@ -20,16 +22,21 @@ const ContextMenu = { ContextMenu.pos = pos ContextMenu.clickedNode = null ContextMenu.clickedEdge = edge + ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ReactApp.render() }, reset: () => { + ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ContextMenu.clickedNode = null ContextMenu.clickedEdge = null ReactApp.render() }, - delete: Control.deleteSelected, + delete: () => { + Control.deleteSelected() + ContextMenu.reset() + }, remove: () => { Control.removeSelectedEdges() Control.removeSelectedNodes() @@ -66,39 +73,27 @@ const ContextMenu = { } ContextMenu.reset() }, - fetchRelatives: (node, metacodeId) => { - Topic.fetchRelatives(node, metacodeId) + fetchSiblings: (node, metacodeId) => { + Topic.fetchSiblings(node, metacodeId) ContextMenu.reset() }, populateSiblings: function(id) { // depending on how many topics are selected, do different things - - // add a loading icon for now - /*const loader = new CanvasLoader('loadingSiblings') - loader.setColor('#4FC059') // default is '#000000' - loader.setDiameter(15) // default is 40 - loader.setDensity(41) // default is 40 - loader.setRange(0.9) // default is 1.3 - loader.show() // Hidden by default*/ + ContextMenu.fetchingSiblingsData = true + ReactApp.render() const topics = DataModel.Topics.map(function(t) { return t.id }) const topicsString = topics.join() const successCallback = function(data) { + ContextMenu.fetchingSiblingsData = false + + // adjust the data for consumption by react + for (var key in data) { + data[key] = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` + } ContextMenu.siblingsData = data ReactApp.render() - /*$('#loadingSiblings').remove() - - for (var key in data) { - const string = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` - $('#fetchSiblingList').append(`
  • ${string}
  • `) - } - - $('.rc-siblings .getSiblings').click(function() { - $('.rightclickmenu').remove() - // data-id is a metacode id - Topic.fetchRelatives(node, $(this).attr('data-id')) - })*/ } $.ajax({ diff --git a/frontend/src/components/MapView/index.js b/frontend/src/components/MapView/index.js index fe8c23b1..3e51691d 100644 --- a/frontend/src/components/MapView/index.js +++ b/frontend/src/components/MapView/index.js @@ -1,6 +1,7 @@ -import React, { Component } from 'react' + import React, { Component } from 'react' import PropTypes from 'prop-types' +import ContextMenu from '../common/ContextMenu' import DataVis from '../common/DataVis' import UpperOptions from '../common/UpperOptions' import InfoAndHelp from '../common/InfoAndHelp' @@ -9,9 +10,11 @@ import VisualizationControls from '../common/VisualizationControls' import MapChat from './MapChat' import TopicCard from '../TopicCard' + export default class MapView extends Component { static propTypes = { + contextMenu: PropTypes.bool, mobile: PropTypes.bool, mapId: PropTypes.string, map: PropTypes.object, @@ -79,7 +82,8 @@ export default class MapView extends Component { filterAllMappers, filterAllSynapses, filterData, openImportLightbox, forkMap, openHelpLightbox, mapIsStarred, onMapStar, onMapUnstar, openTopic, - onZoomExtents, onZoomIn, onZoomOut, hasLearnedTopicCreation } = this.props + onZoomExtents, onZoomIn, onZoomOut, hasLearnedTopicCreation, + contextMenu } = this.props const { chatOpen } = this.state const onChatOpen = () => { this.setState({chatOpen: true}) @@ -109,6 +113,7 @@ export default class MapView extends Component { filterAllSynapses={filterAllSynapses} /> {openTopic && } + {contextMenu && } {currentUser && } {currentUser && this.mapChat = x} />} this.upperOptions = x} @@ -73,6 +75,7 @@ export default class TopicView extends Component { filterAllSynapses={filterAllSynapses} /> + {contextMenu && } { + if (!this.state.populateSiblingsSent) { + contextPopulateSiblings(contextNode.id) + this.setState({populateSiblingsSent: true}) + } + } + + // TODO: add checks for logged in and other context return
    • -
      Hide until refresh
      Ctrl+H
      +
      Hide until refresh +
      Ctrl+H
    • -
      Remove from map
      Ctrl+M
      +
      Remove from map +
      Ctrl+M
    • -
      Delete
      Ctrl+D
      +
      Delete +
      Ctrl+D
    • -
      Open in new tab +
      Open in new tab
    • -
      Change permissions +
      Change permissions
        -
      • commons
      • -
      • public
      • -
      • private
      • +
      • +
        commons +
      • +
      • +
        public +
      • +
      • +
        private +
      -
      +
    • +
      Change metacode +
      + +
      +
    • + {contextNode && topicId &&
    • +
      Reveal siblings +
        +
      • contextFetchSiblings(contextNode)}>All +
        Alt+R
        +
      • + {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { + return
      • contextFetchSiblings(contextNode, key)}> + {contextSiblingsData[key]} +
      • + })} + {contextFetchingSiblingsData &&
      • loading
      • } +
      +
      +
    • }
    From da9e44afa0b3256a307c6cbabcbfd2f64da2d80a Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 18 Sep 2017 23:49:35 -0400 Subject: [PATCH 06/40] clear the right click menu the right way --- frontend/src/Metamaps/JIT.js | 8 ++++---- frontend/src/Metamaps/Map/index.js | 3 ++- frontend/src/Metamaps/Topic.js | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 7116854f..57247ea0 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -350,7 +350,7 @@ const JIT = { // Add also a click handler to nodes onClick: function(node, eventInfo, e) { // remove the rightclickmenu - $('.rightclickmenu').remove() + ContextMenu.reset() if (Mouse.boxStartCoordinates) { if (e.ctrlKey) { @@ -391,7 +391,7 @@ const JIT = { // Add also a click handler to nodes onRightClick: function(node, eventInfo, e) { // remove the rightclickmenu - $('.rightclickmenu').remove() + ContextMenu.reset() if (Mouse.boxStartCoordinates) { Create.newSynapse.hide() @@ -1007,7 +1007,7 @@ const JIT = { TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide() - $('.rightclickmenu').remove() + ContextMenu.reset() // reset the draw synapse positions to false Mouse.synapseStartCoordinates = [] Mouse.synapseEndCoordinates = null @@ -1412,7 +1412,7 @@ const JIT = { Control.selectEdge(adj) // delete old right click menu - $('.rightclickmenu').remove() + ContextMenu.reset() // create new menu for clicked on node const rightclickmenu = document.createElement('div') rightclickmenu.className = 'rightclickmenu' diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index 2db12263..0e3360ac 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -16,6 +16,7 @@ import Loading from '../Loading' import Realtime from '../Realtime' import Selected from '../Selected' import SynapseCard from '../SynapseCard' +import ContextMenu from '../Views/ContextMenu' import TopicCard from '../Views/TopicCard' import Visualize from '../Visualize' @@ -137,7 +138,7 @@ const Map = { if (Active.Map) { $('.main').removeClass('compressed') AutoLayout.resetSpiral() - $('.rightclickmenu').remove() + ContextMenu.reset() TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide(true) // true means force (and override pinned) diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 16b28570..976bb82d 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -15,6 +15,7 @@ import Selected from './Selected' import Settings from './Settings' import SynapseCard from './SynapseCard' import TopicCard from './Views/TopicCard' +import ContextMenu from './Views/ContextMenu' import Util from './Util' import Visualize from './Visualize' @@ -68,7 +69,7 @@ const Topic = { }, end: function() { if (Active.Topic) { - $('.rightclickmenu').remove() + ContextMenu.reset() TopicCard.hideCard() SynapseCard.hideCard() } From a04cd0d395f082d499274540da8896c11b2eb874 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 00:34:37 -0400 Subject: [PATCH 07/40] call the right callbacks and show in the right context --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 1 - frontend/src/components/common/ContextMenu.js | 61 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 5290fe12..6087960e 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -167,7 +167,6 @@ const ReactApp = { contextFetchingSiblingsData: ContextMenu.fetchingSiblingsData, contextSiblingsData: ContextMenu.siblingsData, // functions - contextReset: ContextMenu.reset, contextDelete: ContextMenu.delete, contextRemove: ContextMenu.remove, contextHide: ContextMenu.hide, diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 84c2b44d..1d4b6687 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -6,13 +6,15 @@ import MetacodeSelect from '../MetacodeSelect' class ContextMenu extends Component { static propTypes = { topicId: PropTypes.string, + mapId: PropTypes.string, + currentUser: PropTypes.object, + map: PropTypes.object, contextNode: PropTypes.object, contextEdge: PropTypes.object, contextPos: PropTypes.object, contextFetchingSiblingsData: PropTypes.bool, contextSiblingsData: PropTypes.object, metacodeSets: PropTypes.array, - contextReset: PropTypes.func, contextDelete: PropTypes.func, contextRemove: PropTypes.func, contextHide: PropTypes.func, @@ -33,9 +35,12 @@ class ContextMenu extends Component { render () { const { contextNode, contextPos, contextOnMetacodeSelect, metacodeSets, - contextSiblingsData, contextFetchSiblings, - contextPopulateSiblings, contextFetchingSiblingsData, - topicId } = this.props + contextDelete, contextHide, contextRemove, contextCenterOn, + contextPopoutTopic, contextSiblingsData, contextFetchSiblings, + currentUser, contextPopulateSiblings, contextFetchingSiblingsData, + topicId, map, mapId, contextUpdatePermissions } = this.props + + const canEditMap = map && map.authorizeToEdit(currentUser) const style = { position: 'absolute', top: contextPos.y + 'px', @@ -49,48 +54,60 @@ class ContextMenu extends Component { } } - // TODO: add checks for logged in and other context return
      -
    • +
    • Hide until refresh
      Ctrl+H
    • -
    • + {canEditMap &&
    • Remove from map
      Ctrl+M
      -
    • -
    • +
    • } + {canEditMap &&
    • Delete
      Ctrl+D
      -
    • -
    • +
    • } + {contextNode && topicId &&
    • contextCenterOn(contextNode.id)}> +
      Center this topic +
      Alt+E
      +
    • } + {contextNode &&
    • contextPopoutTopic(contextNode.id)}>
      Open in new tab -
    • -
    • -
    • +
    • } + {(currentUser || (contextNode && topicId)) &&
    • +
    • } + {currentUser &&
    • Change permissions
        -
      • +
      • contextUpdatePermissions('commons')}>
        commons
      • -
      • +
      • contextUpdatePermissions('public')}>
        public
      • -
      • +
      • contextUpdatePermissions('private')}>
        private
      -
    • -
    • +
    • } + {currentUser &&
    • Change metacode
      - { + contextOnMetacodeSelect(contextNode && contextNode.id, id) + }} metacodeSets={metacodeSets} />
      -
    • + } {contextNode && topicId &&
    • Reveal siblings @@ -105,7 +122,7 @@ class ContextMenu extends Component { {contextSiblingsData[key]}
    • })} - {contextFetchingSiblingsData &&
    • loading
    • } + {contextFetchingSiblingsData &&
    • loading...
    • }
    } From 25150733932eca46a509b7bbd8615cc51d87f7e5 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 00:46:23 -0400 Subject: [PATCH 08/40] replace old edgeRightClick code --- frontend/src/Metamaps/JIT.js | 103 +---------------------------------- 1 file changed, 2 insertions(+), 101 deletions(-) diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 57247ea0..716dcb27 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -1399,113 +1399,14 @@ const JIT = { } }, // selectEdgeOnClickHandler selectEdgeOnRightClickHandler: function(adj, e) { - // the 'node' variable is a JIT node, the one that was clicked on + // the 'adj' variable is a JIT adjacency, the one that was clicked on // the 'e' variable is the click event - if (adj.getData('alpha') === 0) return // don't do anything if the edge is filtered - e.preventDefault() e.stopPropagation() - if (Visualize.mGraph.busy) return - Control.selectEdge(adj) - - // delete old right click menu - ContextMenu.reset() - // create new menu for clicked on node - const rightclickmenu = document.createElement('div') - rightclickmenu.className = 'rightclickmenu' - // prevent the custom context menu from immediately opening the default context menu as well - rightclickmenu.setAttribute('oncontextmenu', 'return false') - - // add the proper options to the menu - let menustring = '
      ' - - const authorized = Active.Map && Active.Map.authorizeToEdit(Active.Mapper) - - const disabled = authorized ? '' : 'disabled' - - if (Active.Map) menustring += '
    • Hide until refresh
      Ctrl+H
    • ' - if (Active.Map && Active.Mapper) menustring += '
    • Remove from map
      Ctrl+M
    • ' - if (Active.Topic) menustring += '
    • Remove from view
      Ctrl+M
    • ' - if (Active.Map && Active.Mapper) menustring += '
    • Delete
      Ctrl+D
    • ' - - if (Active.Map && Active.Mapper) menustring += '
    • ' - - if (Active.Mapper) { - const permOptions = outdent` -
        -
      • commons
      • -
      • public
      • private
      ` - - menustring += '
    • Change permissions' + permOptions + '
    • ' - } - - menustring += '
    ' - rightclickmenu.innerHTML = menustring - - // position the menu where the click happened - const position = {} - const RIGHTCLICK_WIDTH = 300 - const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static - const SUBMENUS_WIDTH = 256 - const MAX_SUBMENU_HEIGHT = 270 - const windowWidth = $(window).width() - const windowHeight = $(window).height() - - if (windowWidth - e.clientX < SUBMENUS_WIDTH) { - position.right = windowWidth - e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { - position.right = windowWidth - e.clientX - } else position.left = e.clientX - - if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { - position.bottom = windowHeight - e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { - position.top = e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else position.top = e.clientY - - $(rightclickmenu).css(position) - - // add the menu to the page - $('#wrapper').append(rightclickmenu) - - // attach events to clicks on the list items - - // delete the selected things from the database - if (authorized) { - $('.rc-delete').click(function() { - $('.rightclickmenu').remove() - Control.deleteSelected() - }) - } - - // remove the selected things from the map - if (authorized) { - $('.rc-remove').click(function() { - $('.rightclickmenu').remove() - Control.removeSelectedEdges() - Control.removeSelectedNodes() - }) - } - - // hide selected nodes and synapses until refresh - $('.rc-hide').click(function() { - $('.rightclickmenu').remove() - Control.hideSelectedEdges() - Control.hideSelectedNodes() - }) - - // change the permission of all the selected nodes and synapses that you were the originator of - $('.rc-permission li').click(function() { - $('.rightclickmenu').remove() - // $(this).text() will be 'commons' 'public' or 'private' - Control.updateSelectedPermissions($(this).text()) - }) + ContextMenu.selectEdge(adj, {x: e.clientX, y: e.clientY}) }, // selectEdgeOnRightClickHandler SmoothPanning: function() { const sx = Visualize.mGraph.canvas.scaleOffsetX From 5f1fe4dc3f86421947a26faae576d380e3f1eeec Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 08:37:00 -0400 Subject: [PATCH 09/40] reimplement shifting menu according to click position --- frontend/src/components/common/ContextMenu.js | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 1d4b6687..273672b5 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -33,6 +33,49 @@ class ContextMenu extends Component { } } + getPositionData = () => { + const { contextPos } = this.props + let extraClasses = [] + const position = {} + const RIGHTCLICK_WIDTH = 300 + const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static + const SUBMENUS_WIDTH = 256 + const MAX_SUBMENU_HEIGHT = 270 + const windowWidth = document.documentElement.clientWidth + const windowHeight = document.documentElement.clientHeight + + if (windowWidth - contextPos.x < SUBMENUS_WIDTH) { + position.right = windowWidth - contextPos.x + extraClasses.push('moveMenusToLeft') + } else if (windowWidth - contextPos.x < RIGHTCLICK_WIDTH) { + position.right = windowWidth - contextPos.x + } else if (windowWidth - contextPos.x < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { + position.left = contextPos.x + extraClasses.push('moveMenusToLeft') + } else { + position.left = contextPos.x + } + + if (windowHeight - contextPos.y < MAX_SUBMENU_HEIGHT) { + position.bottom = windowHeight - contextPos.y + extraClasses.push('moveMenusUp') + } else if (windowHeight - contextPos.y < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { + position.top = contextPos.y + extraClasses.push('moveMenusUp') + } else { + position.top = contextPos.y + } + return { + pos: { + top: position.top && position.top + 'px', + bottom: position.bottom && position.bottom + 'px', + left: position.left && position.left + 'px', + right: position.right && position.right + 'px' + }, + extraClasses + } + } + render () { const { contextNode, contextPos, contextOnMetacodeSelect, metacodeSets, contextDelete, contextHide, contextRemove, contextCenterOn, @@ -41,12 +84,8 @@ class ContextMenu extends Component { topicId, map, mapId, contextUpdatePermissions } = this.props const canEditMap = map && map.authorizeToEdit(currentUser) - const style = { - position: 'absolute', - top: contextPos.y + 'px', - left: contextPos.x + 'px' - } - + const positionData = this.getPositionData() + const style = Object.assign({}, {position: 'absolute'}, positionData.pos) const populateSiblings = () => { if (!this.state.populateSiblingsSent) { contextPopulateSiblings(contextNode.id) @@ -54,7 +93,8 @@ class ContextMenu extends Component { } } - return
    + return
    • Hide until refresh @@ -128,37 +168,6 @@ class ContextMenu extends Component {
    • }
    - - // position the menu where the click happened - /*const position = {} - const RIGHTCLICK_WIDTH = 300 - const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static - const SUBMENUS_WIDTH = 256 - const MAX_SUBMENU_HEIGHT = 270 - const windowWidth = $(window).width() - const windowHeight = $(window).height() - - if (windowWidth - e.clientX < SUBMENUS_WIDTH) { - position.right = windowWidth - e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH) { - position.right = windowWidth - e.clientX - } else if (windowWidth - e.clientX < RIGHTCLICK_WIDTH + SUBMENUS_WIDTH) { - position.left = e.clientX - $(rightclickmenu).addClass('moveMenusToLeft') - } else { - position.left = e.clientX - } - - if (windowHeight - e.clientY < MAX_SUBMENU_HEIGHT) { - position.bottom = windowHeight - e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else if (windowHeight - e.clientY < RIGHTCLICK_HEIGHT + MAX_SUBMENU_HEIGHT) { - position.top = e.clientY - $(rightclickmenu).addClass('moveMenusUp') - } else { - position.top = e.clientY - }*/ } } From a4c905df4e1cd04efbcdb4780108fab665771d9f Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 09:04:21 -0400 Subject: [PATCH 10/40] move each li into its own function --- frontend/src/components/common/ContextMenu.js | 216 +++++++++++------- 1 file changed, 138 insertions(+), 78 deletions(-) diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 273672b5..b5431c56 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -76,96 +76,156 @@ class ContextMenu extends Component { } } - render () { - const { contextNode, contextPos, contextOnMetacodeSelect, metacodeSets, - contextDelete, contextHide, contextRemove, contextCenterOn, - contextPopoutTopic, contextSiblingsData, contextFetchSiblings, - currentUser, contextPopulateSiblings, contextFetchingSiblingsData, - topicId, map, mapId, contextUpdatePermissions } = this.props + hide = () => { + const { contextHide } = this.props + return
  • +
    Hide until refresh +
    Ctrl+H
    +
  • + } + remove = () => { + const { contextRemove, map, currentUser } = this.props const canEditMap = map && map.authorizeToEdit(currentUser) - const positionData = this.getPositionData() - const style = Object.assign({}, {position: 'absolute'}, positionData.pos) + if (!canEditMap) { + return null + } + return
  • +
    Remove from map +
    Ctrl+M
    +
  • + } + + delete = () => { + const { contextDelete, map, currentUser } = this.props + const canEditMap = map && map.authorizeToEdit(currentUser) + if (!canEditMap) { + return null + } + return
  • +
    Delete +
    Ctrl+D
    +
  • + } + + center = () => { + const { contextCenterOn, contextNode, topicId } = this.props + if (!(contextNode && topicId)) { + return null + } + return
  • contextCenterOn(contextNode.id)}> +
    Center this topic +
    Alt+E
    +
  • + } + + popout = () => { + const { contextPopoutTopic, contextNode } = this.props + if (!contextNode) { + return null + } + return
  • contextPopoutTopic(contextNode.id)}> +
    Open in new tab +
  • + } + + permission = () => { + const { currentUser, contextUpdatePermissions } = this.props + if (!currentUser) { + return null + } + return
  • +
    Change permissions +
      +
    • contextUpdatePermissions('commons')}> +
      commons +
    • +
    • contextUpdatePermissions('public')}> +
      public +
    • +
    • contextUpdatePermissions('private')}> +
      private +
    • +
    +
    +
  • + } + + metacode = () => { + const { metacodeSets, contextOnMetacodeSelect, + currentUser, contextNode } = this.props + if (!currentUser) { + return null + } + return
  • +
    Change metacode +
    + { + contextOnMetacodeSelect(contextNode && contextNode.id, id) + }} + metacodeSets={metacodeSets} /> +
    +
    +
  • + } + + siblings = () => { + const { contextPopulateSiblings, contextFetchSiblings, + contextSiblingsData, contextFetchingSiblingsData, + topicId, contextNode } = this.props const populateSiblings = () => { if (!this.state.populateSiblingsSent) { contextPopulateSiblings(contextNode.id) this.setState({populateSiblingsSent: true}) } } + if (!(contextNode && topicId)) { + return null + } + return
  • +
    Reveal siblings +
      +
    • contextFetchSiblings(contextNode)}>All +
      Alt+R
      +
    • + {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { + return
    • contextFetchSiblings(contextNode, key)}> + {contextSiblingsData[key]} +
    • + })} + {contextFetchingSiblingsData &&
    • loading...
    • } +
    +
    +
  • + } + + render () { + const { contextNode, currentUser, topicId } = this.props + const positionData = this.getPositionData() + const style = Object.assign({}, {position: 'absolute'}, positionData.pos) + const showSpacer = currentUser || (contextNode && topicId) return
      -
    • -
      Hide until refresh -
      Ctrl+H
      -
    • - {canEditMap &&
    • -
      Remove from map -
      Ctrl+M
      -
    • } - {canEditMap &&
    • -
      Delete -
      Ctrl+D
      -
    • } - {contextNode && topicId &&
    • contextCenterOn(contextNode.id)}> -
      Center this topic -
      Alt+E
      -
    • } - {contextNode &&
    • contextPopoutTopic(contextNode.id)}> -
      Open in new tab -
    • } - {(currentUser || (contextNode && topicId)) &&
    • -
    • } - {currentUser &&
    • -
      Change permissions -
        -
      • contextUpdatePermissions('commons')}> -
        commons -
      • -
      • contextUpdatePermissions('public')}> -
        public -
      • -
      • contextUpdatePermissions('private')}> -
        private -
      • -
      -
      -
    • } - {currentUser &&
    • -
      Change metacode -
      - { - contextOnMetacodeSelect(contextNode && contextNode.id, id) - }} - metacodeSets={metacodeSets} /> -
      -
      -
    • } - {contextNode && topicId &&
    • -
      Reveal siblings -
        -
      • contextFetchSiblings(contextNode)}>All -
        Alt+R
        -
      • - {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { - return
      • contextFetchSiblings(contextNode, key)}> - {contextSiblingsData[key]} -
      • - })} - {contextFetchingSiblingsData &&
      • loading...
      • } -
      -
      -
    • } + {this.hide()} + {this.remove()} + {this.delete()} + {this.center()} + {this.popout()} + {showSpacer &&
    • } + {this.permission()} + {this.metacode()} + {this.siblings()}
    } From 3406f2e04dc7011ce60a3f63cabdb350a991abce Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 09:58:59 -0400 Subject: [PATCH 11/40] make render a callback and use async apply for ease of use --- frontend/src/Metamaps/GlobalUI/ReactApp.js | 21 ++++----- frontend/src/Metamaps/JIT.js | 10 ++--- frontend/src/Metamaps/Map/index.js | 2 +- frontend/src/Metamaps/Topic.js | 2 +- frontend/src/Metamaps/Views/ContextMenu.js | 51 +++++++++++----------- package.json | 1 + 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 6087960e..5cfbf255 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -4,6 +4,7 @@ import React from 'react' import ReactDOM from 'react-dom' import { Router, browserHistory } from 'react-router' import { merge } from 'lodash' +import apply from 'async/apply' import { notifyUser } from './index.js' import ImportDialog from './ImportDialog' @@ -157,7 +158,7 @@ const ReactApp = { } }, getContextMenuProps: function() { - const self = ReactApp + const { render } = ReactApp return { // values contextMenu: !!(ContextMenu.clickedNode || ContextMenu.clickedEdge), @@ -167,15 +168,15 @@ const ReactApp = { contextFetchingSiblingsData: ContextMenu.fetchingSiblingsData, contextSiblingsData: ContextMenu.siblingsData, // functions - contextDelete: ContextMenu.delete, - contextRemove: ContextMenu.remove, - contextHide: ContextMenu.hide, - contextCenterOn: ContextMenu.centerOn, - contextPopoutTopic: ContextMenu.popoutTopic, - contextUpdatePermissions: ContextMenu.updatePermissions, - contextOnMetacodeSelect: ContextMenu.onMetacodeSelect, - contextFetchSiblings: ContextMenu.fetchSiblings, - contextPopulateSiblings: ContextMenu.populateSiblings + contextDelete: apply(ContextMenu.delete, render), + contextRemove: apply(ContextMenu.remove, render), + contextHide: apply(ContextMenu.hide, render), + contextCenterOn: apply(ContextMenu.centerOn, render), + contextPopoutTopic: apply(ContextMenu.popoutTopic, render), + contextUpdatePermissions: apply(ContextMenu.updatePermissions, render), + contextOnMetacodeSelect: apply(ContextMenu.onMetacodeSelect, render), + contextFetchSiblings: apply(ContextMenu.fetchSiblings, render), + contextPopulateSiblings: apply(ContextMenu.populateSiblings, render) } }, getTopicProps: function() { diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index 716dcb27..c8da91f8 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -350,7 +350,7 @@ const JIT = { // Add also a click handler to nodes onClick: function(node, eventInfo, e) { // remove the rightclickmenu - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) if (Mouse.boxStartCoordinates) { if (e.ctrlKey) { @@ -391,7 +391,7 @@ const JIT = { // Add also a click handler to nodes onRightClick: function(node, eventInfo, e) { // remove the rightclickmenu - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) if (Mouse.boxStartCoordinates) { Create.newSynapse.hide() @@ -1007,7 +1007,7 @@ const JIT = { TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide() - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) // reset the draw synapse positions to false Mouse.synapseStartCoordinates = [] Mouse.synapseEndCoordinates = null @@ -1355,7 +1355,7 @@ const JIT = { // select the node Control.selectNode(node, e) - ContextMenu.selectNode(node, {x: e.clientX, y: e.clientY}) + ContextMenu.selectNode(ReactApp.render, node, {x: e.clientX, y: e.clientY}) }, // selectNodeOnRightClickHandler, selectEdgeOnClickHandler: function(adj, e) { @@ -1406,7 +1406,7 @@ const JIT = { e.stopPropagation() if (Visualize.mGraph.busy) return Control.selectEdge(adj) - ContextMenu.selectEdge(adj, {x: e.clientX, y: e.clientY}) + ContextMenu.selectEdge(ReactApp.render, adj, {x: e.clientX, y: e.clientY}) }, // selectEdgeOnRightClickHandler SmoothPanning: function() { const sx = Visualize.mGraph.canvas.scaleOffsetX diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index 0e3360ac..f337d1d0 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -138,7 +138,7 @@ const Map = { if (Active.Map) { $('.main').removeClass('compressed') AutoLayout.resetSpiral() - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) TopicCard.hideCard() SynapseCard.hideCard() Create.newTopic.hide(true) // true means force (and override pinned) diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 976bb82d..1a8a7414 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -69,7 +69,7 @@ const Topic = { }, end: function() { if (Active.Topic) { - ContextMenu.reset() + ContextMenu.reset(ReactApp.render) TopicCard.hideCard() SynapseCard.hideCard() } diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 427a50f6..0eae9b88 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -1,6 +1,5 @@ import Control from '../Control' import DataModel from '../DataModel' -import { ReactApp } from '../GlobalUI' import Selected from '../Selected' import Topic from '../Topic' @@ -10,58 +9,58 @@ const ContextMenu = { pos: {x: 0, y: 0}, fetchingSiblingsData: false, siblingsData: null, - selectNode: (node, pos) => { + selectNode: (render, node, pos) => { ContextMenu.pos = pos ContextMenu.clickedNode = node ContextMenu.clickedEdge = null ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null - ReactApp.render() + render() }, - selectEdge: (edge, pos) => { + selectEdge: (render, edge, pos) => { ContextMenu.pos = pos ContextMenu.clickedNode = null ContextMenu.clickedEdge = edge ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null - ReactApp.render() + render() }, - reset: () => { + reset: (render) => { ContextMenu.fetchingSiblingsData = false ContextMenu.siblingsData = null ContextMenu.clickedNode = null ContextMenu.clickedEdge = null - ReactApp.render() + render() }, - delete: () => { + delete: (render) => { Control.deleteSelected() - ContextMenu.reset() + ContextMenu.reset(render) }, - remove: () => { + remove: (render) => { Control.removeSelectedEdges() Control.removeSelectedNodes() - ContextMenu.reset() + ContextMenu.reset(render) }, - hide: () => { + hide: (render) => { Control.hideSelectedEdges() Control.hideSelectedNodes() - ContextMenu.reset() + ContextMenu.reset(render) }, - centerOn: (id) => { + centerOn: (render, id) => { Topic.centerOn(id) - ContextMenu.reset() + ContextMenu.reset(render) }, - popoutTopic: (id) => { - ContextMenu.reset() + popoutTopic: (render, id) => { + ContextMenu.reset(render) const win = window.open('/topics/' + id, '_blank') win.focus() }, - updatePermissions: (permission) => { + updatePermissions: (render, permission) => { // will be 'commons' 'public' or 'private' Control.updateSelectedPermissions(permission) - ContextMenu.reset() + ContextMenu.reset(render) }, - onMetacodeSelect: (id, metacodeId) => { + onMetacodeSelect: (render, id, metacodeId) => { if (Selected.Nodes.length > 1) { // batch update multiple topics Control.updateSelectedMetacodes(metacodeId) @@ -71,16 +70,16 @@ const ContextMenu = { metacode_id: metacodeId }) } - ContextMenu.reset() + ContextMenu.reset(render) }, - fetchSiblings: (node, metacodeId) => { + fetchSiblings: (render, node, metacodeId) => { Topic.fetchSiblings(node, metacodeId) - ContextMenu.reset() + ContextMenu.reset(render) }, - populateSiblings: function(id) { + populateSiblings: (render, id) => { // depending on how many topics are selected, do different things ContextMenu.fetchingSiblingsData = true - ReactApp.render() + render() const topics = DataModel.Topics.map(function(t) { return t.id }) const topicsString = topics.join() @@ -93,7 +92,7 @@ const ContextMenu = { data[key] = `${DataModel.Metacodes.get(key).get('name')} (${data[key]})` } ContextMenu.siblingsData = data - ReactApp.render() + render() } $.ajax({ diff --git a/package.json b/package.json index f0afc5f7..3be728c4 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "homepage": "https://github.com/metamaps/metamaps#readme", "dependencies": { "ajaxq": "0.0.7", + "async": "2.5.0", "attachmediastream": "2.0.0", "autolinker": "1.4.3", "babel-cli": "6.26.0", From c4d0bf8ce4065bd300bac160dfc8b68745bdcbc6 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 10:02:05 -0400 Subject: [PATCH 12/40] clean up jquery ref and strings --- frontend/src/Metamaps/Topic.js | 4 ++-- frontend/src/Metamaps/Views/ContextMenu.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 1a8a7414..1b1abe88 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -104,7 +104,7 @@ const Topic = { fetchSiblings: function(nodes, metacodeId) { var self = this - var node = $.isArray(nodes) ? nodes[0] : nodes + var node = Array.isArray(nodes) ? nodes[0] : nodes var topics = DataModel.Topics.map(function(t) { return t.id }) var topicsString = topics.join() @@ -156,7 +156,7 @@ const Topic = { } }) }) - if ($.isArray(nodes) && nodes.length > 1) { + if (Array.isArray(nodes) && nodes.length > 1) { self.fetchSiblings(nodes.slice(1), metacodeId) } } diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 0eae9b88..3aa9784a 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -52,7 +52,7 @@ const ContextMenu = { }, popoutTopic: (render, id) => { ContextMenu.reset(render) - const win = window.open('/topics/' + id, '_blank') + const win = window.open(`/topics/${id}`, '_blank') win.focus() }, updatePermissions: (render, permission) => { From 0d6963ebb00cc6e9769f7c9aab2bbeca6d112e37 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 10:16:14 -0400 Subject: [PATCH 13/40] eslint --- frontend/src/Metamaps/JIT.js | 12 +-- frontend/src/Metamaps/Views/ContextMenu.js | 1 + frontend/src/components/MapView/index.js | 3 +- frontend/src/components/common/ContextMenu.js | 74 +++++++++---------- 4 files changed, 40 insertions(+), 50 deletions(-) diff --git a/frontend/src/Metamaps/JIT.js b/frontend/src/Metamaps/JIT.js index c8da91f8..b0aee7ea 100644 --- a/frontend/src/Metamaps/JIT.js +++ b/frontend/src/Metamaps/JIT.js @@ -1,15 +1,10 @@ -/* global $, Image, CanvasLoader */ +/* global $, Image */ import _ from 'lodash' -import outdent from 'outdent' import clipboard from 'clipboard-js' -import React from 'react' -import ReactDOM from 'react-dom' import $jit from '../patched/JIT' -import MetacodeSelect from '../components/MetacodeSelect' - import Active from './Active' import ContextMenu from './Views/ContextMenu' import Control from './Control' @@ -1347,16 +1342,11 @@ const JIT = { selectNodeOnRightClickHandler: function(node, e) { // the 'node' variable is a JIT node, the one that was clicked on // the 'e' variable is the click event - e.preventDefault() e.stopPropagation() - if (Visualize.mGraph.busy) return - - // select the node Control.selectNode(node, e) ContextMenu.selectNode(ReactApp.render, node, {x: e.clientX, y: e.clientY}) - }, // selectNodeOnRightClickHandler, selectEdgeOnClickHandler: function(adj, e) { if (Visualize.mGraph.busy) return diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 3aa9784a..74fa1751 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -1,3 +1,4 @@ +/* global $ */ import Control from '../Control' import DataModel from '../DataModel' import Selected from '../Selected' diff --git a/frontend/src/components/MapView/index.js b/frontend/src/components/MapView/index.js index 3e51691d..d2cc90fd 100644 --- a/frontend/src/components/MapView/index.js +++ b/frontend/src/components/MapView/index.js @@ -1,4 +1,4 @@ - import React, { Component } from 'react' +import React, { Component } from 'react' import PropTypes from 'prop-types' import ContextMenu from '../common/ContextMenu' @@ -10,7 +10,6 @@ import VisualizationControls from '../common/VisualizationControls' import MapChat from './MapChat' import TopicCard from '../TopicCard' - export default class MapView extends Component { static propTypes = { diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index b5431c56..5ffc7755 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -78,9 +78,9 @@ class ContextMenu extends Component { hide = () => { const { contextHide } = this.props - return
  • -
    Hide until refresh -
    Ctrl+H
    + return
  • +
    Hide until refresh +
    Ctrl+H
  • } @@ -90,9 +90,9 @@ class ContextMenu extends Component { if (!canEditMap) { return null } - return
  • -
    Remove from map -
    Ctrl+M
    + return
  • +
    Remove from map +
    Ctrl+M
  • } @@ -102,9 +102,9 @@ class ContextMenu extends Component { if (!canEditMap) { return null } - return
  • -
    Delete -
    Ctrl+D
    + return
  • +
    Delete +
    Ctrl+D
  • } @@ -113,10 +113,10 @@ class ContextMenu extends Component { if (!(contextNode && topicId)) { return null } - return
  • contextCenterOn(contextNode.id)}> -
    Center this topic -
    Alt+E
    +
    Center this topic +
    Alt+E
  • } @@ -125,9 +125,9 @@ class ContextMenu extends Component { if (!contextNode) { return null } - return
  • contextPopoutTopic(contextNode.id)}> -
    Open in new tab +
    Open in new tab
  • } @@ -136,23 +136,23 @@ class ContextMenu extends Component { if (!currentUser) { return null } - return
  • -
    Change permissions + return
  • +
    Change permissions
      -
    • contextUpdatePermissions('commons')}> -
      commons +
      commons
    • -
    • contextUpdatePermissions('public')}> -
      public +
      public
    • -
    • contextUpdatePermissions('private')}> -
      private +
      private
    -
    +
  • } @@ -162,16 +162,16 @@ class ContextMenu extends Component { if (!currentUser) { return null } - return
  • -
    Change metacode -
    + return
  • +
    Change metacode +
    { contextOnMetacodeSelect(contextNode && contextNode.id, id) }} metacodeSets={metacodeSets} />
    -
    +
  • } @@ -188,13 +188,13 @@ class ContextMenu extends Component { if (!(contextNode && topicId)) { return null } - return
  • -
    Reveal siblings -
      -
    • Reveal siblings +
        +
      • contextFetchSiblings(contextNode)}>All -
        Alt+R
        +
        Alt+R
      • {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { return
      • })} - {contextFetchingSiblingsData &&
      • loading...
      • } + {contextFetchingSiblingsData &&
      • loading...
      • }
      -
      +
    • } - render () { + render() { const { contextNode, currentUser, topicId } = this.props const positionData = this.getPositionData() const style = Object.assign({}, {position: 'absolute'}, positionData.pos) const showSpacer = currentUser || (contextNode && topicId) return
      + className={'rightclickmenu ' + positionData.extraClasses.join(' ')}>
        {this.hide()} {this.remove()} {this.delete()} {this.center()} {this.popout()} - {showSpacer &&
      • } + {showSpacer &&
      • } {this.permission()} {this.metacode()} {this.siblings()} From 3c7c8812a4d384a9d9c1f276c2fe353289af666a Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 11:46:19 -0400 Subject: [PATCH 14/40] tempplate strings and new lines --- frontend/src/Metamaps/Views/ContextMenu.js | 2 +- frontend/src/components/common/ContextMenu.js | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/frontend/src/Metamaps/Views/ContextMenu.js b/frontend/src/Metamaps/Views/ContextMenu.js index 74fa1751..f1553706 100644 --- a/frontend/src/Metamaps/Views/ContextMenu.js +++ b/frontend/src/Metamaps/Views/ContextMenu.js @@ -98,7 +98,7 @@ const ContextMenu = { $.ajax({ type: 'GET', - url: '/topics/' + id + '/relative_numbers.json?network=' + topicsString, + url: `/topics/${id}/relative_numbers.json?network=${topicsString}`, success: successCallback, error: function() {} }) diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/common/ContextMenu.js index 5ffc7755..e3649e5d 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/common/ContextMenu.js @@ -37,6 +37,8 @@ class ContextMenu extends Component { const { contextPos } = this.props let extraClasses = [] const position = {} + // TODO: make these dynamic values so that the ContextMenu can + // change height and still work properly const RIGHTCLICK_WIDTH = 300 const RIGHTCLICK_HEIGHT = 144 // this does vary somewhat, but we can use static const SUBMENUS_WIDTH = 256 @@ -79,8 +81,9 @@ class ContextMenu extends Component { hide = () => { const { contextHide } = this.props return
      • -
        Hide until refresh -
        Ctrl+H
        +
        + Hide until refresh +
        Ctrl+H
      • } @@ -91,8 +94,9 @@ class ContextMenu extends Component { return null } return
      • -
        Remove from map -
        Ctrl+M
        +
        + Remove from map +
        Ctrl+M
      • } @@ -103,7 +107,8 @@ class ContextMenu extends Component { return null } return
      • -
        Delete +
        + Delete
        Ctrl+D
      • } @@ -115,7 +120,8 @@ class ContextMenu extends Component { } return
      • contextCenterOn(contextNode.id)}> -
        Center this topic +
        + Center this topic
        Alt+E
      • } @@ -127,7 +133,8 @@ class ContextMenu extends Component { } return
      • contextPopoutTopic(contextNode.id)}> -
        Open in new tab +
        + Open in new tab
      • } @@ -137,19 +144,23 @@ class ContextMenu extends Component { return null } return
      • -
        Change permissions +
        + Change permissions
        • contextUpdatePermissions('commons')}> -
          commons +
          + commons
        • contextUpdatePermissions('public')}> -
          public +
          + public
        • contextUpdatePermissions('private')}> -
          private +
          + private
        @@ -163,7 +174,8 @@ class ContextMenu extends Component { return null } return
      • -
        Change metacode +
        + Change metacode
        { @@ -190,10 +202,12 @@ class ContextMenu extends Component { } return
      • -
        Reveal siblings +
        + Reveal siblings
        • contextFetchSiblings(contextNode)}>All + onClick={() => contextFetchSiblings(contextNode)}> + All
          Alt+R
        • {contextSiblingsData && Object.keys(contextSiblingsData).map(key => { From 64ffc78f45e334dce9026c701e210cb31075b396 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Sep 2017 23:48:46 -0400 Subject: [PATCH 15/40] add the container for the notifications dropdown --- app/assets/stylesheets/notifications.scss.erb | 55 ++++++++++++++++--- app/controllers/notifications_controller.rb | 9 ++- .../src/Metamaps/GlobalUI/Notifications.js | 14 +++++ frontend/src/Metamaps/GlobalUI/ReactApp.js | 5 +- .../src/components/App/NotificationBox.js | 41 ++++++++++++++ .../src/components/App/NotificationIcon.js | 16 ++---- frontend/src/components/App/UpperRightUI.js | 38 +++++++++++-- frontend/src/components/App/index.js | 6 +- 8 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 frontend/src/Metamaps/GlobalUI/Notifications.js create mode 100644 frontend/src/components/App/NotificationBox.js diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index 2f750f90..95524caa 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -13,6 +13,45 @@ $unread_notifications_dot_size: 8px; .notificationsIcon { position: relative; } + + .notificationsBox { + position: absolute; + background: #FFFFFF; + border-radius: 2px; + width: 350px; + right: 0; + top: 50px; + box-shadow: 0 3px 6px rgba(0,0,0,0.16); + + .notificationsBoxTriangle { + min-width: 0 !important; + display: block; + position: absolute; + right: 48px; + width: 20px !important; + height: 20px !important; + margin-left: -10px; + top: -10px; + border-top: 1px solid rgba(49, 72, 67, 0.11) !important; + border-left: 1px solid rgba(49, 72, 67, 0.11) !important; + border-bottom: 0 !important; + border-right: 0 !important; + background-color: #fff; + transform: rotate(45deg); + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + } + + .notificationsBoxSeeAll { + display: block; + width: 100%; + text-align: center; + padding: 6px 0; + font-family: din-regular, helvetica, sans-serif; + color: #4fb5c0; + border-top: 1px solid rgba(0, 0, 0, 0.1); + } + } } .controller-notifications { @@ -66,12 +105,12 @@ $unread_notifications_dot_size: 8px; & > a { float: left; - width: 85%; + width: 85%; box-sizing: border-box; padding-right: 10px; } - .notification-actor { + .notification-actor { float: left; img { @@ -97,7 +136,7 @@ $unread_notifications_dot_size: 8px; display: inline-block; margin: 5px 0; } - } + } .notification-date { position: absolute; @@ -129,7 +168,7 @@ $unread_notifications_dot_size: 8px; } - + .notificationPage { .thirty-two-avatar { @@ -139,14 +178,14 @@ $unread_notifications_dot_size: 8px; border-radius: 16px; vertical-align: middle; } - + .button { line-height: 32px; - + img { margin-top: 8px; } - + &.decline { background: #DB5D5D; &:hover { @@ -154,7 +193,7 @@ $unread_notifications_dot_size: 8px; } } } - + .notification-body { p, div { margin: 1em auto; diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 210c0b43..6d75d24e 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -10,10 +10,15 @@ class NotificationsController < ApplicationController respond_to do |format| format.html format.json do - render json: @notifications.map do |notification| + notifications = @notifications.map do |notification| receipt = @receipts.find_by(notification_id: notification.id) - notification.as_json.merge(is_read: receipt.is_read) + notification.as_json.merge( + is_read: receipt.is_read, + notified_object: notification.notified_object, + sender: notification.sender + ) end + render json: notifications end end end diff --git a/frontend/src/Metamaps/GlobalUI/Notifications.js b/frontend/src/Metamaps/GlobalUI/Notifications.js new file mode 100644 index 00000000..1ca9e36a --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/Notifications.js @@ -0,0 +1,14 @@ +const Notifications = { + notifications: null, + fetch: render => { + $.ajax({ + url: '/notifications.json', + success: function(data) { + Notifications.notifications = data + render() + } + }) + } +} + +export default Notifications diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 5cfbf255..8cc85840 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -8,6 +8,7 @@ import apply from 'async/apply' import { notifyUser } from './index.js' import ImportDialog from './ImportDialog' +import Notifications from './Notifications' import Active from '../Active' import DataModel from '../DataModel' import { ExploreMaps, ChatView, TopicCard, ContextMenu } from '../Views' @@ -107,7 +108,9 @@ const ReactApp = { mobileTitleWidth: self.mobileTitleWidth, mobileTitleClick: (e) => Active.Map && InfoBox.toggleBox(e), openInviteLightbox: () => self.openLightbox('invite'), - serverData: self.serverData + serverData: self.serverData, + notifications: Notifications.notifications, + fetchNotifications: apply(Notifications.fetch, ReactApp.render) }, self.getMapProps(), self.getTopicProps(), diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js new file mode 100644 index 00000000..675a2a3b --- /dev/null +++ b/frontend/src/components/App/NotificationBox.js @@ -0,0 +1,41 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +import onClickOutsideAddon from 'react-onclickoutside' + +class NotificationBox extends Component { + + static propTypes = { + notifications: PropTypes.array, + fetchNotifications: PropTypes.func.isRequired, + toggleNotificationsBox: PropTypes.func.isRequired + } + + componentDidMount = () => { + const { notifications, fetchNotifications } = this.props + if (!notifications) { + fetchNotifications() + } + } + + handleClickOutside = () => { + this.props.toggleNotificationsBox() + } + + render = () => { + const { notifications } = this.props + return
          +
          +
            + {!notifications &&
          • loading...
          • } +
          • A notification
          • +
          • A notification
          • +
          • A notification
          • +
          • A notification
          • +
          + See all +
          + } +} + +export default onClickOutsideAddon(NotificationBox) diff --git a/frontend/src/components/App/NotificationIcon.js b/frontend/src/components/App/NotificationIcon.js index 36b86b72..b265cec0 100644 --- a/frontend/src/components/App/NotificationIcon.js +++ b/frontend/src/components/App/NotificationIcon.js @@ -4,18 +4,14 @@ import PropTypes from 'prop-types' class NotificationIcon extends Component { static propTypes = { - unreadNotificationsCount: PropTypes.number - } - - constructor(props) { - super(props) - - this.state = { - } + unreadNotificationsCount: PropTypes.number, + toggleNotificationsBox: PropTypes.func } render = () => { + const { toggleNotificationsBox } = this.props let linkClasses = 'notificationsIcon upperRightEl upperRightIcon ' + linkClasses += 'ignore-react-onclickoutside ' if (this.props.unreadNotificationsCount > 0) { linkClasses += 'unread' @@ -24,14 +20,14 @@ class NotificationIcon extends Component { } return ( - + ) } diff --git a/frontend/src/components/App/UpperRightUI.js b/frontend/src/components/App/UpperRightUI.js index d1e53652..a1fa0d32 100644 --- a/frontend/src/components/App/UpperRightUI.js +++ b/frontend/src/components/App/UpperRightUI.js @@ -4,31 +4,51 @@ import PropTypes from 'prop-types' import AccountMenu from './AccountMenu' import LoginForm from './LoginForm' import NotificationIcon from './NotificationIcon' +import NotificationBox from './NotificationBox' class UpperRightUI extends Component { static propTypes = { currentUser: PropTypes.object, signInPage: PropTypes.bool, unreadNotificationsCount: PropTypes.number, + fetchNotifications: PropTypes.func, + notifications: PropTypes.array, openInviteLightbox: PropTypes.func } constructor(props) { super(props) - this.state = {accountBoxOpen: false} + this.state = { + accountBoxOpen: false, + notificationsBoxOpen: false + } } reset = () => { - this.setState({accountBoxOpen: false}) + this.setState({ + accountBoxOpen: false, + notificationsBoxOpen: false + }) } toggleAccountBox = () => { - this.setState({accountBoxOpen: !this.state.accountBoxOpen}) + this.setState({ + accountBoxOpen: !this.state.accountBoxOpen, + notificationsBoxOpen: false + }) + } + + toggleNotificationsBox = () => { + this.setState({ + notificationsBoxOpen: !this.state.notificationsBoxOpen, + accountBoxOpen: false + }) } render () { - const { currentUser, signInPage, unreadNotificationsCount, openInviteLightbox } = this.props - const { accountBoxOpen } = this.state + const { currentUser, signInPage, unreadNotificationsCount, + notifications, fetchNotifications, openInviteLightbox } = this.props + const { accountBoxOpen, notificationsBoxOpen } = this.state return
          {currentUser &&
          @@ -36,7 +56,13 @@ class UpperRightUI extends Component {
          } {currentUser && - + + {notificationsBoxOpen && } } {!signInPage &&
          diff --git a/frontend/src/components/App/index.js b/frontend/src/components/App/index.js index ed24e9d1..207da1cd 100644 --- a/frontend/src/components/App/index.js +++ b/frontend/src/components/App/index.js @@ -11,6 +11,8 @@ class App extends Component { children: PropTypes.object, toast: PropTypes.string, unreadNotificationsCount: PropTypes.number, + notifications: PropTypes.array, + fetchNotifications: PropTypes.func, location: PropTypes.object, mobile: PropTypes.bool, mobileTitle: PropTypes.string, @@ -38,7 +40,7 @@ class App extends Component { const { children, toast, unreadNotificationsCount, openInviteLightbox, mobile, mobileTitle, mobileTitleWidth, mobileTitleClick, location, map, userRequested, requestAnswered, requestApproved, serverData, - onRequestAccess } = this.props + onRequestAccess, notifications, fetchNotifications } = this.props const { pathname } = location || {} // this fixes a bug that happens otherwise when you logout const currentUser = this.props.currentUser && this.props.currentUser.id ? this.props.currentUser : null @@ -58,6 +60,8 @@ class App extends Component { onRequestClick={onRequestAccess} />} {!mobile && } From 9cc700c64d4d28938a2a9dcbcf316b3c1661c3f1 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 22 Sep 2017 18:38:38 -0400 Subject: [PATCH 16/40] use decorator pattern for notifs api --- app/assets/stylesheets/notifications.scss.erb | 168 +++++++++--------- app/controllers/notifications_controller.rb | 20 +-- app/decorators/notification_decorator.rb | 47 +++++ app/views/notifications/mark_read.js.erb | 7 - app/views/notifications/mark_unread.js.erb | 7 - config/application.rb | 2 +- .../src/Metamaps/GlobalUI/Notifications.js | 40 +++++ frontend/src/Metamaps/GlobalUI/ReactApp.js | 8 +- frontend/src/Metamaps/GlobalUI/index.js | 3 +- frontend/src/Metamaps/index.js | 3 +- .../src/components/App/NotificationBox.js | 19 +- frontend/src/components/App/UpperRightUI.js | 7 +- frontend/src/components/App/index.js | 7 +- frontend/src/components/Notification.js | 76 ++++++++ 14 files changed, 284 insertions(+), 130 deletions(-) create mode 100644 app/decorators/notification_decorator.rb delete mode 100644 app/views/notifications/mark_read.js.erb delete mode 100644 app/views/notifications/mark_unread.js.erb create mode 100644 frontend/src/components/Notification.js diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index 95524caa..036652d3 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -55,10 +55,6 @@ $unread_notifications_dot_size: 8px; } .controller-notifications { - ul.notifications { - list-style: none; - } - .notificationPage, .notificationsPage { font-family: 'din-regular', Sans-Serif; @@ -86,86 +82,6 @@ $unread_notifications_dot_size: 8px; .emptyInbox { padding-top: 15px; } - - .notification { - padding: 10px; - position: relative; - - &:hover { - background: #F6F6F6; - - .notification-read-unread { - display:block; - } - - .notification-date { - display: none; - } - } - - & > a { - float: left; - width: 85%; - box-sizing: border-box; - padding-right: 10px; - } - - .notification-actor { - float: left; - - img { - width: 32px; - height: 32px; - border-radius: 16px; - } - } - - .notification-body { - margin-left: 50px; - line-height: 20px; - - .in-bold { - font-family: 'din-medium', Sans-Serif; - } - - .action { - background: #4fb5c0; - color: #FFF; - padding: 2px 6px; - border-radius: 3px; - display: inline-block; - margin: 5px 0; - } - } - - .notification-date { - position: absolute; - top: 50%; - right: 10px; - color: #607d8b; - font-size: 13px; - line-height: 13px; - margin-top: -6px; - } - - .notification-read-unread { - display: none; - float: left; - width: 15%; - - a { - position: absolute; - top: 50%; - margin-top: -10px; - text-align: center; - } - } - - &.unread { - background: #EEE; - } - } - } @@ -202,3 +118,87 @@ $unread_notifications_dot_size: 8px; } } } + +ul.notifications { + list-style: none; +} + +.notification { + padding: 10px; + position: relative; + + &:hover { + background: #F6F6F6; + + .notification-read-unread { + display:block; + } + + .notification-date { + display: none; + } + } + + & > a { + float: left; + width: 85%; + box-sizing: border-box; + padding-right: 10px; + } + + .notification-actor { + float: left; + + img { + width: 32px; + height: 32px; + border-radius: 16px; + } + } + + .notification-body { + font-family: 'din-regular', Sans-Serif; + margin-left: 50px; + line-height: 20px; + + .in-bold { + font-family: 'din-medium', Sans-Serif; + } + + .action { + background: #4fb5c0; + color: #FFF; + padding: 2px 6px; + border-radius: 3px; + display: inline-block; + margin: 5px 0; + } + } + + .notification-date { + position: absolute; + top: 50%; + right: 10px; + color: #607d8b; + font-size: 13px; + line-height: 13px; + margin-top: -6px; + } + + .notification-read-unread { + display: none; + float: left; + width: 15%; + + div { + position: absolute; + top: 50%; + margin-top: -10px; + text-align: center; + } + } + + &.unread { + background: #EEE; + } +} diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 6d75d24e..6f557428 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -12,11 +12,7 @@ class NotificationsController < ApplicationController format.json do notifications = @notifications.map do |notification| receipt = @receipts.find_by(notification_id: notification.id) - notification.as_json.merge( - is_read: receipt.is_read, - notified_object: notification.notified_object, - sender: notification.sender - ) + NotificationDecorator.decorate(notification, receipt) end render json: notifications end @@ -39,9 +35,7 @@ class NotificationsController < ApplicationController end end format.json do - render json: @notification.as_json.merge( - is_read: @receipt.is_read - ) + render json: NotificationDecorator.decorate(@notification, @receipt) end end end @@ -49,11 +43,8 @@ class NotificationsController < ApplicationController def mark_read @receipt.update(is_read: true) respond_to do |format| - format.js format.json do - render json: @notification.as_json.merge( - is_read: @receipt.is_read - ) + render json: NotificationDecorator.decorate(@notification, @receipt) end end end @@ -61,11 +52,8 @@ class NotificationsController < ApplicationController def mark_unread @receipt.update(is_read: false) respond_to do |format| - format.js format.json do - render json: @notification.as_json.merge( - is_read: @receipt.is_read - ) + render json: NotificationDecorator.decorate(@notification, @receipt) end end end diff --git a/app/decorators/notification_decorator.rb b/app/decorators/notification_decorator.rb new file mode 100644 index 00000000..bec0685c --- /dev/null +++ b/app/decorators/notification_decorator.rb @@ -0,0 +1,47 @@ +class NotificationDecorator + class << self + def decorate(notification, receipt) + result = { + id: notification.id, + type: notification.notification_code, + subject: notification.subject, + is_read: receipt.is_read, + created_at: notification.created_at, + actor: notification.sender, + object: notification.notified_object + } + + case notification.notification_code + when MAP_ACCESS_APPROVED, MAP_ACCESS_REQUEST, MAP_INVITE_TO_EDIT + map = notification.notified_object&.map + result[:map] = { + id: map&.id, + name: map&.name + } + when TOPIC_ADDED_TO_MAP + topic = notification.notified_object&.eventable + map = notification.notified_object&.map + result[:topic] = { + id: topic&.id, + name: topic&.name + } + result[:map] = { + id: map&.id, + name: map&.name + } + when TOPIC_CONNECTED_1, TOPIC_CONNECTED_2 + topic1 = notification.notified_object&.topic1 + topic2 = notification.notified_object&.topic2 + result[:topic1] = { + id: topic1&.id, + name: topic1&.name + } + resul[:topic2] = { + id: topic2&.id, + name: topic2&.name + } + end + result + end + end +end diff --git a/app/views/notifications/mark_read.js.erb b/app/views/notifications/mark_read.js.erb deleted file mode 100644 index cbf2cf13..00000000 --- a/app/views/notifications/mark_read.js.erb +++ /dev/null @@ -1,7 +0,0 @@ -$('#notification-<%= @notification.id %> .notification-read-unread > a') - .text('mark as unread') - .attr('href', '<%= mark_unread_notification_path(@notification.id) %>') -$('#notification-<%= @notification.id %>') - .removeClass('unread') - .addClass('read') -Metamaps.GlobalUI.NotificationIcon.render(Metamaps.GlobalUI.NotificationIcon.unreadNotificationsCount - 1) \ No newline at end of file diff --git a/app/views/notifications/mark_unread.js.erb b/app/views/notifications/mark_unread.js.erb deleted file mode 100644 index 3fffab24..00000000 --- a/app/views/notifications/mark_unread.js.erb +++ /dev/null @@ -1,7 +0,0 @@ -$('#notification-<%= @notification.id %> .notification-read-unread > a') - .text('mark as read') - .attr('href', '<%= mark_read_notification_path(@notification.id) %>') -$('#notification-<%= @notification.id %>') - .removeClass('read') - .addClass('unread') -Metamaps.GlobalUI.NotificationIcon.render(Metamaps.GlobalUI.NotificationIcon.unreadNotificationsCount + 1) \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index ff5a621c..8731e76a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -19,7 +19,7 @@ module Metamaps end # Custom directories with classes and modules you want to be autoloadable. - config.autoload_paths << Rails.root.join('app', 'services') + config.autoload_paths << Rails.root.join('app', 'decorators', 'services') # Configure the default encoding used in templates for Ruby 1.9. config.encoding = 'utf-8' diff --git a/frontend/src/Metamaps/GlobalUI/Notifications.js b/frontend/src/Metamaps/GlobalUI/Notifications.js index 1ca9e36a..3f0e2f92 100644 --- a/frontend/src/Metamaps/GlobalUI/Notifications.js +++ b/frontend/src/Metamaps/GlobalUI/Notifications.js @@ -1,5 +1,11 @@ +import GlobalUI from './index' + const Notifications = { notifications: null, + unreadNotificationsCount: 0, + init: serverData => { + Notifications.unreadNotificationsCount = serverData.unreadNotificationsCount + }, fetch: render => { $.ajax({ url: '/notifications.json', @@ -8,6 +14,40 @@ const Notifications = { render() } }) + }, + markAsRead: (render, id) => { + const n = Notifications.notifications.find(n => n.id === id) + $.ajax({ + url: `/notifications/${id}/mark_read`, + method: 'PUT', + success: function(r) { + if (n) { + Notifications.unreadNotificationsCount-- + n.is_read = true + render() + } + }, + error: function() { + GlobalUI.notifyUser('There was an error marking that notification as read') + } + }) + }, + markAsUnread: (render, id) => { + const n = Notifications.notifications.find(n => n.id === id) + $.ajax({ + url: `/notifications/${id}/mark_unread`, + method: 'PUT', + success: function() { + if (n) { + Notifications.unreadNotificationsCount++ + n.is_read = false + render() + } + }, + error: function() { + GlobalUI.notifyUser('There was an error marking that notification as read') + } + }) } } diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index 8cc85840..caa483d2 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -31,7 +31,6 @@ const ReactApp = { serverData: {}, mapId: null, topicId: null, - unreadNotificationsCount: 0, mapsWidth: 0, toast: '', mobile: false, @@ -41,7 +40,6 @@ const ReactApp = { init: function(serverData, openLightbox) { const self = ReactApp self.serverData = serverData - self.unreadNotificationsCount = serverData.unreadNotificationsCount self.mobileTitle = serverData.mobileTitle self.openLightbox = openLightbox self.metacodeSets = serverData.metacodeSets @@ -100,7 +98,7 @@ const ReactApp = { getProps: function() { const self = ReactApp return merge({ - unreadNotificationsCount: self.unreadNotificationsCount, + unreadNotificationsCount: Notifications.unreadNotificationsCount, currentUser: Active.Mapper, toast: self.toast, mobile: self.mobile, @@ -110,7 +108,9 @@ const ReactApp = { openInviteLightbox: () => self.openLightbox('invite'), serverData: self.serverData, notifications: Notifications.notifications, - fetchNotifications: apply(Notifications.fetch, ReactApp.render) + fetchNotifications: apply(Notifications.fetch, ReactApp.render), + markAsRead: apply(Notifications.markAsRead, ReactApp.render), + markAsUnread: apply(Notifications.markAsUnread, ReactApp.render) }, self.getMapProps(), self.getTopicProps(), diff --git a/frontend/src/Metamaps/GlobalUI/index.js b/frontend/src/Metamaps/GlobalUI/index.js index b2dd6654..858ef902 100644 --- a/frontend/src/Metamaps/GlobalUI/index.js +++ b/frontend/src/Metamaps/GlobalUI/index.js @@ -4,6 +4,7 @@ import clipboard from 'clipboard-js' import Create from '../Create' +import Notifications from './Notifications' import ReactApp from './ReactApp' import Search from './Search' import CreateMap from './CreateMap' @@ -151,5 +152,5 @@ const GlobalUI = { } } -export { ReactApp, Search, CreateMap, ImportDialog } +export { Notifications, ReactApp, Search, CreateMap, ImportDialog } export default GlobalUI diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js index 12fcbe60..747cb643 100644 --- a/frontend/src/Metamaps/index.js +++ b/frontend/src/Metamaps/index.js @@ -9,7 +9,7 @@ import DataModel from './DataModel' import Debug from './Debug' import Filter from './Filter' import GlobalUI, { - ReactApp, Search, CreateMap, ImportDialog + Notifications, ReactApp, Search, CreateMap, ImportDialog } from './GlobalUI' import Import from './Import' import JIT from './JIT' @@ -42,6 +42,7 @@ Metamaps.DataModel = DataModel Metamaps.Debug = Debug Metamaps.Filter = Filter Metamaps.GlobalUI = GlobalUI +Metamaps.GlobalUI.Notifications = Notifications Metamaps.GlobalUI.ReactApp = ReactApp Metamaps.GlobalUI.Search = Search Metamaps.GlobalUI.CreateMap = CreateMap diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js index 675a2a3b..e5b5daf7 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/App/NotificationBox.js @@ -2,13 +2,16 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import onClickOutsideAddon from 'react-onclickoutside' +import Notification from '../Notification' class NotificationBox extends Component { static propTypes = { notifications: PropTypes.array, fetchNotifications: PropTypes.func.isRequired, - toggleNotificationsBox: PropTypes.func.isRequired + toggleNotificationsBox: PropTypes.func.isRequired, + markAsRead: PropTypes.func.isRequired, + markAsUnread: PropTypes.func.isRequired } componentDidMount = () => { @@ -23,15 +26,17 @@ class NotificationBox extends Component { } render = () => { - const { notifications } = this.props + const { notifications, markAsRead, markAsUnread } = this.props return
          -
            +
              {!notifications &&
            • loading...
            • } -
            • A notification
            • -
            • A notification
            • -
            • A notification
            • -
            • A notification
            • + {notifications && notifications.map(n => { + return + })}
            See all
          diff --git a/frontend/src/components/App/UpperRightUI.js b/frontend/src/components/App/UpperRightUI.js index a1fa0d32..08276c82 100644 --- a/frontend/src/components/App/UpperRightUI.js +++ b/frontend/src/components/App/UpperRightUI.js @@ -13,6 +13,8 @@ class UpperRightUI extends Component { unreadNotificationsCount: PropTypes.number, fetchNotifications: PropTypes.func, notifications: PropTypes.array, + markAsRead: PropTypes.func.isRequired, + markAsUnread: PropTypes.func.isRequired, openInviteLightbox: PropTypes.func } @@ -47,7 +49,8 @@ class UpperRightUI extends Component { render () { const { currentUser, signInPage, unreadNotificationsCount, - notifications, fetchNotifications, openInviteLightbox } = this.props + notifications, fetchNotifications, openInviteLightbox, + markAsRead, markAsUnread } = this.props const { accountBoxOpen, notificationsBoxOpen } = this.state return
          {currentUser && @@ -62,6 +65,8 @@ class UpperRightUI extends Component { {notificationsBoxOpen && } } {!signInPage &&
          diff --git a/frontend/src/components/App/index.js b/frontend/src/components/App/index.js index 207da1cd..9d4d3ee0 100644 --- a/frontend/src/components/App/index.js +++ b/frontend/src/components/App/index.js @@ -13,6 +13,8 @@ class App extends Component { unreadNotificationsCount: PropTypes.number, notifications: PropTypes.array, fetchNotifications: PropTypes.func, + markAsRead: PropTypes.func, + markAsUnread: PropTypes.func, location: PropTypes.object, mobile: PropTypes.bool, mobileTitle: PropTypes.string, @@ -40,7 +42,8 @@ class App extends Component { const { children, toast, unreadNotificationsCount, openInviteLightbox, mobile, mobileTitle, mobileTitleWidth, mobileTitleClick, location, map, userRequested, requestAnswered, requestApproved, serverData, - onRequestAccess, notifications, fetchNotifications } = this.props + onRequestAccess, notifications, fetchNotifications, + markAsRead, markAsUnread } = this.props const { pathname } = location || {} // this fixes a bug that happens otherwise when you logout const currentUser = this.props.currentUser && this.props.currentUser.id ? this.props.currentUser : null @@ -62,6 +65,8 @@ class App extends Component { unreadNotificationsCount={unreadNotificationsCount} notifications={notifications} fetchNotifications={fetchNotifications} + markAsRead={markAsRead} + markAsUnread={markAsUnread} openInviteLightbox={openInviteLightbox} signInPage={pathname === '/login'} />} diff --git a/frontend/src/components/Notification.js b/frontend/src/components/Notification.js new file mode 100644 index 00000000..40212f9a --- /dev/null +++ b/frontend/src/components/Notification.js @@ -0,0 +1,76 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +class Notification extends Component { + + static propTypes = { + markAsRead: PropTypes.func, + markAsUnread: PropTypes.func, + notification: PropTypes.shape({ + id: PropTypes.number.isRequired, + type: PropTypes.string.isRequired, + subject: PropTypes.string.isRequired, + is_read: PropTypes.bool.isRequired, + created_at: PropTypes.string.isRequired, + actor: PropTypes.shape({ + id: PropTypes.number, + name: PropTypes.string, + image: PropTypes.string, + admin: PropTypes.boolean + }), + object: PropTypes.object, + map: PropTypes.object, + topic: PropTypes.object, + topic1: PropTypes.object, + topic2: PropTypes.object + }) + } + + getDate = () => { + const { notification: {created_at} } = this.props + const months = ['Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'] + const created = new Date(created_at) + return `${months[created.getMonth()]} ${created.getDate()}` + } + + markAsRead = () => { + const { notification, markAsRead } = this.props + markAsRead(notification.id) + } + + markAsUnread = () => { + const { notification, markAsUnread } = this.props + markAsUnread(notification.id) + } + + render = () => { + const { notification } = this.props + const classes = `notification ${notification.is_read ? 'read' : 'unread'}` + return
        • + +
          + +
          +
          +
          {notification.actor.name}
          + Other content +
          +
          +
          + {!notification.is_read &&
          + mark read +
          } + {notification.is_read &&
          + mark unread +
          } +
          +
          + {this.getDate()} +
          +
          +
        • + } +} + +export default Notification From fc8ac6eef149db11a0182a45032686fe6974edb8 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sat, 23 Sep 2017 11:20:02 -0400 Subject: [PATCH 17/40] nest inconsistent data under data key --- app/decorators/notification_decorator.rb | 60 ++++++++++++------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/app/decorators/notification_decorator.rb b/app/decorators/notification_decorator.rb index bec0685c..5669fb9c 100644 --- a/app/decorators/notification_decorator.rb +++ b/app/decorators/notification_decorator.rb @@ -8,38 +8,40 @@ class NotificationDecorator is_read: receipt.is_read, created_at: notification.created_at, actor: notification.sender, - object: notification.notified_object + data: { + object: notification.notified_object + } } case notification.notification_code - when MAP_ACCESS_APPROVED, MAP_ACCESS_REQUEST, MAP_INVITE_TO_EDIT - map = notification.notified_object&.map - result[:map] = { - id: map&.id, - name: map&.name - } - when TOPIC_ADDED_TO_MAP - topic = notification.notified_object&.eventable - map = notification.notified_object&.map - result[:topic] = { - id: topic&.id, - name: topic&.name - } - result[:map] = { - id: map&.id, - name: map&.name - } - when TOPIC_CONNECTED_1, TOPIC_CONNECTED_2 - topic1 = notification.notified_object&.topic1 - topic2 = notification.notified_object&.topic2 - result[:topic1] = { - id: topic1&.id, - name: topic1&.name - } - resul[:topic2] = { - id: topic2&.id, - name: topic2&.name - } + when MAP_ACCESS_APPROVED, MAP_ACCESS_REQUEST, MAP_INVITE_TO_EDIT + map = notification.notified_object&.map + result[:data][:map] = { + id: map&.id, + name: map&.name + } + when TOPIC_ADDED_TO_MAP + topic = notification.notified_object&.eventable + map = notification.notified_object&.map + result[:data][:topic] = { + id: topic&.id, + name: topic&.name + } + result[:data][:map] = { + id: map&.id, + name: map&.name + } + when TOPIC_CONNECTED_1, TOPIC_CONNECTED_2 + topic1 = notification.notified_object&.topic1 + topic2 = notification.notified_object&.topic2 + result[:data][:topic1] = { + id: topic1&.id, + name: topic1&.name + } + resul[:data][:topic2] = { + id: topic2&.id, + name: topic2&.name + } end result end From 9471efb6bd8ebd273c6c46e834f2d3b656c37fae Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Sat, 23 Sep 2017 13:19:43 -0700 Subject: [PATCH 18/40] hide "learn markdown" unless you're editing the topic card desc --- app/assets/stylesheets/base.scss.erb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/base.scss.erb b/app/assets/stylesheets/base.scss.erb index 3871fd18..b4b7bb0c 100644 --- a/app/assets/stylesheets/base.scss.erb +++ b/app/assets/stylesheets/base.scss.erb @@ -127,6 +127,10 @@ $mid-gray-opacity: rgba(66, 66, 66, 0.6); a.mdSupport { color: #4fb5c0; font-size: 11px; + display: none; + } + .riek-editing + .mdSupport { + display: block; } } .CardOnGraph.hasAttachment .scroll { From 277644f59d223f6b4081598e3ee5753c7b19b02f Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 25 Sep 2017 15:21:04 -0400 Subject: [PATCH 19/40] improve styling, fix index notifs page --- app/assets/stylesheets/notifications.scss.erb | 38 ++++++++--- app/controllers/notifications_controller.rb | 2 + app/decorators/notification_decorator.rb | 2 +- app/views/notifications/index.html.erb | 2 +- app/views/notifications/mark_read.js.erb | 7 ++ app/views/notifications/mark_unread.js.erb | 7 ++ .../src/Metamaps/GlobalUI/Notifications.js | 14 +++- frontend/src/Metamaps/GlobalUI/index.js | 1 + .../src/components/App/NotificationBox.js | 2 +- frontend/src/components/Notification.js | 68 ++++++++++++++++--- 10 files changed, 121 insertions(+), 22 deletions(-) create mode 100644 app/views/notifications/mark_read.js.erb create mode 100644 app/views/notifications/mark_unread.js.erb diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index 036652d3..3168f591 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -1,4 +1,6 @@ +$notifications-border-color: #DDDDDD; $unread_notifications_dot_size: 8px; + .unread-notifications-dot { width: $unread_notifications_dot_size; height: $unread_notifications_dot_size; @@ -22,6 +24,7 @@ $unread_notifications_dot_size: 8px; right: 0; top: 50px; box-shadow: 0 3px 6px rgba(0,0,0,0.16); + border: 1px solid $notifications-border-color; .notificationsBoxTriangle { min-width: 0 !important; @@ -31,9 +34,9 @@ $unread_notifications_dot_size: 8px; width: 20px !important; height: 20px !important; margin-left: -10px; - top: -10px; - border-top: 1px solid rgba(49, 72, 67, 0.11) !important; - border-left: 1px solid rgba(49, 72, 67, 0.11) !important; + top: -11px; + border-left: 1px solid $notifications-border-color; + border-top: 1px solid $notifications-border-color; border-bottom: 0 !important; border-right: 0 !important; background-color: #fff; @@ -42,6 +45,11 @@ $unread_notifications_dot_size: 8px; -ms-transform: rotate(45deg); } + ul.notifications { + max-height: 500px; + overflow-y: auto; + } + .notificationsBoxSeeAll { display: block; width: 100%; @@ -51,6 +59,14 @@ $unread_notifications_dot_size: 8px; color: #4fb5c0; border-top: 1px solid rgba(0, 0, 0, 0.1); } + + .notification { + font-size: 13px; + + .notification-body { + border-bottom: 1px solid $notifications-border-color; + } + } } } @@ -121,11 +137,18 @@ $unread_notifications_dot_size: 8px; ul.notifications { list-style: none; + + li:last-child { + .notification-body { + border-bottom: none !important; + } + } } .notification { - padding: 10px; + padding: 10px 10px 0 10px; position: relative; + font-family: 'din-regular', Sans-Serif; &:hover { background: #F6F6F6; @@ -157,9 +180,9 @@ ul.notifications { } .notification-body { - font-family: 'din-regular', Sans-Serif; margin-left: 50px; line-height: 20px; + padding-bottom: 10px; .in-bold { font-family: 'din-medium', Sans-Serif; @@ -180,8 +203,6 @@ ul.notifications { top: 50%; right: 10px; color: #607d8b; - font-size: 13px; - line-height: 13px; margin-top: -6px; } @@ -190,11 +211,12 @@ ul.notifications { float: left; width: 15%; - div { + a, div { position: absolute; top: 50%; margin-top: -10px; text-align: center; + cursor: pointer; } } diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 6f557428..15781a32 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -43,6 +43,7 @@ class NotificationsController < ApplicationController def mark_read @receipt.update(is_read: true) respond_to do |format| + format.js format.json do render json: NotificationDecorator.decorate(@notification, @receipt) end @@ -52,6 +53,7 @@ class NotificationsController < ApplicationController def mark_unread @receipt.update(is_read: false) respond_to do |format| + format.js format.json do render json: NotificationDecorator.decorate(@notification, @receipt) end diff --git a/app/decorators/notification_decorator.rb b/app/decorators/notification_decorator.rb index 5669fb9c..a2f7584b 100644 --- a/app/decorators/notification_decorator.rb +++ b/app/decorators/notification_decorator.rb @@ -38,7 +38,7 @@ class NotificationDecorator id: topic1&.id, name: topic1&.name } - resul[:data][:topic2] = { + result[:data][:topic2] = { id: topic2&.id, name: topic2&.name } diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index ec9987f6..3efbcb45 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -8,7 +8,7 @@
            <% blacklist = [MAP_ACCESS_REQUEST, MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT] %> - <% notifications = @notifications.to_a.delete_if{|n| blacklist.include?(n.notification_code) && (n.notified_object.nil? || n.notified_object.map.nil?) }%> + <% notifications = @notifications.to_a.delete_if{|n| blacklist.include?(n.notification_code) && (n.notified_object.nil? || n.notified_object.map.nil?) }%> <% notifications.each do |notification| %> <% receipt = @receipts.find_by(notification_id: notification.id) %>
          • diff --git a/app/views/notifications/mark_read.js.erb b/app/views/notifications/mark_read.js.erb new file mode 100644 index 00000000..17e418b8 --- /dev/null +++ b/app/views/notifications/mark_read.js.erb @@ -0,0 +1,7 @@ +$('#notification-<%= @notification.id %> .notification-read-unread > a') + .text('mark as unread') + .attr('href', '<%= mark_unread_notification_path(@notification.id) %>') +$('#notification-<%= @notification.id %>') + .removeClass('unread') + .addClass('read') +Metamaps.GlobalUI.Notifications.decrementUnread(Metamaps.GlobalUI.ReactApp.render) diff --git a/app/views/notifications/mark_unread.js.erb b/app/views/notifications/mark_unread.js.erb new file mode 100644 index 00000000..873e7af8 --- /dev/null +++ b/app/views/notifications/mark_unread.js.erb @@ -0,0 +1,7 @@ +$('#notification-<%= @notification.id %> .notification-read-unread > a') + .text('mark as read') + .attr('href', '<%= mark_read_notification_path(@notification.id) %>') +$('#notification-<%= @notification.id %>') + .removeClass('read') + .addClass('unread') +Metamaps.GlobalUI.Notifications.incrementUnread(Metamaps.GlobalUI.ReactApp.render) diff --git a/frontend/src/Metamaps/GlobalUI/Notifications.js b/frontend/src/Metamaps/GlobalUI/Notifications.js index 3f0e2f92..0d2e5271 100644 --- a/frontend/src/Metamaps/GlobalUI/Notifications.js +++ b/frontend/src/Metamaps/GlobalUI/Notifications.js @@ -15,14 +15,22 @@ const Notifications = { } }) }, + incrementUnread: (render) => { + Notifications.unreadNotificationsCount++ + render() + }, + decrementUnread: (render) => { + Notifications.unreadNotificationsCount-- + render() + }, markAsRead: (render, id) => { const n = Notifications.notifications.find(n => n.id === id) $.ajax({ - url: `/notifications/${id}/mark_read`, + url: `/notifications/${id}/mark_read.json`, method: 'PUT', success: function(r) { if (n) { - Notifications.unreadNotificationsCount-- + n.is_read = true render() } @@ -35,7 +43,7 @@ const Notifications = { markAsUnread: (render, id) => { const n = Notifications.notifications.find(n => n.id === id) $.ajax({ - url: `/notifications/${id}/mark_unread`, + url: `/notifications/${id}/mark_unread.json`, method: 'PUT', success: function() { if (n) { diff --git a/frontend/src/Metamaps/GlobalUI/index.js b/frontend/src/Metamaps/GlobalUI/index.js index 858ef902..7a7d2ae5 100644 --- a/frontend/src/Metamaps/GlobalUI/index.js +++ b/frontend/src/Metamaps/GlobalUI/index.js @@ -18,6 +18,7 @@ const GlobalUI = { init: function(serverData) { const self = GlobalUI + self.Notifications.init(serverData) self.ReactApp.init(serverData, self.openLightbox) self.CreateMap.init(serverData) self.ImportDialog.init(serverData, self.openLightbox, self.closeLightbox) diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js index e5b5daf7..2f9591ff 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/App/NotificationBox.js @@ -31,7 +31,7 @@ class NotificationBox extends Component {
              {!notifications &&
            • loading...
            • } - {notifications && notifications.map(n => { + {notifications && notifications.slice(0, 10).map(n => { return { + const { notification } = this.props + let map, topic, topic1, topic2 + let result = `
              ${notification.actor.name}
              ` + + switch(notification.type) { + case 'ACCESS_APPROVED': + map = notification.data.map + result += outdent`granted your request to edit map + ${map.name}` + break + case 'ACCESS_REQUEST': + map = notification.data.map + result += outdent`wants permission to map with you on + ${map.name}` + if (!notification.data.object.answered) { + result += '
              Offer a response
              ' + } + break + case 'INVITE_TO_EDIT': + map = notification.data.map + result += outdent`gave you edit access to map + ${map.name}` + break + case 'TOPIC_ADDED_TO_MAP': + map = notification.data.map + topic = notification.data.topic + result += outdent`added topic ${topic.name} + to map ${map.name}` + break + case 'TOPIC_CONNECTED_1': + topic1 = notification.data.topic1 + topic2 = notification.data.topic2 + result += outdent`connected ${topic1.name} + to ${topic2.name}` + break + case 'TOPIC_CONNECTED_2': + topic1 = notification.data.topic1 + topic2 = notification.data.topic2 + result += outdent`connected ${topic2.name} + to ${topic1.name}` + break + case 'MESSAGE_FROM_DEVS': + result += notification.subject + } + return {__html: result} + } + getDate = () => { const { notification: {created_at} } = this.props const months = ['Jan','Feb','Mar','Apr','May','Jun', - 'Jul','Aug','Sep','Oct','Nov','Dec'] + 'Jul','Aug','Sep','Oct','Nov','Dec'] const created = new Date(created_at) return `${months[created.getMonth()]} ${created.getDate()}` } @@ -47,17 +96,20 @@ class Notification extends Component { render = () => { const { notification } = this.props const classes = `notification ${notification.is_read ? 'read' : 'unread'}` + + if (!notification.data.object) { + return null + } + return
            • -
              -
              {notification.actor.name}
              - Other content -
              +
              -
              +
              {!notification.is_read &&
              mark read
              } @@ -65,10 +117,10 @@ class Notification extends Component { mark unread
              }
              -
              +
              {this.getDate()}
              -
              +
            • } } From a0c9530c91add6e2c370c45e0104997c50b09cec Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Mon, 25 Sep 2017 16:01:25 -0400 Subject: [PATCH 20/40] Update index.js --- frontend/src/components/MapView/MapChat/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/MapView/MapChat/index.js b/frontend/src/components/MapView/MapChat/index.js index 4f702125..1334d479 100644 --- a/frontend/src/components/MapView/MapChat/index.js +++ b/frontend/src/components/MapView/MapChat/index.js @@ -110,7 +110,7 @@ class MapChat extends Component {
            {chatOpen &&
            - PARTICIPANTS + Participants
            @@ -134,7 +134,7 @@ class MapChat extends Component { )}
            - CHAT + Chat
            { this.messagesDiv = div }}> From 216a19476b16f91d4f8df6a14fb2d835a5fd670b Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Thu, 28 Sep 2017 12:28:33 -0400 Subject: [PATCH 21/40] add hover states and empty case --- app/assets/stylesheets/notifications.scss.erb | 35 ++++--- .../src/components/App/NotificationBox.js | 13 ++- frontend/src/components/Loading.js | 92 +++++++++++++++++++ 3 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 frontend/src/components/Loading.js diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index 3168f591..f6d01dc8 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -1,4 +1,5 @@ $notifications-border-color: #DDDDDD; +$notifications-hover-color: #F6F6F6; $unread_notifications_dot_size: 8px; .unread-notifications-dot { @@ -48,6 +49,20 @@ $unread_notifications_dot_size: 8px; ul.notifications { max-height: 500px; overflow-y: auto; + + .notification { + font-size: 13px; + + .notification-body { + border-bottom: 1px solid $notifications-border-color; + } + } + + .notificationsEmpty { + font-family: din-regular, helvetica, sans-serif; + margin: 50px 10px; + text-align: center; + } } .notificationsBoxSeeAll { @@ -56,15 +71,11 @@ $unread_notifications_dot_size: 8px; text-align: center; padding: 6px 0; font-family: din-regular, helvetica, sans-serif; - color: #4fb5c0; border-top: 1px solid rgba(0, 0, 0, 0.1); - } - .notification { - font-size: 13px; - - .notification-body { - border-bottom: 1px solid $notifications-border-color; + &:hover { + color: #333; + background: $notifications-hover-color; } } } @@ -150,8 +161,12 @@ ul.notifications { position: relative; font-family: 'din-regular', Sans-Serif; + &.unread { + background: #EEE; + } + &:hover { - background: #F6F6F6; + background: $notifications-hover-color; .notification-read-unread { display:block; @@ -219,8 +234,4 @@ ul.notifications { cursor: pointer; } } - - &.unread { - background: #EEE; - } } diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js index 2f9591ff..5d4dcc2b 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/App/NotificationBox.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import onClickOutsideAddon from 'react-onclickoutside' import Notification from '../Notification' +import Loading from '../Loading' class NotificationBox extends Component { @@ -27,10 +28,15 @@ class NotificationBox extends Component { render = () => { const { notifications, markAsRead, markAsUnread } = this.props + const empty = notifications && notifications.length === 0 return
              - {!notifications &&
            • loading...
            • } + {!notifications &&
            • } + {empty &&
            • + You have no notifications.
              + More time for dancing. +
            • } {notifications && notifications.slice(0, 10).map(n => { return })}
            - See all + {notifications && !empty && + See all + }
            } } diff --git a/frontend/src/components/Loading.js b/frontend/src/components/Loading.js new file mode 100644 index 00000000..018806af --- /dev/null +++ b/frontend/src/components/Loading.js @@ -0,0 +1,92 @@ +import React from 'react' +import PropTypes from 'prop-types' + +// based on https://www.npmjs.com/package/react-loading-animation + +const loading_style = { + position: 'relative', + margin: '0 auto', + width: '30px', + height: '30px', +} + +const svg_style = { + animation: 'rotate 2s linear infinite', + height: '100%', + transformOrigin: 'center center', + width: '100%', + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + margin: 'auto' +} + +const circle_style = { + strokeDasharray: '1,200', + strokeDashoffset: '0', + animation: 'dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite', + strokeLinecap: 'round' +} + +const animation = `@keyframes rotate { + 100% { + transform: rotate(360deg); + } +} +@keyframes dash { + 0% { + stroke-dasharray: 1,200; + stroke-dashoffset: 0; + } + 50% { + stroke-dasharray: 89,200; + stroke-dashoffset: -35px; + } + 100% { + stroke-dasharray: 89,200; + stroke-dashoffset: -124px; + } +} +@keyframes color { + 100%, 0% { + stroke: #a354cd; + } + 50% { + stroke: #4fb5c0; + } +}` + +class Loading extends React.Component { + static propTypes = { + style: PropTypes.object, + width: PropTypes.string, + height: PropTypes.string, + margin: PropTypes.string + } + + static defaultProps = { + style: {}, + width: '30px', + height: '30px', + margin: '0 auto' + } + + render () { + let { width, height, margin, style } = this.props + + loading_style.width = width + loading_style.height = height + loading_style.margin = margin + + return
            + + + + +
            + } +} + +export default Loading From 8695297a0fa38e676e5d255d6c7cf75cca3c36cb Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 29 Sep 2017 13:06:19 -0400 Subject: [PATCH 22/40] wasn't using the proper serializer causing frontend error --- app/controllers/notifications_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 15781a32..cdbfb2ab 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -6,7 +6,6 @@ class NotificationsController < ApplicationController def index @notifications = current_user.mailbox.notifications.page(params[:page]).per(25) - respond_to do |format| format.html format.json do @@ -14,7 +13,11 @@ class NotificationsController < ApplicationController receipt = @receipts.find_by(notification_id: notification.id) NotificationDecorator.decorate(notification, receipt) end - render json: notifications + if notifications.length > 0 + render json: notifications + else + render json: [].to_json + end end end end From 5e6fb6290c19e449e3cbd8a5bdf2a4717afaa0b8 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 29 Sep 2017 13:06:33 -0400 Subject: [PATCH 23/40] refactor for clarity --- .../src/components/App/NotificationBox.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js index 5d4dcc2b..01382f26 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/App/NotificationBox.js @@ -33,16 +33,17 @@ class NotificationBox extends Component {
              {!notifications &&
            • } - {empty &&
            • - You have no notifications.
              - More time for dancing. -
            • } - {notifications && notifications.slice(0, 10).map(n => { - return - })} + {empty ? ( +
            • + You have no notifications.
              + More time for dancing. +
            • + ) : ( + notifications.slice(0, 10).map(n => ) + )}
            {notifications && !empty && From 15f512efef95a9eb36dafaaac66a746ce4bbd0e6 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 29 Sep 2017 14:05:39 -0400 Subject: [PATCH 24/40] improve notificationbox readability --- app/assets/stylesheets/notifications.scss.erb | 2 +- .../src/Metamaps/GlobalUI/Notifications.js | 2 +- .../src/components/App/NotificationBox.js | 55 +++++++++++++------ 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb index f6d01dc8..4c97547b 100644 --- a/app/assets/stylesheets/notifications.scss.erb +++ b/app/assets/stylesheets/notifications.scss.erb @@ -149,7 +149,7 @@ $unread_notifications_dot_size: 8px; ul.notifications { list-style: none; - li:last-child { + li:nth-last-child(2) { .notification-body { border-bottom: none !important; } diff --git a/frontend/src/Metamaps/GlobalUI/Notifications.js b/frontend/src/Metamaps/GlobalUI/Notifications.js index 0d2e5271..fef31602 100644 --- a/frontend/src/Metamaps/GlobalUI/Notifications.js +++ b/frontend/src/Metamaps/GlobalUI/Notifications.js @@ -30,7 +30,7 @@ const Notifications = { method: 'PUT', success: function(r) { if (n) { - + Notifications.unreadNotificationsCount-- n.is_read = true render() } diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js index 01382f26..3df31e7c 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/App/NotificationBox.js @@ -26,29 +26,48 @@ class NotificationBox extends Component { this.props.toggleNotificationsBox() } - render = () => { + hasSomeNotifications = () => { + const { notifications } = this.props + return notifications && notifications.length > 0 + } + + showLoading = () => { + return
          • + } + + showEmpty = () => { + return
          • + You have no notifications.
            + More time for dancing. +
          • + } + + showNotifications = () => { const { notifications, markAsRead, markAsUnread } = this.props - const empty = notifications && notifications.length === 0 + if (!this.hasSomeNotifications()) { + return this.showEmpty() + } + return notifications.slice(0, 10).map( + n => + ).concat([ +
          • + + See all + +
          • + ]) + } + + render = () => { + const { notifications } = this.props return
              - {!notifications &&
            • } - {empty ? ( -
            • - You have no notifications.
              - More time for dancing. -
            • - ) : ( - notifications.slice(0, 10).map(n => ) - )} + {notifications ? this.showNotifications() : this.showLoading()}
            - {notifications && !empty && - See all - }
            } } From d51a22c5a927e6704d58adb1ef66e2fde30deb94 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 13 Oct 2017 12:13:08 -0400 Subject: [PATCH 25/40] eslint fixes --- .../src/Metamaps/GlobalUI/Notifications.js | 1 + .../src/components/App/NotificationBox.js | 1 - frontend/src/components/Loading.js | 84 +++++++++---------- frontend/src/components/Notification.js | 7 +- 4 files changed, 46 insertions(+), 47 deletions(-) diff --git a/frontend/src/Metamaps/GlobalUI/Notifications.js b/frontend/src/Metamaps/GlobalUI/Notifications.js index fef31602..29908695 100644 --- a/frontend/src/Metamaps/GlobalUI/Notifications.js +++ b/frontend/src/Metamaps/GlobalUI/Notifications.js @@ -1,3 +1,4 @@ +/* global $ */ import GlobalUI from './index' const Notifications = { diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/App/NotificationBox.js index 3df31e7c..f5f0b801 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/App/NotificationBox.js @@ -6,7 +6,6 @@ import Notification from '../Notification' import Loading from '../Loading' class NotificationBox extends Component { - static propTypes = { notifications: PropTypes.array, fetchNotifications: PropTypes.func.isRequired, diff --git a/frontend/src/components/Loading.js b/frontend/src/components/Loading.js index 018806af..66382978 100644 --- a/frontend/src/components/Loading.js +++ b/frontend/src/components/Loading.js @@ -3,31 +3,31 @@ import PropTypes from 'prop-types' // based on https://www.npmjs.com/package/react-loading-animation -const loading_style = { - position: 'relative', - margin: '0 auto', - width: '30px', - height: '30px', +const loadingStyle = { + position: 'relative', + margin: '0 auto', + width: '30px', + height: '30px' } -const svg_style = { - animation: 'rotate 2s linear infinite', - height: '100%', - transformOrigin: 'center center', - width: '100%', - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - margin: 'auto' +const svgStyle = { + animation: 'rotate 2s linear infinite', + height: '100%', + transformOrigin: 'center center', + width: '100%', + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + margin: 'auto' } -const circle_style = { - strokeDasharray: '1,200', - strokeDashoffset: '0', - animation: 'dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite', - strokeLinecap: 'round' +const circleStyle = { + strokeDasharray: '1,200', + strokeDashoffset: '0', + animation: 'dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite', + strokeLinecap: 'round' } const animation = `@keyframes rotate { @@ -59,34 +59,34 @@ const animation = `@keyframes rotate { }` class Loading extends React.Component { - static propTypes = { - style: PropTypes.object, - width: PropTypes.string, - height: PropTypes.string, - margin: PropTypes.string - } + static propTypes = { + style: PropTypes.object, + width: PropTypes.string, + height: PropTypes.string, + margin: PropTypes.string + } - static defaultProps = { - style: {}, - width: '30px', - height: '30px', - margin: '0 auto' - } + static defaultProps = { + style: {}, + width: '30px', + height: '30px', + margin: '0 auto' + } - render () { - let { width, height, margin, style } = this.props + render() { + let { width, height, margin, style } = this.props - loading_style.width = width - loading_style.height = height - loading_style.margin = margin + loadingStyle.width = width + loadingStyle.height = height + loadingStyle.margin = margin - return
            + return
            - - + +
            - } + } } export default Loading diff --git a/frontend/src/components/Notification.js b/frontend/src/components/Notification.js index 05da2cc6..22237aaa 100644 --- a/frontend/src/components/Notification.js +++ b/frontend/src/components/Notification.js @@ -3,7 +3,6 @@ import PropTypes from 'prop-types' import outdent from 'outdent' class Notification extends Component { - static propTypes = { markAsRead: PropTypes.func, markAsUnread: PropTypes.func, @@ -32,7 +31,7 @@ class Notification extends Component { let map, topic, topic1, topic2 let result = `
            ${notification.actor.name}
            ` - switch(notification.type) { + switch (notification.type) { case 'ACCESS_APPROVED': map = notification.data.map result += outdent`granted your request to edit map @@ -77,8 +76,8 @@ class Notification extends Component { getDate = () => { const { notification: {created_at} } = this.props - const months = ['Jan','Feb','Mar','Apr','May','Jun', - 'Jul','Aug','Sep','Oct','Nov','Dec'] + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] const created = new Date(created_at) return `${months[created.getMonth()]} ${created.getDate()}` } From f9c139c19e740889a3e46e3a2b42ea8552c2c074 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 13 Oct 2017 12:22:05 -0400 Subject: [PATCH 26/40] ruby codeclimate fixes --- app/controllers/notifications_controller.rb | 2 +- app/decorators/notification_decorator.rb | 57 +++++++++++---------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index cdbfb2ab..22a34452 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -13,7 +13,7 @@ class NotificationsController < ApplicationController receipt = @receipts.find_by(notification_id: notification.id) NotificationDecorator.decorate(notification, receipt) end - if notifications.length > 0 + if !notifications.empty? render json: notifications else render json: [].to_json diff --git a/app/decorators/notification_decorator.rb b/app/decorators/notification_decorator.rb index a2f7584b..cc503821 100644 --- a/app/decorators/notification_decorator.rb +++ b/app/decorators/notification_decorator.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true class NotificationDecorator class << self def decorate(notification, receipt) @@ -14,34 +15,34 @@ class NotificationDecorator } case notification.notification_code - when MAP_ACCESS_APPROVED, MAP_ACCESS_REQUEST, MAP_INVITE_TO_EDIT - map = notification.notified_object&.map - result[:data][:map] = { - id: map&.id, - name: map&.name - } - when TOPIC_ADDED_TO_MAP - topic = notification.notified_object&.eventable - map = notification.notified_object&.map - result[:data][:topic] = { - id: topic&.id, - name: topic&.name - } - result[:data][:map] = { - id: map&.id, - name: map&.name - } - when TOPIC_CONNECTED_1, TOPIC_CONNECTED_2 - topic1 = notification.notified_object&.topic1 - topic2 = notification.notified_object&.topic2 - result[:data][:topic1] = { - id: topic1&.id, - name: topic1&.name - } - result[:data][:topic2] = { - id: topic2&.id, - name: topic2&.name - } + when MAP_ACCESS_APPROVED, MAP_ACCESS_REQUEST, MAP_INVITE_TO_EDIT + map = notification.notified_object&.map + result[:data][:map] = { + id: map&.id, + name: map&.name + } + when TOPIC_ADDED_TO_MAP + topic = notification.notified_object&.eventable + map = notification.notified_object&.map + result[:data][:topic] = { + id: topic&.id, + name: topic&.name + } + result[:data][:map] = { + id: map&.id, + name: map&.name + } + when TOPIC_CONNECTED_1, TOPIC_CONNECTED_2 + topic1 = notification.notified_object&.topic1 + topic2 = notification.notified_object&.topic2 + result[:data][:topic1] = { + id: topic1&.id, + name: topic1&.name + } + result[:data][:topic2] = { + id: topic2&.id, + name: topic2&.name + } end result end From a1d4c99ff641002b6474efb87ed264fb1021a276 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Fri, 13 Oct 2017 13:58:21 -0400 Subject: [PATCH 27/40] abstract exploreMapsBar into reusable navBar --- app/assets/stylesheets/clean.css.erb | 44 ++++----- app/assets/stylesheets/mobile.scss.erb | 2 +- app/views/layouts/doorkeeper.html.erb | 31 ------ app/views/notifications/_header.html.erb | 14 --- app/views/notifications/index.html.erb | 2 - app/views/notifications/show.html.erb | 6 +- config/application.rb | 6 +- frontend/src/components/App/NavBar.js | 19 ++++ frontend/src/components/App/NavBarLink.js | 28 ++++++ frontend/src/components/Apps/index.js | 21 ++++ frontend/src/components/Maps/Header.js | 96 +++++++------------ frontend/src/components/Maps/index.js | 1 - .../src/components/Notifications/index.js | 18 ++++ frontend/src/components/makeRoutes.js | 24 ++--- 14 files changed, 164 insertions(+), 148 deletions(-) delete mode 100644 app/views/layouts/doorkeeper.html.erb delete mode 100644 app/views/notifications/_header.html.erb create mode 100644 frontend/src/components/App/NavBar.js create mode 100644 frontend/src/components/App/NavBarLink.js create mode 100644 frontend/src/components/Apps/index.js create mode 100644 frontend/src/components/Notifications/index.js diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index 3bc37d68..0790437e 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -668,19 +668,19 @@ box-shadow: 0px 3px 3px rgba(0,0,0,0.23), 0 3px 3px rgba(0,0,0,0.16); } -#exploreMapsHeader { +#navBar { position: absolute; width: 100%; } -.exploreMapsBar { +.navBarContainer { z-index:2; background-color:#FAFAFA; height: 42px; padding-top: 52px; } -.exploreMapsMenu { +.navBarMenu { display: block; width: 100%; height:42px; @@ -689,11 +689,11 @@ text-align: center; } -.exploreMapsCenter { +.navBarCenter { display: block; } -.exploreMapsButton { +.navBarButton { color: #757575; cursor: default; font-weight: normal; @@ -706,13 +706,13 @@ cursor: pointer; position:relative; } -.exploreMapsButton:hover, .exploreMapsButton.active { +.navBarButton:hover, .navBarButton.active { text-decoration: none; color: #424242; border-bottom: 2px solid #00BCD4; } -.exploreMapsButton.mapperButton { +.navBarButton.mapperButton { height: 40px; padding: 0; } @@ -729,7 +729,7 @@ } -.exploreMapsButton .exploreMapsIcon { +.navBarButton .navBarIcon { background-repeat: no-repeat; width:32px; height:32px; @@ -738,53 +738,53 @@ left:5px; } -.exploreMapsCenter .authedApps .exploreMapsIcon { +.navBarCenter .authedApps .navBarIcon { background-image: url(<%= asset_path('user_sprite.png') %>); background-position: 0 -32px; } -.exploreMapsCenter .myMaps .exploreMapsIcon { +.navBarCenter .myMaps .navBarIcon { background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: -32px 0; } -.exploreMapsCenter .sharedMaps .exploreMapsIcon { +.navBarCenter .sharedMaps .navBarIcon { background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: -128px 0; } -.exploreMapsCenter .activeMaps .exploreMapsIcon { +.navBarCenter .activeMaps .navBarIcon { background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: 0 0; } -.exploreMapsCenter .featuredMaps .exploreMapsIcon { +.navBarCenter .featuredMaps .navBarIcon { background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: -96px 0; } -.exploreMapsCenter .starredMaps .exploreMapsIcon { +.navBarCenter .starredMaps .navBarIcon { background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: -96px 0; } -.exploreMapsCenter .notificationsLink .exploreMapsIcon { +.navBarCenter .notificationsLink .navBarIcon { background-image: url(<%= asset_path 'topright_sprite.png' %>); background-position: -128px 0; } -.authedApps:hover .exploreMapsIcon, .authedApps.active .exploreMapsIcon { +.authedApps:hover .navBarIcon, .authedApps.active .navBarIcon { background-position-x: -32px; } -.myMaps:hover .exploreMapsIcon, .myMaps.active .exploreMapsIcon { +.myMaps:hover .navBarIcon, .myMaps.active .navBarIcon { background-position: -32px -32px; } -.activeMaps:hover .exploreMapsIcon, .activeMaps.active .exploreMapsIcon { +.activeMaps:hover .navBarIcon, .activeMaps.active .navBarIcon { background-position: 0 -32px; } -.featuredMaps:hover .exploreMapsIcon, .featuredMaps.active .exploreMapsIcon { +.featuredMaps:hover .navBarIcon, .featuredMaps.active .navBarIcon { background-position: -96px -32px; } -.starredMaps:hover .exploreMapsIcon, .starredMaps.active .exploreMapsIcon { +.starredMaps:hover .navBarIcon, .starredMaps.active .navBarIcon { background-position: -96px -32px; } -.sharedMaps:hover .exploreMapsIcon, .sharedMaps.active .exploreMapsIcon { +.sharedMaps:hover .navBarIcon, .sharedMaps.active .navBarIcon { background-position: -128px -32px; } -.notificationsLink:hover .exploreMapsIcon, .notificationsLink.active .exploreMapsIcon { +.notificationsLink:hover .navBarIcon, .notificationsLink.active .navBarIcon { background-position-y: -32px; } diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index 704569c8..02aefeb4 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -32,7 +32,7 @@ /* Smartphones (portrait and landscape) ----------- the minimum space that two map cards can fit side by side */ @media only screen and (max-width : 504px) { - .upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .feedback-icon, .chat-box, #exploreMapsHeader { + .upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .feedback-icon, .chat-box, #navBar { display: none !important; } diff --git a/app/views/layouts/doorkeeper.html.erb b/app/views/layouts/doorkeeper.html.erb deleted file mode 100644 index b9ff5bef..00000000 --- a/app/views/layouts/doorkeeper.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -<%# -# @file -# Main application file. Holds scaffolding present on every page. -# Then a certain non-partial view (no _ preceding filename) will be -# displayed within, based on URL -#%> - -<%= render :partial => 'layouts/head' %> - -
            - <%= yield %> -
            -
            -
            -
            - <% if current_user && current_user.admin %> - -
            Registered Apps -
            - <% end %> - -
            Authorized Apps -
            - -
            Maps -
            -
            -
            -
            -
            -<%= render :partial => 'layouts/foot' %> diff --git a/app/views/notifications/_header.html.erb b/app/views/notifications/_header.html.erb deleted file mode 100644 index 2507b2ef..00000000 --- a/app/views/notifications/_header.html.erb +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index 3efbcb45..4b0a69d2 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -76,5 +76,3 @@
            <% end %>
            - -<%= render partial: 'notifications/header' %> diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb index a552aa77..e2cb2bd2 100644 --- a/app/views/notifications/show.html.erb +++ b/app/views/notifications/show.html.erb @@ -9,7 +9,7 @@

            <% case @notification.notification_code when MAP_ACCESS_REQUEST - request = @notification.notified_object + request = @notification.notified_object map = request.map %> <%= image_tag @notification.sender.image(:thirtytwo), class: 'thirty-two-avatar' %> <%= request.user.name %> wants to collaborate on map <%= map.name %> <% else %> @@ -24,7 +24,7 @@ <% if request.approved %> You already responded to this access request, and allowed access. <% elsif !request.approved %> - You already responded to this access request, and declined access. If you changed your mind, you can still grant + You already responded to this access request, and declined access. If you changed your mind, you can still grant them access by going to the map and adding them as a collaborator. <% end %> <% else %> @@ -50,5 +50,3 @@ <% end %>

          - -<%= render partial: 'notifications/header' %> diff --git a/config/application.rb b/config/application.rb index 8731e76a..715ee8af 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,9 +25,9 @@ module Metamaps config.encoding = 'utf-8' config.to_prepare do - Doorkeeper::ApplicationsController.layout 'doorkeeper' - Doorkeeper::AuthorizationsController.layout 'doorkeeper' - Doorkeeper::AuthorizedApplicationsController.layout 'doorkeeper' + Doorkeeper::ApplicationsController.layout "application" + Doorkeeper::AuthorizationsController.layout "application" + Doorkeeper::AuthorizedApplicationsController.layout "application" Doorkeeper::ApplicationController.helper ApplicationHelper end diff --git a/frontend/src/components/App/NavBar.js b/frontend/src/components/App/NavBar.js new file mode 100644 index 00000000..92ec9591 --- /dev/null +++ b/frontend/src/components/App/NavBar.js @@ -0,0 +1,19 @@ +import React, { Component } from 'react' + +class NavBar extends Component { + render() { + return ( + + ) + } +} + +export default NavBar diff --git a/frontend/src/components/App/NavBarLink.js b/frontend/src/components/App/NavBarLink.js new file mode 100644 index 00000000..ff0856e7 --- /dev/null +++ b/frontend/src/components/App/NavBarLink.js @@ -0,0 +1,28 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { Link } from 'react-router' +import _ from 'lodash' + +const NavBarLink = props => { + const { show, text, href, linkClass } = props + const otherProps = _.omit(props, ['show', 'text', 'href', 'linkClass']) + if (!show) { + return null + } + + return ( + +
          + {text} + + ) +} + +NavBarLink.propTypes = { + show: PropTypes.bool, + text: PropTypes.string, + href: PropTypes.string, + linkClass: PropTypes.string +} + +export default NavBarLink diff --git a/frontend/src/components/Apps/index.js b/frontend/src/components/Apps/index.js new file mode 100644 index 00000000..53d23a0f --- /dev/null +++ b/frontend/src/components/Apps/index.js @@ -0,0 +1,21 @@ +import React, { Component } from 'react' +import NavBar from '../App/Navbar' +import NavBarLink from '../App/NavBarLink' + +class Apps extends Component { + render = () => { + const { currentUser } = this.props + + return ( + + {currentUser.get('admin') && } + + + + ) + } +} + +export default Apps diff --git a/frontend/src/components/Maps/Header.js b/frontend/src/components/Maps/Header.js index 2168e0c0..3f6181c3 100644 --- a/frontend/src/components/Maps/Header.js +++ b/frontend/src/components/Maps/Header.js @@ -1,30 +1,14 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { Link } from 'react-router' -import _ from 'lodash' - -const MapLink = props => { - const { show, text, href, linkClass } = props - const otherProps = _.omit(props, ['show', 'text', 'href', 'linkClass']) - if (!show) { - return null - } - - return ( - -
          - {text} - - ) -} +import NavBar from '../App/Navbar' +import NavBarLink from '../App/NavBarLink' class Header extends Component { render = () => { const { signedIn, section, user } = this.props const activeClass = (title) => { - let forClass = 'exploreMapsButton' - forClass += ' ' + title + 'Maps' + let forClass = title + 'Maps' if (title === 'my' && section === 'mine' || title === section) forClass += ' active' return forClass @@ -34,47 +18,41 @@ class Header extends Component { const mapper = section === 'mapper' return ( -
          -
          -
          -
          - - - - - + + + + + + - {mapper ? ( -
          - {user && } - {user &&
          {user.name}’s Maps
          } -
          -
          - ) : null } -
          -
          -
          -
          + {mapper ? ( +
          + {user && } + {user &&
          {user.name}’s Maps
          } +
          +
          + ) : null } + ) } } diff --git a/frontend/src/components/Maps/index.js b/frontend/src/components/Maps/index.js index 081515fe..c6835a5f 100644 --- a/frontend/src/components/Maps/index.js +++ b/frontend/src/components/Maps/index.js @@ -6,7 +6,6 @@ import MapperCard from './MapperCard' import MapCard from './MapCard' class Maps extends Component { - static propTypes = { section: PropTypes.string, maps: PropTypes.object, diff --git a/frontend/src/components/Notifications/index.js b/frontend/src/components/Notifications/index.js new file mode 100644 index 00000000..52a3c63c --- /dev/null +++ b/frontend/src/components/Notifications/index.js @@ -0,0 +1,18 @@ +import React, { Component } from 'react' +import NavBar from '../App/Navbar' +import NavBarLink from '../App/NavBarLink' + +class Notifications extends Component { + render = () => { + const { currentUser } = this.props + return ( + + + + + ) + } +} + +export default Notifications diff --git a/frontend/src/components/makeRoutes.js b/frontend/src/components/makeRoutes.js index 45f8b746..c0ff2b1c 100644 --- a/frontend/src/components/makeRoutes.js +++ b/frontend/src/components/makeRoutes.js @@ -1,8 +1,10 @@ import React from 'react' import { Route, IndexRoute } from 'react-router' import App from './App' +import Apps from './Apps' import Maps from './Maps' import MapView from './MapView' +import Notifications from './Notifications' import TopicView from './TopicView' function nullComponent(props) { @@ -31,8 +33,8 @@ export default function makeRoutes (currentUser) { - - + + @@ -50,20 +52,20 @@ export default function makeRoutes (currentUser) { - + - - + + - - + + - - - - + + + + From 693e6f5e101a2d11babd9c8bf9e19d3b90d60c31 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sat, 14 Oct 2017 12:03:05 -0400 Subject: [PATCH 28/40] bug fixes and make active class auto --- app/assets/stylesheets/application.scss.erb | 1 + app/views/layouts/application.html.erb | 4 +- config/application.rb | 6 +- frontend/src/components/App/NavBarLink.js | 71 +++++++++++++----- frontend/src/components/App/index.js | 21 ++++++ frontend/src/components/Apps/index.js | 11 +-- frontend/src/components/Maps/Header.js | 72 +++++++++---------- .../src/components/Notifications/index.js | 5 +- frontend/src/components/makeRoutes.js | 4 +- 9 files changed, 124 insertions(+), 71 deletions(-) diff --git a/app/assets/stylesheets/application.scss.erb b/app/assets/stylesheets/application.scss.erb index d255b652..11330ef9 100644 --- a/app/assets/stylesheets/application.scss.erb +++ b/app/assets/stylesheets/application.scss.erb @@ -826,6 +826,7 @@ label { font-size: 14px; line-height: 14px; color: #757575; + cursor: pointer; } .accountListItem:hover { color: #424242; diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 3ebbbad6..340874fc 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,10 +6,10 @@ #%> <%= render :partial => 'layouts/head' %> - controller-<%= controller_name %> action-<%= action_name %>"> + controller-<%= controller_name %> action-<%= action_name %>">
          <%= yield %> - <% if authenticated? %> + <% if current_user %> <% # for creating and pulling in topics and synapses %> <% if controller_name == 'maps' && action_name == "conversation" %> <%= render :partial => 'maps/newtopicsecret' %> diff --git a/config/application.rb b/config/application.rb index 715ee8af..06842eb6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -25,9 +25,9 @@ module Metamaps config.encoding = 'utf-8' config.to_prepare do - Doorkeeper::ApplicationsController.layout "application" - Doorkeeper::AuthorizationsController.layout "application" - Doorkeeper::AuthorizedApplicationsController.layout "application" + Doorkeeper::ApplicationsController.layout 'application' + Doorkeeper::AuthorizationsController.layout 'application' + Doorkeeper::AuthorizedApplicationsController.layout 'application' Doorkeeper::ApplicationController.helper ApplicationHelper end diff --git a/frontend/src/components/App/NavBarLink.js b/frontend/src/components/App/NavBarLink.js index ff0856e7..033de2e9 100644 --- a/frontend/src/components/App/NavBarLink.js +++ b/frontend/src/components/App/NavBarLink.js @@ -3,26 +3,63 @@ import PropTypes from 'prop-types' import { Link } from 'react-router' import _ from 'lodash' -const NavBarLink = props => { - const { show, text, href, linkClass } = props - const otherProps = _.omit(props, ['show', 'text', 'href', 'linkClass']) - if (!show) { - return null +const PROP_LIST = [ + 'matchChildRoutes', + 'hardReload', + 'show', + 'text', + 'href', + 'linkClass' +] + +class NavBarLink extends Component { + static propTypes = { + matchChildRoutes: PropTypes.bool, + hardReload: PropTypes.bool, + show: PropTypes.bool, + text: PropTypes.string, + href: PropTypes.string, + linkClass: PropTypes.string } - return ( - -
          - {text} - - ) -} + static contextTypes = { + location: PropTypes.object + } -NavBarLink.propTypes = { - show: PropTypes.bool, - text: PropTypes.string, - href: PropTypes.string, - linkClass: PropTypes.string + render = () => { + const { + matchChildRoutes, + hardReload, + show, + text, + href, + linkClass + } = this.props + const { location } = this.context + const otherProps = _.omit(this.props, PROP_LIST) + const classes = ['navBarButton', linkClass] + const active = matchChildRoutes ? + location.pathname.startsWith(href) : + location.pathname === href + if (active) classes.push('active') + if (!show) { + return null + } + if (hardReload) { + return ( + +
          + {text} +
          + ) + } + return ( + +
          + {text} + + ) + } } export default NavBarLink diff --git a/frontend/src/components/App/index.js b/frontend/src/components/App/index.js index 9d4d3ee0..9a830255 100644 --- a/frontend/src/components/App/index.js +++ b/frontend/src/components/App/index.js @@ -38,17 +38,38 @@ class App extends Component { return {location} } + constructor (props) { + super(props) + this.state = { + yieldHTML: null + } + } + + componentDidMount () { + this.setYield() + } + + setYield () { + const yieldHTML = document.getElementById('yield') + if (yieldHTML) { + this.setState({yieldHTML: yieldHTML.innerHTML}) + document.body.removeChild(yieldHTML) + } + } + render () { const { children, toast, unreadNotificationsCount, openInviteLightbox, mobile, mobileTitle, mobileTitleWidth, mobileTitleClick, location, map, userRequested, requestAnswered, requestApproved, serverData, onRequestAccess, notifications, fetchNotifications, markAsRead, markAsUnread } = this.props + const { yieldHTML } = this.state const { pathname } = location || {} // this fixes a bug that happens otherwise when you logout const currentUser = this.props.currentUser && this.props.currentUser.id ? this.props.currentUser : null const unauthedHome = pathname === '/' && !currentUser return
          + {yieldHTML &&
          } {mobile && - {currentUser.get('admin') && } - } + - + ) } diff --git a/frontend/src/components/Maps/Header.js b/frontend/src/components/Maps/Header.js index 3f6181c3..770c0efb 100644 --- a/frontend/src/components/Maps/Header.js +++ b/frontend/src/components/Maps/Header.js @@ -7,51 +7,43 @@ class Header extends Component { render = () => { const { signedIn, section, user } = this.props - const activeClass = (title) => { - let forClass = title + 'Maps' - if (title === 'my' && section === 'mine' || - title === section) forClass += ' active' - return forClass - } - const explore = section === 'mine' || section === 'active' || section === 'starred' || section === 'shared' || section === 'featured' const mapper = section === 'mapper' return ( - - - - - - - {mapper ? ( -
          - {user && } - {user &&
          {user.name}’s Maps
          } -
          -
          - ) : null } + + + + + + {mapper ? ( +
          + {user && } + {user &&
          {user.name}’s Maps
          } +
          +
          + ) : null }
          ) } diff --git a/frontend/src/components/Notifications/index.js b/frontend/src/components/Notifications/index.js index 52a3c63c..a744e0c3 100644 --- a/frontend/src/components/Notifications/index.js +++ b/frontend/src/components/Notifications/index.js @@ -4,11 +4,10 @@ import NavBarLink from '../App/NavBarLink' class Notifications extends Component { render = () => { - const { currentUser } = this.props return ( - + ) diff --git a/frontend/src/components/makeRoutes.js b/frontend/src/components/makeRoutes.js index c0ff2b1c..90146627 100644 --- a/frontend/src/components/makeRoutes.js +++ b/frontend/src/components/makeRoutes.js @@ -54,8 +54,8 @@ export default function makeRoutes (currentUser) { - - + + From 29a25c28a8de03301312bfbc1a5a9e1fec6a1f0d Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sat, 14 Oct 2017 12:31:20 -0400 Subject: [PATCH 29/40] distinguish between route components and reusable components --- frontend/src/Metamaps/GlobalUI/ImportDialog.js | 2 +- frontend/src/Metamaps/GlobalUI/ReactApp.js | 2 +- frontend/src/components/{App => }/AccountMenu.js | 0 frontend/src/components/{common => }/ContextMenu.js | 2 +- frontend/src/components/{common => }/DataVis.js | 0 frontend/src/components/{common => }/FilterBox.js | 0 frontend/src/components/{common => }/InfoAndHelp.js | 2 +- frontend/src/components/{App => }/LoginForm.js | 0 frontend/src/components/{App => }/MobileHeader.js | 2 +- frontend/src/components/{App => }/NavBar.js | 0 frontend/src/components/{App => }/NavBarLink.js | 0 frontend/src/components/{App => }/NotificationBox.js | 4 ++-- .../src/components/{App => }/NotificationIcon.js | 0 frontend/src/components/{common => }/Sprite.js | 0 frontend/src/components/{App => }/Toast.js | 0 frontend/src/components/{App => }/UpperLeftUI.js | 0 frontend/src/components/{common => }/UpperOptions.js | 2 +- frontend/src/components/{App => }/UpperRightUI.js | 0 .../components/{common => }/VisualizationControls.js | 0 .../src/{components/App/index.js => routes/App.js} | 8 ++++---- .../src/{components/Apps/index.js => routes/Apps.js} | 4 ++-- .../MapView/ImportDialogBox.js | 0 .../{components => routes}/MapView/Instructions.js | 0 .../MapView/MapChat/Message.js | 0 .../MapView/MapChat/NewMessage.js | 0 .../MapView/MapChat/Participant.js | 0 .../{components => routes}/MapView/MapChat/Unread.js | 0 .../{components => routes}/MapView/MapChat/index.js | 0 .../src/{components => routes}/MapView/MapInfoBox.js | 0 frontend/src/{components => routes}/MapView/index.js | 12 ++++++------ frontend/src/{components => routes}/Maps/Header.js | 4 ++-- frontend/src/{components => routes}/Maps/MapCard.js | 0 .../src/{components => routes}/Maps/MapperCard.js | 0 frontend/src/{components => routes}/Maps/index.js | 0 .../index.js => routes/Notifications.js} | 4 ++-- .../TopicView/index.js => routes/TopicView.js} | 12 ++++++------ frontend/src/{components => routes}/makeRoutes.js | 0 37 files changed, 30 insertions(+), 30 deletions(-) rename frontend/src/components/{App => }/AccountMenu.js (100%) rename frontend/src/components/{common => }/ContextMenu.js (99%) rename frontend/src/components/{common => }/DataVis.js (100%) rename frontend/src/components/{common => }/FilterBox.js (100%) rename frontend/src/components/{common => }/InfoAndHelp.js (96%) rename frontend/src/components/{App => }/LoginForm.js (100%) rename frontend/src/components/{App => }/MobileHeader.js (99%) rename frontend/src/components/{App => }/NavBar.js (100%) rename frontend/src/components/{App => }/NavBarLink.js (100%) rename frontend/src/components/{App => }/NotificationBox.js (96%) rename frontend/src/components/{App => }/NotificationIcon.js (100%) rename frontend/src/components/{common => }/Sprite.js (100%) rename frontend/src/components/{App => }/Toast.js (100%) rename frontend/src/components/{App => }/UpperLeftUI.js (100%) rename frontend/src/components/{common => }/UpperOptions.js (98%) rename frontend/src/components/{App => }/UpperRightUI.js (100%) rename frontend/src/components/{common => }/VisualizationControls.js (100%) rename frontend/src/{components/App/index.js => routes/App.js} (94%) rename frontend/src/{components/Apps/index.js => routes/Apps.js} (87%) rename frontend/src/{components => routes}/MapView/ImportDialogBox.js (100%) rename frontend/src/{components => routes}/MapView/Instructions.js (100%) rename frontend/src/{components => routes}/MapView/MapChat/Message.js (100%) rename frontend/src/{components => routes}/MapView/MapChat/NewMessage.js (100%) rename frontend/src/{components => routes}/MapView/MapChat/Participant.js (100%) rename frontend/src/{components => routes}/MapView/MapChat/Unread.js (100%) rename frontend/src/{components => routes}/MapView/MapChat/index.js (100%) rename frontend/src/{components => routes}/MapView/MapInfoBox.js (100%) rename frontend/src/{components => routes}/MapView/index.js (92%) rename frontend/src/{components => routes}/Maps/Header.js (94%) rename frontend/src/{components => routes}/Maps/MapCard.js (100%) rename frontend/src/{components => routes}/Maps/MapperCard.js (100%) rename frontend/src/{components => routes}/Maps/index.js (100%) rename frontend/src/{components/Notifications/index.js => routes/Notifications.js} (80%) rename frontend/src/{components/TopicView/index.js => routes/TopicView.js} (89%) rename frontend/src/{components => routes}/makeRoutes.js (100%) diff --git a/frontend/src/Metamaps/GlobalUI/ImportDialog.js b/frontend/src/Metamaps/GlobalUI/ImportDialog.js index 63c47491..c253512d 100644 --- a/frontend/src/Metamaps/GlobalUI/ImportDialog.js +++ b/frontend/src/Metamaps/GlobalUI/ImportDialog.js @@ -4,7 +4,7 @@ import React from 'react' import ReactDOM from 'react-dom' import outdent from 'outdent' -import ImportDialogBox from '../../components/MapView/ImportDialogBox' +import ImportDialogBox from '../../routes/MapView/ImportDialogBox' import PasteInput from '../PasteInput' import Map from '../Map' diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js index caa483d2..6237f08a 100644 --- a/frontend/src/Metamaps/GlobalUI/ReactApp.js +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -18,7 +18,7 @@ import Realtime from '../Realtime' import Map, { InfoBox } from '../Map' import Topic from '../Topic' import Visualize from '../Visualize' -import makeRoutes from '../../components/makeRoutes' +import makeRoutes from '../../routes/makeRoutes' let routes // 220 wide + 16 padding on both sides diff --git a/frontend/src/components/App/AccountMenu.js b/frontend/src/components/AccountMenu.js similarity index 100% rename from frontend/src/components/App/AccountMenu.js rename to frontend/src/components/AccountMenu.js diff --git a/frontend/src/components/common/ContextMenu.js b/frontend/src/components/ContextMenu.js similarity index 99% rename from frontend/src/components/common/ContextMenu.js rename to frontend/src/components/ContextMenu.js index e3649e5d..35ea0ee8 100644 --- a/frontend/src/components/common/ContextMenu.js +++ b/frontend/src/components/ContextMenu.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import MetacodeSelect from '../MetacodeSelect' +import MetacodeSelect from './MetacodeSelect' class ContextMenu extends Component { static propTypes = { diff --git a/frontend/src/components/common/DataVis.js b/frontend/src/components/DataVis.js similarity index 100% rename from frontend/src/components/common/DataVis.js rename to frontend/src/components/DataVis.js diff --git a/frontend/src/components/common/FilterBox.js b/frontend/src/components/FilterBox.js similarity index 100% rename from frontend/src/components/common/FilterBox.js rename to frontend/src/components/FilterBox.js diff --git a/frontend/src/components/common/InfoAndHelp.js b/frontend/src/components/InfoAndHelp.js similarity index 96% rename from frontend/src/components/common/InfoAndHelp.js rename to frontend/src/components/InfoAndHelp.js index 8a4edf25..e6bc01d8 100644 --- a/frontend/src/components/common/InfoAndHelp.js +++ b/frontend/src/components/InfoAndHelp.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import MapInfoBox from '../MapView/MapInfoBox' +import MapInfoBox from '../routes/MapView/MapInfoBox' class InfoAndHelp extends Component { static propTypes = { diff --git a/frontend/src/components/App/LoginForm.js b/frontend/src/components/LoginForm.js similarity index 100% rename from frontend/src/components/App/LoginForm.js rename to frontend/src/components/LoginForm.js diff --git a/frontend/src/components/App/MobileHeader.js b/frontend/src/components/MobileHeader.js similarity index 99% rename from frontend/src/components/App/MobileHeader.js rename to frontend/src/components/MobileHeader.js index a9acce37..dd27c2a7 100644 --- a/frontend/src/components/App/MobileHeader.js +++ b/frontend/src/components/MobileHeader.js @@ -2,7 +2,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { Link } from 'react-router' -import Sprite from '../common/Sprite' +import Sprite from './Sprite' class MobileHeader extends Component { static propTypes = { diff --git a/frontend/src/components/App/NavBar.js b/frontend/src/components/NavBar.js similarity index 100% rename from frontend/src/components/App/NavBar.js rename to frontend/src/components/NavBar.js diff --git a/frontend/src/components/App/NavBarLink.js b/frontend/src/components/NavBarLink.js similarity index 100% rename from frontend/src/components/App/NavBarLink.js rename to frontend/src/components/NavBarLink.js diff --git a/frontend/src/components/App/NotificationBox.js b/frontend/src/components/NotificationBox.js similarity index 96% rename from frontend/src/components/App/NotificationBox.js rename to frontend/src/components/NotificationBox.js index f5f0b801..bfd5f409 100644 --- a/frontend/src/components/App/NotificationBox.js +++ b/frontend/src/components/NotificationBox.js @@ -2,8 +2,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import onClickOutsideAddon from 'react-onclickoutside' -import Notification from '../Notification' -import Loading from '../Loading' +import Notification from './Notification' +import Loading from './Loading' class NotificationBox extends Component { static propTypes = { diff --git a/frontend/src/components/App/NotificationIcon.js b/frontend/src/components/NotificationIcon.js similarity index 100% rename from frontend/src/components/App/NotificationIcon.js rename to frontend/src/components/NotificationIcon.js diff --git a/frontend/src/components/common/Sprite.js b/frontend/src/components/Sprite.js similarity index 100% rename from frontend/src/components/common/Sprite.js rename to frontend/src/components/Sprite.js diff --git a/frontend/src/components/App/Toast.js b/frontend/src/components/Toast.js similarity index 100% rename from frontend/src/components/App/Toast.js rename to frontend/src/components/Toast.js diff --git a/frontend/src/components/App/UpperLeftUI.js b/frontend/src/components/UpperLeftUI.js similarity index 100% rename from frontend/src/components/App/UpperLeftUI.js rename to frontend/src/components/UpperLeftUI.js diff --git a/frontend/src/components/common/UpperOptions.js b/frontend/src/components/UpperOptions.js similarity index 98% rename from frontend/src/components/common/UpperOptions.js rename to frontend/src/components/UpperOptions.js index 5108eab3..26cde8bf 100644 --- a/frontend/src/components/common/UpperOptions.js +++ b/frontend/src/components/UpperOptions.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import FilterBox from '../common/FilterBox' +import FilterBox from './FilterBox' export default class UpperOptions extends Component { static propTypes = { diff --git a/frontend/src/components/App/UpperRightUI.js b/frontend/src/components/UpperRightUI.js similarity index 100% rename from frontend/src/components/App/UpperRightUI.js rename to frontend/src/components/UpperRightUI.js diff --git a/frontend/src/components/common/VisualizationControls.js b/frontend/src/components/VisualizationControls.js similarity index 100% rename from frontend/src/components/common/VisualizationControls.js rename to frontend/src/components/VisualizationControls.js diff --git a/frontend/src/components/App/index.js b/frontend/src/routes/App.js similarity index 94% rename from frontend/src/components/App/index.js rename to frontend/src/routes/App.js index 9a830255..79ad3335 100644 --- a/frontend/src/components/App/index.js +++ b/frontend/src/routes/App.js @@ -1,10 +1,10 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import MobileHeader from './MobileHeader' -import UpperLeftUI from './UpperLeftUI' -import UpperRightUI from './UpperRightUI' -import Toast from './Toast' +import MobileHeader from '../components/MobileHeader' +import UpperLeftUI from '../components/UpperLeftUI' +import UpperRightUI from '../components/UpperRightUI' +import Toast from '../components/Toast' class App extends Component { static propTypes = { diff --git a/frontend/src/components/Apps/index.js b/frontend/src/routes/Apps.js similarity index 87% rename from frontend/src/components/Apps/index.js rename to frontend/src/routes/Apps.js index 1fc5ffe3..94c7e32d 100644 --- a/frontend/src/components/Apps/index.js +++ b/frontend/src/routes/Apps.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import NavBar from '../App/Navbar' -import NavBarLink from '../App/NavBarLink' +import NavBar from '../components/Navbar' +import NavBarLink from '../components/NavBarLink' class Apps extends Component { render = () => { diff --git a/frontend/src/components/MapView/ImportDialogBox.js b/frontend/src/routes/MapView/ImportDialogBox.js similarity index 100% rename from frontend/src/components/MapView/ImportDialogBox.js rename to frontend/src/routes/MapView/ImportDialogBox.js diff --git a/frontend/src/components/MapView/Instructions.js b/frontend/src/routes/MapView/Instructions.js similarity index 100% rename from frontend/src/components/MapView/Instructions.js rename to frontend/src/routes/MapView/Instructions.js diff --git a/frontend/src/components/MapView/MapChat/Message.js b/frontend/src/routes/MapView/MapChat/Message.js similarity index 100% rename from frontend/src/components/MapView/MapChat/Message.js rename to frontend/src/routes/MapView/MapChat/Message.js diff --git a/frontend/src/components/MapView/MapChat/NewMessage.js b/frontend/src/routes/MapView/MapChat/NewMessage.js similarity index 100% rename from frontend/src/components/MapView/MapChat/NewMessage.js rename to frontend/src/routes/MapView/MapChat/NewMessage.js diff --git a/frontend/src/components/MapView/MapChat/Participant.js b/frontend/src/routes/MapView/MapChat/Participant.js similarity index 100% rename from frontend/src/components/MapView/MapChat/Participant.js rename to frontend/src/routes/MapView/MapChat/Participant.js diff --git a/frontend/src/components/MapView/MapChat/Unread.js b/frontend/src/routes/MapView/MapChat/Unread.js similarity index 100% rename from frontend/src/components/MapView/MapChat/Unread.js rename to frontend/src/routes/MapView/MapChat/Unread.js diff --git a/frontend/src/components/MapView/MapChat/index.js b/frontend/src/routes/MapView/MapChat/index.js similarity index 100% rename from frontend/src/components/MapView/MapChat/index.js rename to frontend/src/routes/MapView/MapChat/index.js diff --git a/frontend/src/components/MapView/MapInfoBox.js b/frontend/src/routes/MapView/MapInfoBox.js similarity index 100% rename from frontend/src/components/MapView/MapInfoBox.js rename to frontend/src/routes/MapView/MapInfoBox.js diff --git a/frontend/src/components/MapView/index.js b/frontend/src/routes/MapView/index.js similarity index 92% rename from frontend/src/components/MapView/index.js rename to frontend/src/routes/MapView/index.js index d2cc90fd..027b395d 100644 --- a/frontend/src/components/MapView/index.js +++ b/frontend/src/routes/MapView/index.js @@ -1,14 +1,14 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import ContextMenu from '../common/ContextMenu' -import DataVis from '../common/DataVis' -import UpperOptions from '../common/UpperOptions' -import InfoAndHelp from '../common/InfoAndHelp' +import ContextMenu from '../../components/ContextMenu' +import DataVis from '../../components/DataVis' +import UpperOptions from '../../components/UpperOptions' +import InfoAndHelp from '../../components/InfoAndHelp' import Instructions from './Instructions' -import VisualizationControls from '../common/VisualizationControls' +import VisualizationControls from '../../components/VisualizationControls' import MapChat from './MapChat' -import TopicCard from '../TopicCard' +import TopicCard from '../../components/TopicCard' export default class MapView extends Component { diff --git a/frontend/src/components/Maps/Header.js b/frontend/src/routes/Maps/Header.js similarity index 94% rename from frontend/src/components/Maps/Header.js rename to frontend/src/routes/Maps/Header.js index 770c0efb..931cb54f 100644 --- a/frontend/src/components/Maps/Header.js +++ b/frontend/src/routes/Maps/Header.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import NavBar from '../App/Navbar' -import NavBarLink from '../App/NavBarLink' +import NavBar from '../../components/Navbar' +import NavBarLink from '../../components/NavBarLink' class Header extends Component { render = () => { diff --git a/frontend/src/components/Maps/MapCard.js b/frontend/src/routes/Maps/MapCard.js similarity index 100% rename from frontend/src/components/Maps/MapCard.js rename to frontend/src/routes/Maps/MapCard.js diff --git a/frontend/src/components/Maps/MapperCard.js b/frontend/src/routes/Maps/MapperCard.js similarity index 100% rename from frontend/src/components/Maps/MapperCard.js rename to frontend/src/routes/Maps/MapperCard.js diff --git a/frontend/src/components/Maps/index.js b/frontend/src/routes/Maps/index.js similarity index 100% rename from frontend/src/components/Maps/index.js rename to frontend/src/routes/Maps/index.js diff --git a/frontend/src/components/Notifications/index.js b/frontend/src/routes/Notifications.js similarity index 80% rename from frontend/src/components/Notifications/index.js rename to frontend/src/routes/Notifications.js index a744e0c3..2814b33a 100644 --- a/frontend/src/components/Notifications/index.js +++ b/frontend/src/routes/Notifications.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' -import NavBar from '../App/Navbar' -import NavBarLink from '../App/NavBarLink' +import NavBar from '../components/Navbar' +import NavBarLink from '../components/NavBarLink' class Notifications extends Component { render = () => { diff --git a/frontend/src/components/TopicView/index.js b/frontend/src/routes/TopicView.js similarity index 89% rename from frontend/src/components/TopicView/index.js rename to frontend/src/routes/TopicView.js index 12d494b2..cc8e54ca 100644 --- a/frontend/src/components/TopicView/index.js +++ b/frontend/src/routes/TopicView.js @@ -1,12 +1,12 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import ContextMenu from '../common/ContextMenu' -import DataVis from '../common/DataVis' -import UpperOptions from '../common/UpperOptions' -import InfoAndHelp from '../common/InfoAndHelp' -import VisualizationControls from '../common/VisualizationControls' -import TopicCard from '../TopicCard' +import ContextMenu from '../components/ContextMenu' +import DataVis from '../components/DataVis' +import UpperOptions from '../components/UpperOptions' +import InfoAndHelp from '../components/InfoAndHelp' +import VisualizationControls from '../components/VisualizationControls' +import TopicCard from '../components/TopicCard' export default class TopicView extends Component { diff --git a/frontend/src/components/makeRoutes.js b/frontend/src/routes/makeRoutes.js similarity index 100% rename from frontend/src/components/makeRoutes.js rename to frontend/src/routes/makeRoutes.js From e89917931427abaa06266bc1d29798340d642ca3 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sat, 14 Oct 2017 14:44:10 -0400 Subject: [PATCH 30/40] Update first-deploy.md --- doc/production/first-deploy.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/doc/production/first-deploy.md b/doc/production/first-deploy.md index 44b6b659..b588299d 100644 --- a/doc/production/first-deploy.md +++ b/doc/production/first-deploy.md @@ -163,9 +163,9 @@ If your system uses systemd for init scripts, ptu the following code into `/etc/ User=metamaps Group=metamaps Environment=HOME=/home/metamaps - Environment=PATH="/usr/local/rvm/gems/ruby-2.3.0@metamaps/bin:/usr/local/rvm/gems/ruby-2.3.0@global/bin:/usr/local/rvm/rubies/ruby-2.3.0/bin:/usr/local/rvm/bin:/usr/local/bin:/usr/bin:/bin" - Environment=GEM_PATH="/usr/local/rvm/gems/ruby-2.3.0@metamaps:/usr/local/rvm/gems/ruby-2.3.0@global" - Environment=RAILS_ENV="production" + Environment=PATH=/usr/local/rvm/gems/ruby-2.3.0@metamaps/bin:/usr/local/rvm/gems/ruby-2.3.0@global/bin:/usr/local/rvm/rubies/ruby-2.3.0/bin:/usr/local/rvm/bin:/usr/local/bin:/usr/bin:/bin + Environment=GEM_PATH=/usr/local/rvm/gems/ruby-2.3.0@metamaps:/usr/local/rvm/gems/ruby-2.3.0@global + Environment=RAILS_ENV=production [Install] WantedBy=multi-user.target @@ -174,3 +174,13 @@ Then start the service and check the last ten lines of the log file to make sure sudo systemctl start metamaps_delayed_job # ??? how the heck do you check systemd logs?? + +##### initial service startup + sudo systemctl enable metamaps_delayed_job + sudo systemctl start metamaps_delayed_job + sudo systemctl status metamaps_delayed_job + +##### after changing + sudo systemctl daemon-reload + sudo systemctl restart metamaps_delayed_job + sudo systemctl status metamaps_delayed_job From 4a7595e76d14893e0892baefdb772f6cd86b9a7d Mon Sep 17 00:00:00 2001 From: Metamaps on Linode Date: Sat, 14 Oct 2017 14:54:45 -0400 Subject: [PATCH 31/40] fixup imports --- frontend/src/routes/Apps.js | 2 +- frontend/src/routes/Maps/Header.js | 2 +- frontend/src/routes/Notifications.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/routes/Apps.js b/frontend/src/routes/Apps.js index 94c7e32d..108bcc43 100644 --- a/frontend/src/routes/Apps.js +++ b/frontend/src/routes/Apps.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import NavBar from '../components/Navbar' +import NavBar from '../components/NavBar' import NavBarLink from '../components/NavBarLink' class Apps extends Component { diff --git a/frontend/src/routes/Maps/Header.js b/frontend/src/routes/Maps/Header.js index 931cb54f..103380c8 100644 --- a/frontend/src/routes/Maps/Header.js +++ b/frontend/src/routes/Maps/Header.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import NavBar from '../../components/Navbar' +import NavBar from '../../components/NavBar' import NavBarLink from '../../components/NavBarLink' class Header extends Component { diff --git a/frontend/src/routes/Notifications.js b/frontend/src/routes/Notifications.js index 2814b33a..f15bffe1 100644 --- a/frontend/src/routes/Notifications.js +++ b/frontend/src/routes/Notifications.js @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import NavBar from '../components/Navbar' +import NavBar from '../components/NavBar' import NavBarLink from '../components/NavBarLink' class Notifications extends Component { From 0a6e7918ef4d91714d8db8e5731067482c558561 Mon Sep 17 00:00:00 2001 From: Metamaps on Linode Date: Sat, 14 Oct 2017 14:55:07 -0400 Subject: [PATCH 32/40] fix docs building by reverting raml2html --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3be728c4..5cece282 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,6 @@ "sinon": "2.2.0" }, "optionalDependencies": { - "raml2html": "6.4.1" + "raml2html": "4.0.5" } } From 14dc3687cdb450746538b5df764ce1d89101dff2 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Sat, 14 Oct 2017 12:02:41 -0700 Subject: [PATCH 33/40] run perms:fix on assets:precompile --- lib/tasks/extensions.rake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/tasks/extensions.rake b/lib/tasks/extensions.rake index fc4a4855..96de0368 100644 --- a/lib/tasks/extensions.rake +++ b/lib/tasks/extensions.rake @@ -3,8 +3,13 @@ namespace :assets do task :js_compile do system 'npm install' system 'npm run build' + end + + task :production_ready do system 'bin/build-apidocs.sh' if Rails.env.production? + Rake::Task['perms:fix'].invoke if Rails.env.production? end end Rake::Task[:'assets:precompile'].enhance([:'assets:js_compile']) +Rake::Task[:'assets:precompile'].enhance([:'assets:production_ready']) From 9b52d0e0817761c34cccae95a8d2ff6ef70899e0 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sun, 15 Oct 2017 10:56:04 -0400 Subject: [PATCH 34/40] fix tests --- frontend/test/components/{common => }/InfoAndHelp.spec.js | 4 ++-- .../{components => routes}/MapView/ImportDialogBox.spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename frontend/test/components/{common => }/InfoAndHelp.spec.js (95%) rename frontend/test/{components => routes}/MapView/ImportDialogBox.spec.js (95%) diff --git a/frontend/test/components/common/InfoAndHelp.spec.js b/frontend/test/components/InfoAndHelp.spec.js similarity index 95% rename from frontend/test/components/common/InfoAndHelp.spec.js rename to frontend/test/components/InfoAndHelp.spec.js index 9880af0f..d0c604d2 100644 --- a/frontend/test/components/common/InfoAndHelp.spec.js +++ b/frontend/test/components/InfoAndHelp.spec.js @@ -4,8 +4,8 @@ import { expect } from 'chai' import { shallow } from 'enzyme' import sinon from 'sinon' -import InfoAndHelp from '../../../src/components/common/InfoAndHelp.js' -import MapInfoBox from '../../../src/components/MapView/MapInfoBox.js' +import InfoAndHelp from '../../src/components/InfoAndHelp.js' +import MapInfoBox from '../../src/routes/MapView/MapInfoBox.js' function assertTooltip({ wrapper, description, cssClass, tooltipText, callback }) { it(description, function() { diff --git a/frontend/test/components/MapView/ImportDialogBox.spec.js b/frontend/test/routes/MapView/ImportDialogBox.spec.js similarity index 95% rename from frontend/test/components/MapView/ImportDialogBox.spec.js rename to frontend/test/routes/MapView/ImportDialogBox.spec.js index 7151230e..20ac5542 100644 --- a/frontend/test/components/MapView/ImportDialogBox.spec.js +++ b/frontend/test/routes/MapView/ImportDialogBox.spec.js @@ -1,6 +1,6 @@ /* global describe, it */ import React from 'react' -import ImportDialogBox from '../../../src/components/MapView/ImportDialogBox.js' +import ImportDialogBox from '../../../src/routes/MapView/ImportDialogBox.js' import Dropzone from 'react-dropzone' import { expect } from 'chai' import { shallow } from 'enzyme' From b9fb4a282994613ab25a280557885ddc11505d5c Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sat, 14 Oct 2017 15:55:50 -0400 Subject: [PATCH 35/40] move admin header to react --- app/assets/stylesheets/admin.scss.erb | 11 +- app/assets/stylesheets/application.scss.erb | 130 -------------------- app/assets/stylesheets/clean.css.erb | 24 ++-- app/views/admin/_adminpanel.html.erb | 6 - app/views/metacode_sets/edit.html.erb | 4 +- app/views/metacode_sets/index.html.erb | 65 +++++----- app/views/metacode_sets/new.html.erb | 6 +- app/views/metacodes/edit.html.erb | 4 +- app/views/metacodes/index.html.erb | 53 ++++---- app/views/metacodes/new.html.erb | 2 +- frontend/src/components/NavBarLink.js | 8 +- frontend/src/routes/Admin.js | 18 +++ frontend/src/routes/makeRoutes.js | 13 +- 13 files changed, 115 insertions(+), 229 deletions(-) delete mode 100644 app/views/admin/_adminpanel.html.erb create mode 100644 frontend/src/routes/Admin.js diff --git a/app/assets/stylesheets/admin.scss.erb b/app/assets/stylesheets/admin.scss.erb index 8859457c..16b4ead5 100644 --- a/app/assets/stylesheets/admin.scss.erb +++ b/app/assets/stylesheets/admin.scss.erb @@ -56,16 +56,15 @@ } li.toggledOff { - opacity: 0.4; + opacity: 0.6; } } -.blackBox { +.centerContent { width: 760px; margin: 0 auto; padding: 80px 0 60px 20px; - background: rgba(0, 0, 0, 0.4); - color: white; + background: rgba(125, 125, 125, 0.4); overflow: hidden; position: relative; @@ -85,10 +84,10 @@ display: table-row; } tr:nth-child(odd) { - background: rgba(0, 0, 0, 0.2); + background: rgba(125, 125, 125, 0.2); } tr:nth-child(even) { - background: rgba(0, 0, 0, 0.3); + background: rgba(125, 125, 125, 0.3); } th, td { diff --git a/app/assets/stylesheets/application.scss.erb b/app/assets/stylesheets/application.scss.erb index 11330ef9..753124d2 100644 --- a/app/assets/stylesheets/application.scss.erb +++ b/app/assets/stylesheets/application.scss.erb @@ -2930,136 +2930,6 @@ and it won't be important on password protected instances */ color: #424242; } -/* Admin Pages */ - -.blackBox { - width: 760px; - margin: 0 auto; - padding: 20px 0 60px 20px; - background: rgba(0, 0, 0, 0.4); - color: white; - overflow: hidden; - position: relative; -} -.blackBox .metacodeSetsDescription { - width: 314px; -} -.blackBox td.metacodeSetDesc { - width: 314px; - word-wrap: break-word; -} -.blackBox .metacodeSetImage { - width: 36px; - height: 36px; - float: left; -} -.blackBox tr { - display: table-row; -} -.blackBox tr:nth-child(odd) { - background: rgba(0, 0, 0, 0.2); -} -.blackBox tr:nth-child(even) { - background: rgba(0, 0, 0, 0.3); -} -.blackBox th, -.blackBox td { - padding: 10px; -} -.blackBox td.iconURL { - max-width: 415px; - word-wrap: break-word; -} -.blackBox td.iconColor { - -} -.blackBox .field { - margin: 15px 0 5px; -} -.blackBox label { - float: left; - width: 100px; - margin-right: 15px; -} -.blackBox input[type="text"] { - width: 336px; - height: 32px; - font-size: 15px; - direction: ltr; - -webkit-appearance: none; - appearance: none; - display: inline-block; - margin: 0; - padding: 0 8px; - background: #fff; - border: 1px solid #d9d9d9; - border-top: 1px solid #c0c0c0; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-border-radius: 1px; - -moz-border-radius: 1px; - border-radius: 1px; - font: -webkit-small-control; - color: initial; - letter-spacing: normal; - word-spacing: normal; - text-transform: none; - text-indent: 0px; - text-shadow: none; - display: inline-block; - text-align: start; - font-family: arial; -} -.blackBox input[type="text"]:hover, -.blackBox textarea:hover { - border: 1px solid #b9b9b9; - border-top: 1px solid #a0a0a0; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); -} -.blackBox textarea { - padding: 8px; - border: 1px solid #d9d9d9; - border-top: 1px solid #c0c0c0; - resize: none; - font: -webkit-small-control; - letter-spacing: normal; - word-spacing: normal; - text-transform: none; - text-indent: 0px; - text-shadow: none; - text-align: start; - font-family: arial; - font-size: 15px; - line-height: 17px; - width: 318px; -} -.blackBox .allMetacodes { - padding: 5px 0; -} -.blackBox a.button { - margin-right: 20px; - line-height: 40px; -} -.blackBox a.button, -.blackBox input.add { - float: left; - margin-top: 5px; - height: 40px; - font-size: 17px; - width: auto; - padding: 0 30px; - cursor: pointer; - font-weight: normal; -} -.blackBox a.button:hover, -.blackBox input.add:hover { - -webkit-box-shadow: none; - box-shadow: none; -} - /* request */ .requestInvite { diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index 0790437e..445a4950 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -694,17 +694,16 @@ } .navBarButton { - color: #757575; + color: #757575; cursor: default; font-weight: normal; font-family: 'din-medium'; font-size: 14px; - height: 14px; - padding: 14px 8px 12px 40px; - border-bottom: 2px solid rgba(0,0,0,0); + padding: 0 8px; + border-bottom: 2px solid rgba(0,0,0,0); display: inline-block; - cursor: pointer; - position:relative; + cursor: pointer; + position:relative; } .navBarButton:hover, .navBarButton.active { text-decoration: none; @@ -733,9 +732,16 @@ background-repeat: no-repeat; width:32px; height:32px; - position:absolute; - top:5px; - left:5px; + margin-top:5px; + margin-left:5px; + margin-right: 5px; + display: inline-block; + vertical-align: top; +} + +.navBarLinkText { + padding: 11px 0 12px 0; + display: inline-block; } .navBarCenter .authedApps .navBarIcon { diff --git a/app/views/admin/_adminpanel.html.erb b/app/views/admin/_adminpanel.html.erb deleted file mode 100644 index c34c6cc6..00000000 --- a/app/views/admin/_adminpanel.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -<%= link_to 'Metacode Sets', metacode_sets_path, { :class => 'button' }%> -<%= link_to 'New Set', new_metacode_set_path, { :class => 'button' }%> -<%= link_to 'Metacodes', metacodes_path, { :class => 'button' }%> -<%= link_to 'New Metacode', new_metacode_path, { :class => 'button' }%> -
          -
          diff --git a/app/views/metacode_sets/edit.html.erb b/app/views/metacode_sets/edit.html.erb index 5377cba8..06f2cb06 100644 --- a/app/views/metacode_sets/edit.html.erb +++ b/app/views/metacode_sets/edit.html.erb @@ -1,6 +1,6 @@
          -
          - <%= render 'form' %> +
          + <%= render 'form' %>
          diff --git a/app/views/metacode_sets/index.html.erb b/app/views/metacode_sets/index.html.erb index e5015afa..68986895 100644 --- a/app/views/metacode_sets/index.html.erb +++ b/app/views/metacode_sets/index.html.erb @@ -1,37 +1,36 @@ -
          -
          - <%= render :partial => 'admin/adminpanel' %> -
          - - - - - - +
          +
          +
          +
          NameDescriptionMetacodes
          + + + + + - <% @metacode_sets.each do |metacode_set| %> - - - - + + + - - <% end %> -
          NameDescriptionMetacodes
          - <%= metacode_set.name %>
          - <%= link_to 'Edit', - edit_metacode_set_path(metacode_set) %> -
          - <%= link_to 'Delete', - metacode_set, method: :delete, - data: { confirm: 'Are you sure?' } %> -
          <%= metacode_set.desc %> - <% metacode_set.metacodes.each_with_index do |metacode, index| %> - - <% if (index+1)%4 == 0 %> -
          + <% @metacode_sets.each do |metacode_set| %> +
          + <%= metacode_set.name %>
          + <%= link_to 'Edit', + edit_metacode_set_path(metacode_set) %> +
          + <%= link_to 'Delete', + metacode_set, method: :delete, + data: { confirm: 'Are you sure?' } %> +
          <%= metacode_set.desc %> + <% metacode_set.metacodes.each_with_index do |metacode, index| %> + + <% if (index+1)%4 == 0 %> +
          + <% end %> <% end %> - <% end %> -
          -
          +
          + + + <% end %> +
          diff --git a/app/views/metacode_sets/new.html.erb b/app/views/metacode_sets/new.html.erb index 1ff5e852..06f2cb06 100644 --- a/app/views/metacode_sets/new.html.erb +++ b/app/views/metacode_sets/new.html.erb @@ -1,6 +1,6 @@
          -
          - <%= render 'form' %> +
          + <%= render 'form' %>
          @@ -11,4 +11,4 @@ <% end %> Metamaps.Admin.allMetacodes.push("<%= m.id %>"); <% end %> - \ No newline at end of file + diff --git a/app/views/metacodes/edit.html.erb b/app/views/metacodes/edit.html.erb index de8c85c1..4eb40656 100644 --- a/app/views/metacodes/edit.html.erb +++ b/app/views/metacodes/edit.html.erb @@ -1,5 +1,5 @@
          -
          +
          <%= render 'form' %>
          -
          \ No newline at end of file +
          diff --git a/app/views/metacodes/index.html.erb b/app/views/metacodes/index.html.erb index afe20706..f7f86e53 100644 --- a/app/views/metacodes/index.html.erb +++ b/app/views/metacodes/index.html.erb @@ -1,31 +1,30 @@
          -
          - <%= render :partial => 'admin/adminpanel' %> -
          - - - - - - - - +
          +
          +
          NameIconColor
          + + + + + + + - <% @metacodes.each do |metacode| %> - - - - <% if metacode.color %> - - <% else %> - - <% end %> - - - - <% end %> -
          NameIconColor
          <%= metacode.name %><%= metacode.icon %> - <%= metacode.color %> - <%= image_tag metacode.icon, width: 40 %><%= link_to 'Edit', edit_metacode_path(metacode) %>
          + <% @metacodes.each do |metacode| %> + + <%= metacode.name %> + <%= metacode.icon %> + <% if metacode.color %> + + <%= metacode.color %> + + <% else %> + + <% end %> + <%= image_tag metacode.icon, width: 40 %> + <%= link_to 'Edit', edit_metacode_path(metacode) %> + + <% end %> +
          diff --git a/app/views/metacodes/new.html.erb b/app/views/metacodes/new.html.erb index e10f28d1..81819734 100644 --- a/app/views/metacodes/new.html.erb +++ b/app/views/metacodes/new.html.erb @@ -1,5 +1,5 @@
          -
          +
          <%= render 'form' %>
          diff --git a/frontend/src/components/NavBarLink.js b/frontend/src/components/NavBarLink.js index 033de2e9..67feec88 100644 --- a/frontend/src/components/NavBarLink.js +++ b/frontend/src/components/NavBarLink.js @@ -48,15 +48,15 @@ class NavBarLink extends Component { if (hardReload) { return ( -
          - {text} + {linkClass &&
          } +
          {text}
          ) } return ( -
          - {text} + {linkClass &&
          } +
          {text}
          ) } diff --git a/frontend/src/routes/Admin.js b/frontend/src/routes/Admin.js new file mode 100644 index 00000000..d48664ef --- /dev/null +++ b/frontend/src/routes/Admin.js @@ -0,0 +1,18 @@ +import React, { Component } from 'react' +import NavBar from '../components/NavBar' +import NavBarLink from '../components/NavBarLink' + +class Admin extends Component { + render = () => { + return ( + + + + + + + ) + } +} + +export default Admin diff --git a/frontend/src/routes/makeRoutes.js b/frontend/src/routes/makeRoutes.js index 90146627..3ce9c509 100644 --- a/frontend/src/routes/makeRoutes.js +++ b/frontend/src/routes/makeRoutes.js @@ -1,5 +1,6 @@ import React from 'react' import { Route, IndexRoute } from 'react-router' +import Admin from './Admin' import App from './App' import Apps from './Apps' import Maps from './Maps' @@ -42,14 +43,14 @@ export default function makeRoutes (currentUser) { - - - + + + - - - + + + From 9af3754bc82de2412fa879d600b2097327f72004 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Sun, 15 Oct 2017 14:07:11 -0400 Subject: [PATCH 36/40] follow created maps as default setting if you are the map creator, we'd like to set you up to be following it by default --- app/models/user_preference.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index c881c4ac..d92eb7a9 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -23,7 +23,7 @@ class UserPreference def initialize_follow_settings @follow_topic_on_created = false @follow_topic_on_contributed = false - @follow_map_on_created = false + @follow_map_on_created = true @follow_map_on_contributed = false end end From 8b492d6dc8c17647285ec3641bc0ec7a9e9a8739 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 17 Oct 2017 01:28:07 -0400 Subject: [PATCH 37/40] made the mistake of not checking all cases --- .../map_activity_mailer/daily_summary.html.erb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/map_activity_mailer/daily_summary.html.erb b/app/views/map_activity_mailer/daily_summary.html.erb index 99fd53f7..827e0e99 100644 --- a/app/views/map_activity_mailer/daily_summary.html.erb +++ b/app/views/map_activity_mailer/daily_summary.html.erb @@ -15,22 +15,22 @@ <%= link_to @map.name, map_url(@map) %>

          - <% if @summary_data[:stats][:messages_sent] > 0 %> + <% if !@summary_data[:stats][:messages_sent].nil? %>

          <%= pluralize(@summary_data[:stats][:messages_sent], 'message') %>

          <% end %> - <% if @summary_data[:stats][:topics_added] > 0 %> + <% if !@summary_data[:stats][:topics_added].nil? %>

          <%= pluralize(@summary_data[:stats][:topics_added], 'topic') %> added

          <% end %> - <% if @summary_data[:stats][:synapses_added] > 0 %> + <% if !@summary_data[:stats][:synapses_added].nil? %>

          <%= pluralize(@summary_data[:stats][:synapses_added], 'synapse') %> added

          <% end %> - <% if @summary_data[:stats][:topics_moved] > 0 %> + <% if !@summary_data[:stats][:topics_moved].nil? %>

          <%= pluralize(@summary_data[:stats][:topics_moved], 'topic') %> moved

          <% end %> - <% if @summary_data[:stats][:topics_removed] > 0 %> + <% if !@summary_data[:stats][:topics_removed].nil? %>

          <%= pluralize(@summary_data[:stats][:topics_removed], 'topic') %> removed

          <% end %> - <% if @summary_data[:stats][:synapses_removed] > 0 %> + <% if !@summary_data[:stats][:synapses_removed].nil? %>

          <%= pluralize(@summary_data[:stats][:synapses_removed], 'synapse') %> removed

          <% end %>
          @@ -61,7 +61,7 @@
          <% end %> - + <% if @summary_data[:topics_removed] || @summary_data[:synapses_removed] %>
          <% if @summary_data[:topics_removed] %> From cbf44e3dfef83364293dab45d6adff80a217f904 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Tue, 17 Oct 2017 08:28:00 -0700 Subject: [PATCH 38/40] add one view test --- .../daily_summary.html.erb_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 spec/views/map_activity_mailer/daily_summary.html.erb_spec.rb diff --git a/spec/views/map_activity_mailer/daily_summary.html.erb_spec.rb b/spec/views/map_activity_mailer/daily_summary.html.erb_spec.rb new file mode 100644 index 00000000..f581a1d2 --- /dev/null +++ b/spec/views/map_activity_mailer/daily_summary.html.erb_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'map_activity_mailer/daily_summary.html.erb' do + it 'displays messages sent' do + assign(:user, create(:user)) + assign(:map, create(:map)) + assign(:summary_data, stats: { + messages_sent: 5 + }) + + render + + expect(rendered).to match(/5 messages/) + end +end From 901eb4a513766c6504c5a3626e7384e834fb4095 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 17 Oct 2017 12:06:37 -0400 Subject: [PATCH 39/40] Update daily_summary.html.erb --- app/views/map_activity_mailer/daily_summary.html.erb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/map_activity_mailer/daily_summary.html.erb b/app/views/map_activity_mailer/daily_summary.html.erb index 827e0e99..d28d00a5 100644 --- a/app/views/map_activity_mailer/daily_summary.html.erb +++ b/app/views/map_activity_mailer/daily_summary.html.erb @@ -15,22 +15,22 @@ <%= link_to @map.name, map_url(@map) %>

          - <% if !@summary_data[:stats][:messages_sent].nil? %> + <% if @summary_data[:stats][:messages_sent] %>

          <%= pluralize(@summary_data[:stats][:messages_sent], 'message') %>

          <% end %> - <% if !@summary_data[:stats][:topics_added].nil? %> + <% if @summary_data[:stats][:topics_added] %>

          <%= pluralize(@summary_data[:stats][:topics_added], 'topic') %> added

          <% end %> - <% if !@summary_data[:stats][:synapses_added].nil? %> + <% if @summary_data[:stats][:synapses_added] %>

          <%= pluralize(@summary_data[:stats][:synapses_added], 'synapse') %> added

          <% end %> - <% if !@summary_data[:stats][:topics_moved].nil? %> + <% if @summary_data[:stats][:topics_moved] %>

          <%= pluralize(@summary_data[:stats][:topics_moved], 'topic') %> moved

          <% end %> - <% if !@summary_data[:stats][:topics_removed].nil? %> + <% if @summary_data[:stats][:topics_removed] %>

          <%= pluralize(@summary_data[:stats][:topics_removed], 'topic') %> removed

          <% end %> - <% if !@summary_data[:stats][:synapses_removed].nil? %> + <% if @summary_data[:stats][:synapses_removed] %>

          <%= pluralize(@summary_data[:stats][:synapses_removed], 'synapse') %> removed

          <% end %>
          From e66498a861615423bcee08b09e85929ae6e98543 Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 17 Oct 2017 12:16:03 -0400 Subject: [PATCH 40/40] add logging for delayedJob --- config/initializers/delayed_job.rb | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/config/initializers/delayed_job.rb b/config/initializers/delayed_job.rb index d3132394..c4725b19 100644 --- a/config/initializers/delayed_job.rb +++ b/config/initializers/delayed_job.rb @@ -1,9 +1,14 @@ -Delayed::Worker.class_eval do - - def handle_failed_job_with_notification(job, error) - handle_failed_job_without_notification(job, error) - ExceptionNotifier.notify_exception(error) - end - alias_method_chain :handle_failed_job, :notification +# frozen_string_literal: true +module ExceptionNotifierInDelayedJob + def handle_failed_job(job, error) + super + ExceptionNotfier.notify_exception(error) + end end + +Delayed::Worker.class_eval do + prepend ExceptionNotifierInDelayedJob +end + +Delayed::Worker.logger = Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))