diff --git a/app/assets/stylesheets/application.scss.erb b/app/assets/stylesheets/application.scss.erb index 0c7b9976..87f2b4c5 100644 --- a/app/assets/stylesheets/application.scss.erb +++ b/app/assets/stylesheets/application.scss.erb @@ -775,6 +775,7 @@ label { } .sidebarAccountIcon img { border-radius: 16px; + width: 32px; } .sidebarAccountBox { display: none; @@ -3148,4 +3149,4 @@ script.data-gratipay-username { background: #FFF; cursor: pointer; font-family: din-regular; -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/base.scss.erb b/app/assets/stylesheets/base.scss.erb index 355f5652..f4e247f2 100644 --- a/app/assets/stylesheets/base.scss.erb +++ b/app/assets/stylesheets/base.scss.erb @@ -54,7 +54,6 @@ width:100%; height:100%; position: absolute; - display: none; } .showcard .permission { @@ -233,7 +232,7 @@ background-repeat:no-repeat; } } - + .contributor { bottom: 7px; margin-left: 40px; diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index 1582404e..69bbecf7 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -46,26 +46,9 @@ transition-timing-function: ease-in-out; }*/ -.mapElement { - display: none; -} -.mapPage .mapElement, -.topicPage .mapElement { - display: block; -} -.mapPage .mapElementHidden, -.topicPage .mapElement.mapInfoBox, -.topicPage .mapElement.importDialog { - display:none; -} -.topicPage .starMap { - display: none; -} - /* loading */ #loading { - display: none; width: 28px; height: 28px; position: fixed; @@ -236,6 +219,14 @@ /* end upperRightUI */ +/* map wrapper */ +.mapWrapper { + position:absolute; + width: 100%; + height: 100%; +} + +/* end map wrapper */ /* yield */ @@ -356,22 +347,15 @@ /* infoAndHelp */ -.mapPage .infoAndHelp, .topicPage .infoAndHelp { - right: 70px; -} .mapPage .openCheatsheet .tooltipsAbove, .topicPage .openCheatsheet .tooltipsAbove { right: 1px; left: auto; } -.unauthenticated .homePage .infoAndHelp { - display:none; -} - .infoAndHelp { position: absolute; bottom: 20px; - right: 20px; + right: 70px; z-index: 3; width: auto; font-style: italic; @@ -392,16 +376,12 @@ } .mapInfoIcon { position: relative; - top: 56px; /* puts it just offscreen */ - background-image: url(<%= asset_path('mapinfo_sprite.png') %>); - background-repeat:no-repeat; + background-image: url(<%= asset_path('mapinfo_sprite.png') %>); + background-repeat:no-repeat; } .mapInfoIcon:hover { background-position: 0 -32px; } -.mapPage .mapInfoIcon { - top: 0; -} .starMap { background-image: url(<%= asset_path('starmap_sprite.png') %>); @@ -430,24 +410,17 @@ .mapControls { position: absolute; bottom: 24px; - right:-32px; /* puts it just offscreen */ + right:24px; width:32px; z-index: 3; } -.mapPage .mapControls, .topicPage .mapControls { - right: 24px; -} - -.topicPage .zoomExtents { - display: none; -} .mapControl { width:32px; height:32px; background-color: #424242; - background-repeat: no-repeat; - background-position: 0 0; + background-repeat: no-repeat; + background-position: 0 0; cursor:pointer; } @@ -671,8 +644,11 @@ /* explore maps */ -#explore { - display: none; +#react-app { + position: absolute; + height: 100%; + width: 100%; + overflow-y: auto; } #exploreMaps { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4bb5be10..55abe8e3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,7 +4,7 @@ class ApplicationController < ActionController::Base include Pundit include PunditExtra rescue_from Pundit::NotAuthorizedError, with: :handle_unauthorized - protect_from_forgery(with: :exception) + #protect_from_forgery(with: :exception) before_action :invite_link before_action :prepare_exception_notifier diff --git a/app/views/layouts/_account.html.erb b/app/views/layouts/_account.html.erb deleted file mode 100644 index 94f69f62..00000000 --- a/app/views/layouts/_account.html.erb +++ /dev/null @@ -1,60 +0,0 @@ -<%# - # @file - # The inner HTML of the account box that comes up in the bottom left - #%> - -<% if current_user %> - <% account = current_user %> - <%= image_tag account.image.url(:sixtyfour), :size => "48x48", :class => "sidebarAccountImage" %> -

<%= account.name.split[0...1][0] %>

- -<% else %> - <%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { class: "loginAnywhere" }) do |f| %> -
-
- <%= f.email_field :email, :placeholder => "Email" %> -
-
- <%= f.password_field :password, :placeholder => "Password" %> -
-
<%= f.submit "SIGN IN" %>
- <% if devise_mapping.rememberable? -%> -
- <%= f.label :remember_me, "Stay signed in" %> - <%= f.check_box :remember_me %> -
-
- <% end -%> -
-
- <%- if devise_mapping.recoverable? && controller_name != 'passwords' %> - <%= link_to "Forgot password?", new_password_path(resource_name) %> - <% end -%> -
- <% end %> -<% end %> - -<% # Rails.logger.info(stored_location_for(:user)) %> diff --git a/app/views/layouts/_lowermapelements.html.erb b/app/views/layouts/_lowermapelements.html.erb deleted file mode 100644 index e3d5aeaf..00000000 --- a/app/views/layouts/_lowermapelements.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -
-
Center View
-
Zoom In
-
Zoom Out
-
- -
- <%= render :partial => 'maps/mapinfobox' %> - - <% starred = current_user && @map && current_user.starred_map?(@map) - starClass = starred ? 'starred' : '' - tooltip = starred ? 'Star' : 'Unstar' %> -
<%= tooltip %>
-
Map Info
-
Help
-
-
diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb deleted file mode 100644 index 7ef76bad..00000000 --- a/app/views/layouts/_upperelements.html.erb +++ /dev/null @@ -1,107 +0,0 @@ - - - -
- -
- <%= 'data-router=true' %><% end %>>METAMAPS -
- - -
- -
-
-
-
- - <% request = current_user && @map && @allrequests.find{|a| a.user == current_user} - className = (@map and not policy(@map).update?) ? 'isViewOnly ' : '' - if @map - className += 'sendRequest' if not request - className += 'sentRequest' if request and not request.answered - className += 'requestDenied' if request and request.answered and not request.approved - end %> - -
-
View Only
- <% if current_user %> -
Request Access
-
Request Pending
-
Request Not Accepted
- <% end %> -
-
-
- -
-
- <% if current_user %> -
-
- Import Data -
-
- <% end %> - - -
-
Filter
-
- <%= render :partial => 'shared/filterBox' %> -
-
- - <% if current_user %> - -
-
Save To New Map
-
- <% end %> - -
-
- - <% if current_user %> - - -
- Create New Map -
-
- <% end %> - - - <% if current_user.present? %> - - <%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_unread_notification_count > 0 ? 'unread' : 'read'}" do %> -
- Notifications -
- <% if user_unread_notification_count > 0 %> -
- <% end %> - <% end %> -
- <% end %> - - - <% if !(controller_name == "sessions" && action_name == "new") %> -
-
Account
- <% if current_user && current_user.image %> - <%= image_tag current_user.image.url(:thirtytwo), :size => "32x32" %> - <% elsif !current_user %> - SIGN IN -
- <% end %> -
-
- <%= render :partial => 'layouts/account' %> -
-
- <% end %> -
-
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index daa16d02..227d324d 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,83 +6,34 @@ #%> <%= render :partial => 'layouts/head' %> - controller-<%= controller_name %> action-<%= action_name %>"> - -
- - - - <%= content_tag :div, class: "main" do %> - - <% classes = action_name == "home" ? "homePage" : "" - classes += action_name == "home" && authenticated? ? " explorePage" : "" - classes += controller_name == "maps" && action_name == "index" ? " explorePage" : "" - if controller_name == "maps" && action_name == "show" - classes += " mapPage" - if policy(@map).update? - classes += " canEditMap" - end - if @map.permission == "commons" - classes += " commonsMap" - end - end - classes += controller_name == "topics" && action_name == "show" ? " topicPage" : "" - %> - -
- - <%= render :partial => 'layouts/upperelements', :locals => { :noHardHomeLink => controller_name == "notifications" ? true : false } %> - - <%= yield %> - -
- <% if authenticated? %> - <% # for creating and pulling in topics and synapses %> - <% if controller_name == 'maps' && action_name == "conversation" %> - <%= render :partial => 'maps/newtopicsecret' %> - <% else %> - <%= render :partial => 'maps/newtopic' %> - <% end %> - <%= render :partial => 'maps/newsynapse' %> - <% # for populating the change metacode list on the topic card %> - <%= render :partial => 'shared/metacodeoptions' %> - <% end %> - <%= render :partial => 'layouts/lowermapelements' %> - -
- - <% if !(controller_name == 'maps' && action_name == "conversation") %> -
-
- Double-click to
add a topic -
-
- Use Tab & Shift+Tab to select a metacode -
-
- Press Enter to add the topic -
-
- <% end %> - -
- <%= render :partial => 'layouts/mobilemenu' %> - -

- <% if devise_error_messages? %> - <%= devise_error_messages! %> - <% end %> - <% if notice %> - <%= notice %> - <% end %> - <% if alert %> - <%= alert %> - <% end %> -

-
-
- + <%= content_tag :div, class: "main", id: "react-app" do %> + <%= yield %> + <%= render :partial => 'layouts/mobilemenu' %> <% end %> - + <% if devise_error_messages? %> + <%= devise_error_messages! %> + <% end %> + <% if notice %> + <%= notice %> + <% end %> + <% if alert %> + <%= alert %> + <% end %> + <% if authenticated? %> + <% # for creating and pulling in topics and synapses %> + <% if controller_name == 'maps' && action_name == "conversation" %> + <%= render :partial => 'maps/newtopicsecret' %> + <% else %> + <%= render :partial => 'maps/newtopic' %> + <% end %> + <%= render :partial => 'maps/newsynapse' %> + <% # for populating the change metacode list on the topic card %> + <%= render :partial => 'shared/metacodeoptions' %> + <% end %> +
+ <%= render :partial => 'layouts/foot' %> diff --git a/frontend/src/Metamaps/GlobalUI/ImportDialog.js b/frontend/src/Metamaps/GlobalUI/ImportDialog.js index 30215fa1..774c2190 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/ImportDialogBox' +import ImportDialogBox from '../../components/MapView/ImportDialogBox' import PasteInput from '../PasteInput' import Map from '../Map' diff --git a/frontend/src/Metamaps/GlobalUI/NotificationIcon.js b/frontend/src/Metamaps/GlobalUI/NotificationIcon.js deleted file mode 100644 index 1e9ff3bd..00000000 --- a/frontend/src/Metamaps/GlobalUI/NotificationIcon.js +++ /dev/null @@ -1,30 +0,0 @@ -/* global $ */ - -import React from 'react' -import ReactDOM from 'react-dom' - -import Active from '../Active' -import NotificationIconComponent from '../../components/NotificationIcon' - -const NotificationIcon = { - unreadNotificationsCount: null, - - init: function(serverData) { - const self = NotificationIcon - self.unreadNotificationsCount = serverData.unreadNotificationsCount - self.render() - }, - render: function(newUnreadCount = null) { - if (newUnreadCount !== null) { - NotificationIcon.unreadNotificationsCount = newUnreadCount - } - - if (Active.Mapper !== null) { - ReactDOM.render(React.createElement(NotificationIconComponent, { - unreadNotificationsCount: NotificationIcon.unreadNotificationsCount - }), $('#notification_icon').get(0)) - } - } -} - -export default NotificationIcon diff --git a/frontend/src/Metamaps/GlobalUI/ReactApp.js b/frontend/src/Metamaps/GlobalUI/ReactApp.js new file mode 100644 index 00000000..7420bced --- /dev/null +++ b/frontend/src/Metamaps/GlobalUI/ReactApp.js @@ -0,0 +1,133 @@ +/* global $ */ + +import React from 'react' +import ReactDOM from 'react-dom' +import { Router, browserHistory } from 'react-router' + +import { merge } from 'lodash' + +import { notifyUser } from './index.js' +import Active from '../Active' +import DataModel from '../DataModel' +import { ExploreMaps, ChatView, TopicCard } from '../Views' +import Realtime from '../Realtime' +import Map from '../Map' +import Topic from '../Topic' +import makeRoutes from '../../components/makeRoutes' +let routes + +const ReactApp = { + mapId: null, + unreadNotificationsCount: 0, + mapIsStarred: false, + init: function(serverData) { + const self = ReactApp + self.unreadNotificationsCount = serverData.unreadNotificationsCount + self.mapIsStarred = serverData.mapIsStarred + routes = makeRoutes() + self.render() + }, + handleUpdate: function(location) { + const self = ReactApp + // TODO: also handle page title updates + switch (this.state.location.pathname.split('/')[1]) { + case '': + case 'explore': + ExploreMaps.updateFromPath(this.state.location.pathname) + self.mapId = null + Active.Map = null + Active.Topic = null + break + case 'topics': + break + case 'maps': + self.mapId = this.state.location.pathname.split('/')[2] + break + } + self.render() + // track using google analytics here + //window.ga && window.ga('send', 'pageview', location.pathname, {title: document.title}) + }, + render: function() { + const self = ReactApp + const createElement = (Component, props) => + const app = + ReactDOM.render(app, document.getElementById('react-app')) + }, + getProps: function() { + const self = ReactApp + return merge({ + unreadNotificationsCount: self.unreadNotificationsCount, + currentUser: Active.Mapper + }, + self.getMapProps(), + self.getTopicProps(), + self.getMapsProps(), + self.getTopicCardProps(), + self.getChatProps()) + }, + getMapProps: function() { + const self = ReactApp + return { + mapId: self.mapId, + map: Active.Map, + mapIsStarred: self.mapIsStarred, + endActiveMap: Map.end, + launchNewMap: Map.launch + } + }, + getTopicCardProps: function() { + const self = ReactApp + return { + openTopic: TopicCard.openTopic, + metacodeSets: TopicCard.metacodeSets, + updateTopic: TopicCard.updateTopic, + onTopicFollow: TopicCard.onTopicFollow, + redrawCanvas: TopicCard.redrawCanvas + } + }, + getTopicProps: function() { + const self = ReactApp + return { + topic: Active.Topic + } + }, + getMapsProps: function() { + const self = ReactApp + return { + section: ExploreMaps.collection && ExploreMaps.collection.id, + maps: ExploreMaps.collection, + juntoState: Realtime.juntoState, + moreToLoad: ExploreMaps.collection && ExploreMaps.collection.page !== 'loadedAll', + user: ExploreMaps.collection && ExploreMaps.collection.id === 'mapper' ? ExploreMaps.mapper : null, + loadMore: ExploreMaps.loadMore, + pending: ExploreMaps.pending, + onStar: ExploreMaps.onStar, + onRequest: ExploreMaps.onRequest, + onMapFollow: ExploreMaps.onMapFollow + } + }, + getChatProps: function() { + const self = ReactApp + return { + conversationLive: ChatView.conversationLive, + isParticipating: ChatView.isParticipating, + onOpen: ChatView.onOpen, + onClose: ChatView.onClose, + leaveCall: Realtime.leaveCall, + joinCall: Realtime.joinCall, + inviteACall: Realtime.inviteACall, + inviteToJoin: Realtime.inviteToJoin, + participants: ChatView.participants ? ChatView.participants.models.map(p => p.attributes) : [], + messages: ChatView.messages ? ChatView.messages.models.map(m => m.attributes) : [], + videoToggleClick: ChatView.videoToggleClick, + cursorToggleClick: ChatView.cursorToggleClick, + soundToggleClick: ChatView.soundToggleClick, + inputBlur: ChatView.inputBlur, + inputFocus: ChatView.inputFocus, + handleInputMessage: ChatView.handleInputMessage + } + } +} + +export default ReactApp diff --git a/frontend/src/Metamaps/GlobalUI/Search.js b/frontend/src/Metamaps/GlobalUI/Search.js index a37b62a8..da279804 100644 --- a/frontend/src/Metamaps/GlobalUI/Search.js +++ b/frontend/src/Metamaps/GlobalUI/Search.js @@ -1,7 +1,6 @@ /* global $, Hogan, Bloodhound, CanvasLoader */ import Active from '../Active' -import Router from '../Router' const Search = { locked: false, @@ -189,11 +188,11 @@ const Search = { if (['topic', 'map', 'mapper'].indexOf(datum.rtype) !== -1) { if (datum.rtype === 'topic') { - Router.topics(datum.id) + // TODO: navigate to topic datum.id } else if (datum.rtype === 'map') { - Router.maps(datum.id) + // TODO: navigate to map datum.id } else if (datum.rtype === 'mapper') { - Router.explore('mapper', datum.id) + // TODO: navigate to mapper section datum.id } } }, diff --git a/frontend/src/Metamaps/GlobalUI/index.js b/frontend/src/Metamaps/GlobalUI/index.js index a1d2bbff..14185abf 100644 --- a/frontend/src/Metamaps/GlobalUI/index.js +++ b/frontend/src/Metamaps/GlobalUI/index.js @@ -4,11 +4,11 @@ import clipboard from 'clipboard-js' import Create from '../Create' +import ReactApp from './ReactApp' import Search from './Search' import CreateMap from './CreateMap' import Account from './Account' import ImportDialog from './ImportDialog' -import NotificationIcon from './NotificationIcon' const GlobalUI = { notifyTimeout: null, @@ -18,11 +18,11 @@ const GlobalUI = { init: function(serverData) { const self = GlobalUI + self.ReactApp.init(serverData) self.Search.init(serverData) self.CreateMap.init(serverData) self.Account.init(serverData) self.ImportDialog.init(serverData, self.openLightbox, self.closeLightbox) - self.NotificationIcon.init(serverData) if ($('#toast').html().trim()) self.notifyUser($('#toast').html()) @@ -153,5 +153,5 @@ const GlobalUI = { } } -export { Search, CreateMap, Account, ImportDialog, NotificationIcon } +export { ReactApp, Search, CreateMap, Account, ImportDialog } export default GlobalUI diff --git a/frontend/src/Metamaps/Map/InfoBox.js b/frontend/src/Metamaps/Map/InfoBox.js index 1949e96e..312ac68d 100644 --- a/frontend/src/Metamaps/Map/InfoBox.js +++ b/frontend/src/Metamaps/Map/InfoBox.js @@ -5,7 +5,6 @@ import outdent from 'outdent' import Active from '../Active' import DataModel from '../DataModel' import GlobalUI from '../GlobalUI' -import Router from '../Router' import Util from '../Util' const InfoBox = { @@ -192,7 +191,6 @@ const InfoBox = { $('.mapContributors .tip').unbind().click(function(event) { event.stopPropagation() }) - $('.mapContributors .tip li a').click(Router.intercept) $('.mapInfoBox').unbind('.hideTip').bind('click.hideTip', function() { $('.mapContributors .tip').hide() @@ -393,7 +391,7 @@ const InfoBox = { DataModel.Maps.Mine.remove(map) DataModel.Maps.Shared.remove(map) map.destroy() - Router.home() + // TODO: navigate home GlobalUI.notifyUser('Map eliminated') } else if (!authorized) { window.alert("Hey now. We can't just go around willy nilly deleting other people's maps now can we? Run off and find something constructive to do, eh?") diff --git a/frontend/src/Metamaps/Map/index.js b/frontend/src/Metamaps/Map/index.js index e3c3bbc6..94a749c1 100644 --- a/frontend/src/Metamaps/Map/index.js +++ b/frontend/src/Metamaps/Map/index.js @@ -13,7 +13,6 @@ import GlobalUI from '../GlobalUI' import JIT from '../JIT' import Loading from '../Loading' import Realtime from '../Realtime' -import Router from '../Router' import Selected from '../Selected' import SynapseCard from '../SynapseCard' import TopicCard from '../Views/TopicCard' @@ -132,7 +131,7 @@ const Map = { // for mobile $('#header_content').html(map.get('name')) } - + Loading.show() $.ajax({ url: '/maps/' + id + '/contains.json', success: start @@ -232,7 +231,7 @@ const Map = { var map = Active.Map DataModel.Maps.Active.remove(map) DataModel.Maps.Featured.remove(map) - Router.home() + // TODO: navigate home GlobalUI.notifyUser('Sorry! That map has been changed to Private.') }, cantEditNow: function() { @@ -245,7 +244,7 @@ const Map = { confirmString += 'Do you want to reload and enable realtime collaboration?' var c = window.confirm(confirmString) if (c) { - Router.maps(Active.Map.id) + // TODO: reload the map somehow } }, editedByActiveMapper: function() { diff --git a/frontend/src/Metamaps/Router.js b/frontend/src/Metamaps/Router.js deleted file mode 100644 index 6df1c264..00000000 --- a/frontend/src/Metamaps/Router.js +++ /dev/null @@ -1,244 +0,0 @@ -/* global $ */ - -import Backbone from 'backbone' -try { Backbone.$ = window.$ } catch (err) {} - -import Active from './Active' -import DataModel from './DataModel' -import GlobalUI from './GlobalUI' -import Loading from './Loading' -import Map from './Map' -import Topic from './Topic' -import Views from './Views' -import Visualize from './Visualize' - -const _Router = Backbone.Router.extend({ - currentPage: '', - currentSection: '', - timeoutId: undefined, - routes: { - '': 'home', // #home - 'explore/:section': 'explore', // #explore/active - 'explore/:section/:id': 'explore', // #explore/mapper/1234 - 'maps/:id': 'maps', // #maps/7 - 'topics/:id': 'topics' // #topics/7 - }, - home: function() { - let self = this - clearTimeout(this.timeoutId) - - if (Active.Mapper) document.title = 'Explore Active Maps | Metamaps' - else document.title = 'Home | Metamaps' - - this.currentSection = '' - this.currentPage = '' - $('.wrapper').removeClass('mapPage topicPage') - - var classes = Active.Mapper ? 'homePage explorePage' : 'homePage' - $('.wrapper').addClass(classes) - - var navigate = function() { - self.timeoutId = setTimeout(function() { - self.navigateAndTrack('') - }, 300) - } - - // all this only for the logged in home page - if (Active.Mapper) { - $('.homeButton a').attr('href', '/') - GlobalUI.hideDiv('#yield') - - GlobalUI.showDiv('#explore') - - Views.ExploreMaps.setCollection(DataModel.Maps.Active) - if (DataModel.Maps.Active.length === 0) { - Views.ExploreMaps.pending = true - DataModel.Maps.Active.getMaps(navigate) // this will trigger an explore maps render - } else { - Views.ExploreMaps.render(navigate) - } - } else { - // logged out home page - GlobalUI.hideDiv('#explore') - GlobalUI.showDiv('#yield') - this.timeoutId = setTimeout(navigate, 500) - } - - GlobalUI.hideDiv('#infovis') - GlobalUI.hideDiv('#instructions') - Map.end() - Topic.end() - Active.Map = null - Active.Topic = null - }, - explore: function(section, id) { - var self = this - clearTimeout(this.timeoutId) - - // just capitalize the variable section - // either 'featured', 'mapper', or 'active' - var capitalize = section.charAt(0).toUpperCase() + section.slice(1) - - if (section === 'shared' || section === 'featured' || section === 'active' || section === 'starred') { - document.title = 'Explore ' + capitalize + ' Maps | Metamaps' - } else if (section === 'mapper') { - $.ajax({ - url: '/users/' + id + '.json', - success: function(response) { - document.title = response.name + ' | Metamaps' - }, - error: function() {} - }) - } else if (section === 'mine') { - document.title = 'Explore My Maps | Metamaps' - } - - if (Active.Mapper && section !== 'mapper') $('.homeButton a').attr('href', '/explore/' + section) - $('.wrapper').removeClass('homePage mapPage topicPage') - $('.wrapper').addClass('explorePage') - - this.currentSection = 'explore' - this.currentPage = section - - // this will mean it's a mapper page being loaded - if (id) { - if (DataModel.Maps.Mapper.mapperId !== id) { - // empty the collection if we are trying to load the maps - // collection of a different mapper than we had previously - DataModel.Maps.Mapper.reset() - DataModel.Maps.Mapper.page = 1 - } - DataModel.Maps.Mapper.mapperId = id - } - - Views.ExploreMaps.setCollection(DataModel.Maps[capitalize]) - - var navigate = function() { - var path = '/explore/' + self.currentPage - - // alter url if for mapper profile page - if (self.currentPage === 'mapper') { - path += '/' + DataModel.Maps.Mapper.mapperId - } - - self.navigateAndTrack(path) - } - var navigateTimeout = function() { - self.timeoutId = setTimeout(navigate, 300) - } - if (DataModel.Maps[capitalize].length === 0) { - Loading.show() - Views.ExploreMaps.pending = true - setTimeout(function() { - DataModel.Maps[capitalize].getMaps(navigate) // this will trigger an explore maps render - }, 300) // wait 300 milliseconds till the other animations are done to do the fetch - } else { - if (id) { - Views.ExploreMaps.fetchUserThenRender(navigateTimeout) - } else { - Views.ExploreMaps.render(navigateTimeout) - } - } - - GlobalUI.showDiv('#explore') - GlobalUI.hideDiv('#yield') - GlobalUI.hideDiv('#infovis') - GlobalUI.hideDiv('#instructions') - Map.end() - Topic.end() - Active.Map = null - Active.Topic = null - }, - maps: function(id) { - clearTimeout(this.timeoutId) - - this.currentSection = 'map' - this.currentPage = id - - $('.wrapper').removeClass('homePage explorePage topicPage') - $('.wrapper').addClass('mapPage') - // another class will be added to wrapper if you - // can edit this map '.canEditMap' - - GlobalUI.hideDiv('#yield') - GlobalUI.hideDiv('#explore') - - // clear the visualization, if there was one, before showing its div again - if (Visualize.mGraph) { - Visualize.clearVisualization() - } - GlobalUI.showDiv('#infovis') - Topic.end() - Active.Topic = null - - Loading.show() - Map.end() - Map.launch(id) - }, - topics: function(id) { - clearTimeout(this.timeoutId) - - this.currentSection = 'topic' - this.currentPage = id - - $('.wrapper').removeClass('homePage explorePage mapPage') - $('.wrapper').addClass('topicPage') - - GlobalUI.hideDiv('#yield') - GlobalUI.hideDiv('#explore') - - // clear the visualization, if there was one, before showing its div again - if (Visualize.mGraph) { - Visualize.clearVisualization() - } - GlobalUI.showDiv('#infovis') - Map.end() - Active.Map = null - - Topic.end() - Topic.launch(id) - } -}) - -const Router = new _Router() - -Router.navigateAndTrack = (fragment, options) => { - Router.navigate(fragment, options) - window.ga && window.ga('send', 'pageview', location.pathname, {title: document.title}) -} - -Router.intercept = function(evt) { - var segments - - var href = { - prop: $(this).prop('href'), - attr: $(this).attr('href') - } - var root = window.location.protocol + '//' + window.location.host + Backbone.history.options.root - - if (href.prop && href.prop === root) href.attr = '' - - if (href.prop && href.prop.slice(0, root.length) === root) { - evt.preventDefault() - - segments = href.attr.split('/') - segments.splice(0, 1) // pop off the element created by the first / - - if (href.attr === '') { - Router.home() - } else { - Router[segments[0]](segments[1], segments[2]) - } - } -} - -Router.init = function() { - Backbone.history.start({ - silent: true, - pushState: true, - root: '/' - }) - $(document).on('click', 'a[data-router="true"]', Router.intercept) -} - -export default Router diff --git a/frontend/src/Metamaps/Topic.js b/frontend/src/Metamaps/Topic.js index 663b0c11..1d3d28de 100644 --- a/frontend/src/Metamaps/Topic.js +++ b/frontend/src/Metamaps/Topic.js @@ -10,7 +10,6 @@ import Filter from './Filter' import GlobalUI from './GlobalUI' import JIT from './JIT' import Map from './Map' -import Router from './Router' import Selected from './Selected' import Settings from './Settings' import SynapseCard from './SynapseCard' @@ -90,7 +89,6 @@ const Topic = { if (callback) callback() } }) - Router.navigate('/topics/' + nodeid) Active.Topic = DataModel.Topics.get(nodeid) } }, diff --git a/frontend/src/Metamaps/Views/ChatView.js b/frontend/src/Metamaps/Views/ChatView.js index 0024a623..f7c41a9d 100644 --- a/frontend/src/Metamaps/Views/ChatView.js +++ b/frontend/src/Metamaps/Views/ChatView.js @@ -10,7 +10,7 @@ import ReactDOM from 'react-dom' import Active from '../Active' import DataModel from '../DataModel' import Realtime from '../Realtime' -import MapChat from '../../components/MapChat' +import ReactApp from '../GlobalUI/ReactApp' const ChatView = { isOpen: false, @@ -51,24 +51,7 @@ const ChatView = { render: () => { if (!Active.Map) return const self = ChatView - self.mapChat = ReactDOM.render(React.createElement(MapChat, { - conversationLive: self.conversationLive, - isParticipating: self.isParticipating, - onOpen: self.onOpen, - onClose: self.onClose, - leaveCall: Realtime.leaveCall, - joinCall: Realtime.joinCall, - inviteACall: Realtime.inviteACall, - inviteToJoin: Realtime.inviteToJoin, - participants: self.participants.models.map(p => p.attributes), - messages: self.messages.models.map(m => m.attributes), - videoToggleClick: self.videoToggleClick, - cursorToggleClick: self.cursorToggleClick, - soundToggleClick: self.soundToggleClick, - inputBlur: self.inputBlur, - inputFocus: self.inputFocus, - handleInputMessage: self.handleInputMessage - }), document.getElementById(ChatView.domId)) + ReactApp.render() }, onOpen: () => { $(document).trigger(ChatView.events.openTray) diff --git a/frontend/src/Metamaps/Views/ExploreMaps.js b/frontend/src/Metamaps/Views/ExploreMaps.js index 5c10691b..5d332390 100644 --- a/frontend/src/Metamaps/Views/ExploreMaps.js +++ b/frontend/src/Metamaps/Views/ExploreMaps.js @@ -6,6 +6,7 @@ import ReactDOM from 'react-dom' // TODO ensure this isn't a double import import Active from '../Active' import DataModel from '../DataModel' import GlobalUI from '../GlobalUI' +import { ReactApp } from '../GlobalUI' import Realtime from '../Realtime' import Loading from '../Loading' import Maps from '../../components/Maps' @@ -13,6 +14,37 @@ import Maps from '../../components/Maps' const ExploreMaps = { pending: false, mapper: null, + updateFromPath: function(path) { + const self = ExploreMaps + const test = path.split('/')[1] + const section = path.split('/')[2] + const id = path.split('/')[3] + + if (test === 'explore') { + const capitalize = section.charAt(0).toUpperCase() + section.slice(1) + self.setCollection(DataModel.Maps[capitalize]) + } else if (test === '') { + self.setCollection(DataModel.Maps.Active) + } + + if (id) { + if (self.collection.mapperId !== id) { + // empty the collection if we are trying to load the maps + // collection of a different mapper than we had previously + self.collection.reset() + self.collection.page = 1 + self.render() + } + self.collection.mapperId = id + } + if (self.collection.length === 0) { + Loading.show() + self.pending = true + self.collection.getMaps() + } else { + id ? self.fetchUserThenRender() : self.render() + } + }, setCollection: function(collection) { var self = ExploreMaps @@ -26,55 +58,9 @@ const ExploreMaps = { self.collection.on('successOnFetch', self.handleSuccess) self.collection.on('errorOnFetch', self.handleError) }, - render: function(cb) { + render: function() { var self = ExploreMaps - - if (!self.collection) return - - var exploreObj = { - currentUser: Active.Mapper, - section: self.collection.id, - maps: self.collection, - juntoState: Realtime.juntoState, - moreToLoad: self.collection.page !== 'loadedAll', - user: self.collection.id === 'mapper' ? self.mapper : null, - loadMore: self.loadMore, - pending: self.pending, - onStar: function(map) { - $.post('/maps/' + map.id + '/star') - map.set('star_count', map.get('star_count') + 1) - if (DataModel.Stars) DataModel.Stars.push({ user_id: Active.Mapper.id, map_id: map.id }) - DataModel.Maps.Starred.add(map) - GlobalUI.notifyUser('Map is now starred') - self.render() - }, - onRequest: function(map) { - $.post({ - url: `/maps/${map.id}/access_request` - }) - GlobalUI.notifyUser('You will be notified by email if request accepted') - }, - onFollow: function(map) { - const isFollowing = map.isFollowedBy(Active.Mapper) - $.post({ - url: `/maps/${map.id}/${isFollowing ? 'un' : ''}follow` - }) - if (isFollowing) { - GlobalUI.notifyUser('You are no longer following this map') - Active.Mapper.unfollowMap(map.id) - } else { - GlobalUI.notifyUser('You are now following this map') - Active.Mapper.followMap(map.id) - } - self.render() - } - } - ReactDOM.render( - React.createElement(Maps, exploreObj), - document.getElementById('explore') - ).resize() - - if (cb) cb() + ReactApp.render() Loading.hide() }, loadMore: function() { @@ -85,14 +71,13 @@ const ExploreMaps = { } self.render() }, - handleSuccess: function(cb) { + handleSuccess: function() { var self = ExploreMaps self.pending = false if (self.collection && self.collection.id === 'mapper') { - self.fetchUserThenRender(cb) + self.fetchUserThenRender() } else { - self.render(cb) - Loading.hide() + self.render() } }, handleError: function() { @@ -103,8 +88,8 @@ const ExploreMaps = { var self = ExploreMaps if (self.mapper && self.mapper.id === self.collection.mapperId) { - self.render(cb) - return Loading.hide() + self.render() + return } // first load the mapper object and then call the render function @@ -112,14 +97,40 @@ const ExploreMaps = { url: '/users/' + self.collection.mapperId + '/details.json', success: function(response) { self.mapper = response - self.render(cb) - Loading.hide() + self.render() }, error: function() { - self.render(cb) - Loading.hide() + self.render() } }) + }, + onStar: function(map) { + $.post('/maps/' + map.id + '/star') + map.set('star_count', map.get('star_count') + 1) + if (DataModel.Stars) DataModel.Stars.push({ user_id: Active.Mapper.id, map_id: map.id }) + DataModel.Maps.Starred.add(map) + GlobalUI.notifyUser('Map is now starred') + ReactApp.render() + }, + onRequest: function(map) { + $.post({ + url: `/maps/${map.id}/access_request` + }) + GlobalUI.notifyUser('You will be notified by email if request accepted') + }, + onMapFollow: function(map) { + const isFollowing = map.isFollowedBy(Active.Mapper) + $.post({ + url: `/maps/${map.id}/${isFollowing ? 'un' : ''}follow` + }) + if (isFollowing) { + GlobalUI.notifyUser('You are no longer following this map') + Active.Mapper.unfollowMap(map.id) + } else { + GlobalUI.notifyUser('You are now following this map') + Active.Mapper.followMap(map.id) + } + ReactApp.render() } } diff --git a/frontend/src/Metamaps/Views/TopicCard.js b/frontend/src/Metamaps/Views/TopicCard.js index 0b02fccd..67417126 100644 --- a/frontend/src/Metamaps/Views/TopicCard.js +++ b/frontend/src/Metamaps/Views/TopicCard.js @@ -5,48 +5,39 @@ import ReactDOM from 'react-dom' import Active from '../Active' import Visualize from '../Visualize' -import GlobalUI from '../GlobalUI' - -import ReactTopicCard from '../../components/TopicCard' +import GlobalUI, { ReactApp } from '../GlobalUI' const TopicCard = { - openTopicCard: null, // stores the topic that's currently open + openTopic: null, // stores the topic that's currently open metacodeSets: [], + updateTopic: () => {}, + onTopicFollow: () => {}, + redrawCanvas: () => { + Visualize.mGraph.plot() + }, init: function(serverData) { const self = TopicCard self.metacodeSets = serverData.metacodeSets }, populateShowCard: function(topic) { const self = TopicCard - ReactDOM.render( - React.createElement(ReactTopicCard, { - topic: topic, - ActiveMapper: Active.Mapper, - updateTopic: obj => { - topic.save(obj, { success: topic => self.populateShowCard(topic) }) - }, - onFollow: () => { - const isFollowing = topic.isFollowedBy(Active.Mapper) - $.post({ - url: `/topics/${topic.id}/${isFollowing ? 'un' : ''}follow` - }) - if (isFollowing) { - GlobalUI.notifyUser('You are no longer following this topic') - Active.Mapper.unfollowTopic(topic.id) - } else { - GlobalUI.notifyUser('You are now following this topic') - Active.Mapper.followTopic(topic.id) - } - self.populateShowCard(topic) - }, - metacodeSets: self.metacodeSets, - redrawCanvas: () => { - Visualize.mGraph.plot() - } - }), - document.getElementById('showcard') - ) - + TopicCard.updateTopic = obj => { + topic.save(obj, { success: topic => self.populateShowCard(topic) }) + } + TopicCard.onTopicFollow = () => { + const isFollowing = topic.isFollowedBy(Active.Mapper) + $.post({ + url: `/topics/${topic.id}/${isFollowing ? 'un' : ''}follow` + }) + if (isFollowing) { + GlobalUI.notifyUser('You are no longer following this topic') + Active.Mapper.unfollowTopic(topic.id) + } else { + GlobalUI.notifyUser('You are now following this topic') + Active.Mapper.followTopic(topic.id) + } + self.populateShowCard(topic) + } // initialize draggability $('.showcard').draggable({ handle: '.metacodeImage', @@ -54,12 +45,12 @@ const TopicCard = { $(this).height('auto') } }) + ReactApp.render() }, - showCard: function(node, opts) { + showCard: function(node, opts = {}) { var self = TopicCard - if (!opts) opts = {} var topic = node.getData('topic') - self.openTopicCard = topic + self.openTopic = topic // populate the card that's about to show with the right topics data self.populateShowCard(topic) return $('.showcard').fadeIn('fast', () => opts.complete && opts.complete()) @@ -67,7 +58,7 @@ const TopicCard = { hideCard: function() { var self = TopicCard $('.showcard').fadeOut('fast') - self.openTopicCard = null + self.openTopic = null } } diff --git a/frontend/src/Metamaps/Visualize.js b/frontend/src/Metamaps/Visualize.js index 43f6071b..aa0745da 100644 --- a/frontend/src/Metamaps/Visualize.js +++ b/frontend/src/Metamaps/Visualize.js @@ -8,7 +8,6 @@ import Active from './Active' import DataModel from './DataModel' import JIT from './JIT' import Loading from './Loading' -import Router from './Router' import TopicCard from './Views/TopicCard' const Visualize = { @@ -198,19 +197,6 @@ const Visualize = { } } hold() - - // update the url now that the map is ready - clearTimeout(Router.timeoutId) - Router.timeoutId = setTimeout(function() { - var m = Active.Map - var t = Active.Topic - - if (m && window.location.pathname !== '/maps/' + m.id) { - Router.navigateAndTrack('/maps/' + m.id) - } else if (t && window.location.pathname !== '/topics/' + t.id) { - Router.navigateAndTrack('/topics/' + t.id) - } - }, 800) }, clearVisualization: function() { Visualize.mGraph.graph.empty() diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js index eb9969b0..8be91f94 100644 --- a/frontend/src/Metamaps/index.js +++ b/frontend/src/Metamaps/index.js @@ -9,8 +9,7 @@ import DataModel from './DataModel' import Debug from './Debug' import Filter from './Filter' import GlobalUI, { - Search, CreateMap, ImportDialog, Account as GlobalUIAccount, - NotificationIcon + ReactApp, Search, CreateMap, ImportDialog, Account as GlobalUIAccount } from './GlobalUI' import Import from './Import' import JIT from './JIT' @@ -23,7 +22,6 @@ import Mouse from './Mouse' import Organize from './Organize' import PasteInput from './PasteInput' import Realtime from './Realtime' -import Router from './Router' import Selected from './Selected' import Settings from './Settings' import Synapse from './Synapse' @@ -45,11 +43,11 @@ Metamaps.DataModel = DataModel Metamaps.Debug = Debug Metamaps.Filter = Filter Metamaps.GlobalUI = GlobalUI +Metamaps.GlobalUI.ReactApp = ReactApp Metamaps.GlobalUI.Search = Search Metamaps.GlobalUI.CreateMap = CreateMap Metamaps.GlobalUI.Account = GlobalUIAccount Metamaps.GlobalUI.ImportDialog = ImportDialog -Metamaps.GlobalUI.NotificationIcon = NotificationIcon Metamaps.Import = Import Metamaps.JIT = JIT Metamaps.Listeners = Listeners @@ -64,7 +62,6 @@ Metamaps.Mouse = Mouse Metamaps.Organize = Organize Metamaps.PasteInput = PasteInput Metamaps.Realtime = Realtime -Metamaps.Router = Router Metamaps.Selected = Selected Metamaps.Settings = Settings Metamaps.Synapse = Synapse @@ -86,26 +83,6 @@ document.addEventListener('DOMContentLoaded', function() { Metamaps[prop].init(Metamaps.ServerData) } } - // load whichever page you are on - if (Metamaps.currentSection === 'explore') { - const capitalize = Metamaps.currentPage.charAt(0).toUpperCase() + Metamaps.currentPage.slice(1) - - Views.ExploreMaps.setCollection(DataModel.Maps[capitalize]) - if (Metamaps.currentPage === 'mapper') { - Views.ExploreMaps.fetchUserThenRender() - } else { - Views.ExploreMaps.render() - } - GlobalUI.showDiv('#explore') - } else if (Metamaps.currentSection === '' && Active.Mapper) { - Views.ExploreMaps.setCollection(DataModel.Maps.Active) - Views.ExploreMaps.render() - GlobalUI.showDiv('#explore') - } else if (Active.Map || Active.Topic) { - Loading.show() - JIT.prepareVizData() - GlobalUI.showDiv('#infovis') - } }) export default Metamaps diff --git a/frontend/src/components/App/AccountMenu.js b/frontend/src/components/App/AccountMenu.js new file mode 100644 index 00000000..d6f2ca9e --- /dev/null +++ b/frontend/src/components/App/AccountMenu.js @@ -0,0 +1,38 @@ +import React, { Component, PropTypes } from 'react' + +class AccountMenu extends Component { + static propTypes = { + currentUser: PropTypes.object + } + + render () { + return
+ 11835c3 +

Connor

+ +
+ } +} + +export default AccountMenu diff --git a/frontend/src/components/App/FilterBox.js b/frontend/src/components/App/FilterBox.js new file mode 100644 index 00000000..3ea1a614 --- /dev/null +++ b/frontend/src/components/App/FilterBox.js @@ -0,0 +1,12 @@ +import React, { Component, PropTypes } from 'react' + +class FilterBox extends Component { + static propTypes = { + } + + render () { + return null + } +} + +export default FilterBox diff --git a/frontend/src/components/App/LoginForm.js b/frontend/src/components/App/LoginForm.js new file mode 100644 index 00000000..273ee31b --- /dev/null +++ b/frontend/src/components/App/LoginForm.js @@ -0,0 +1,36 @@ +import React, { Component, PropTypes } from 'react' + +class LoginForm extends Component { + static propTypes = { + loginFormAuthToken: PropTypes.string + } + + render () { + return
+ + +
+
+ +
+
+ +
+
+ +
+
+ + + +
+
+
+
+ Forgot password? +
+
+ } +} + +export default LoginForm diff --git a/frontend/src/components/NotificationIcon.js b/frontend/src/components/App/NotificationIcon.js similarity index 90% rename from frontend/src/components/NotificationIcon.js rename to frontend/src/components/App/NotificationIcon.js index 201020b5..3f9c8980 100644 --- a/frontend/src/components/NotificationIcon.js +++ b/frontend/src/components/App/NotificationIcon.js @@ -1,6 +1,11 @@ import React, { PropTypes, Component } from 'react' class NotificationIcon extends Component { + + static propTypes = { + unreadNotificationsCount: PropTypes.number + } + constructor(props) { super(props) @@ -31,8 +36,4 @@ class NotificationIcon extends Component { } } -NotificationIcon.propTypes = { - unreadNotificationsCount: PropTypes.number -} - export default NotificationIcon diff --git a/frontend/src/components/App/Toast.js b/frontend/src/components/App/Toast.js new file mode 100644 index 00000000..48ac4cb1 --- /dev/null +++ b/frontend/src/components/App/Toast.js @@ -0,0 +1,14 @@ +import React, { Component, PropTypes } from 'react' + +class Toast extends Component { + static propTypes = { + message: PropTypes.string + } + + render () { + const html = {__html: this.props.html} + return

+ } +} + +export default Toast diff --git a/frontend/src/components/App/UpperLeftUI.js b/frontend/src/components/App/UpperLeftUI.js new file mode 100644 index 00000000..51a79784 --- /dev/null +++ b/frontend/src/components/App/UpperLeftUI.js @@ -0,0 +1,43 @@ +import React, { Component, PropTypes } from 'react' +import { Link } from 'react-router' + +class UpperLeftUI extends Component { + static propTypes = { + currentUser: PropTypes.object + } + + render () { + return

+
+ METAMAPS +
+
+ +
+
+
+
+
+
View Only
+
Request Access
+
Request Pending
+
Request Not Accepted
+
+
+
+ } +} + +export default UpperLeftUI + +/* + +<% request = current_user && @map && @allrequests.find{|a| a.user == current_user} + className = (@map and not policy(@map).update?) ? 'isViewOnly ' : '' + if @map + className += 'sendRequest' if not request + className += 'sentRequest' if request and not request.answered + className += 'requestDenied' if request and request.answered and not request.approved + end %> + + */ diff --git a/frontend/src/components/App/UpperRightUI.js b/frontend/src/components/App/UpperRightUI.js new file mode 100644 index 00000000..6519386b --- /dev/null +++ b/frontend/src/components/App/UpperRightUI.js @@ -0,0 +1,66 @@ +import React, { Component, PropTypes } from 'react' + +import FilterBox from './FilterBox' +import AccountMenu from './AccountMenu' +import LoginForm from './LoginForm' +import NotificationIcon from './NotificationIcon' + +class UpperRightUI extends Component { + static propTypes = { + currentUser: PropTypes.object, + signInPage: PropTypes.bool, + unreadNotificationsCount: PropTypes.number + } + + static contextTypes = { + location: PropTypes.object + } + + render () { + const { currentUser, signInPage, unreadNotificationsCount } = this.props + const { location } = this.context + // TODO: get these map related things out of this file + // TODO: only show 'import' if you have edit rights for the map + const isMapPage = location.pathname.slice(0, 6) === '/maps/' + return
+ {isMapPage &&
+ {currentUser &&
+
+ Import Data +
+
} +
+
Filter
+
+ +
+
+ {currentUser &&
+
Save To New Map
+
} +
+
} + {currentUser && +
+ Create New Map +
+
} + {currentUser && + + } + {!signInPage &&
+
Account
+ {currentUser && } + {!currentUser && 'SIGN IN'} + {!currentUser &&
} +
+
+ {currentUser ? : } +
+
} +
+
+ } +} + +export default UpperRightUI diff --git a/frontend/src/components/App/index.js b/frontend/src/components/App/index.js new file mode 100644 index 00000000..9a8e6204 --- /dev/null +++ b/frontend/src/components/App/index.js @@ -0,0 +1,54 @@ +import React, { Component, PropTypes } from 'react' + +import Toast from './Toast' +import UpperLeftUI from './UpperLeftUI' +import UpperRightUI from './UpperRightUI' + +class App extends Component { + static propTypes = { + children: PropTypes.object, + toast: PropTypes.string, + unreadNotificationsCount: PropTypes.number, + location: PropTypes.object + } + + static childContextTypes = { + currentUser: PropTypes.object, + location: PropTypes.object + } + + getChildContext () { + const { route, location } = this.props + return {currentUser: route.currentUser, location} + } + + render () { + const { toast, currentUser, unreadNotificationsCount } = this.props + return
+ + + + {currentUser && } + {this.props.children} +
+ } +} + +export default App + +/* +<% classes = action_name == "home" ? "homePage" : "" + classes += action_name == "home" && authenticated? ? " explorePage" : "" + classes += controller_name == "maps" && action_name == "index" ? " explorePage" : "" + if controller_name == "maps" && action_name == "show" + classes += " mapPage" + if policy(@map).update? + classes += " canEditMap" + end + if @map.permission == "commons" + classes += " commonsMap" + end + end + classes += controller_name == "topics" && action_name == "show" ? " topicPage" : "" + %> +*/ diff --git a/frontend/src/components/ImportDialogBox.js b/frontend/src/components/MapView/ImportDialogBox.js similarity index 100% rename from frontend/src/components/ImportDialogBox.js rename to frontend/src/components/MapView/ImportDialogBox.js diff --git a/frontend/src/components/MapChat/Message.js b/frontend/src/components/MapView/MapChat/Message.js similarity index 96% rename from frontend/src/components/MapChat/Message.js rename to frontend/src/components/MapView/MapChat/Message.js index 7cfe9ff2..e4caf3ce 100644 --- a/frontend/src/components/MapChat/Message.js +++ b/frontend/src/components/MapView/MapChat/Message.js @@ -1,6 +1,6 @@ import React from 'react' import Autolinker from 'autolinker' -import Util from '../../Metamaps/Util' +import Util from '../../../Metamaps/Util' const linker = new Autolinker({ newWindow: true, truncate: 50, email: false, phone: false }) diff --git a/frontend/src/components/MapChat/NewMessage.js b/frontend/src/components/MapView/MapChat/NewMessage.js similarity index 100% rename from frontend/src/components/MapChat/NewMessage.js rename to frontend/src/components/MapView/MapChat/NewMessage.js diff --git a/frontend/src/components/MapChat/Participant.js b/frontend/src/components/MapView/MapChat/Participant.js similarity index 100% rename from frontend/src/components/MapChat/Participant.js rename to frontend/src/components/MapView/MapChat/Participant.js diff --git a/frontend/src/components/MapChat/Unread.js b/frontend/src/components/MapView/MapChat/Unread.js similarity index 100% rename from frontend/src/components/MapChat/Unread.js rename to frontend/src/components/MapView/MapChat/Unread.js diff --git a/frontend/src/components/MapChat/index.js b/frontend/src/components/MapView/MapChat/index.js similarity index 99% rename from frontend/src/components/MapChat/index.js rename to frontend/src/components/MapView/MapChat/index.js index 27bff527..dc8875e5 100644 --- a/frontend/src/components/MapChat/index.js +++ b/frontend/src/components/MapView/MapChat/index.js @@ -3,7 +3,7 @@ import Unread from './Unread' import Participant from './Participant' import Message from './Message' import NewMessage from './NewMessage' -import Util from '../../Metamaps/Util' +import Util from '../../../Metamaps/Util' function makeList(messages) { let currentHeader diff --git a/frontend/src/components/MapView/MapInfoBox.js b/frontend/src/components/MapView/MapInfoBox.js new file mode 100644 index 00000000..a62934a1 --- /dev/null +++ b/frontend/src/components/MapView/MapInfoBox.js @@ -0,0 +1,12 @@ +import React, { Component, PropTypes } from 'react' + +class MapInfoBox extends Component { + static propTypes = { + } + + render () { + return null + } +} + +export default MapInfoBox diff --git a/frontend/src/components/MapView/index.js b/frontend/src/components/MapView/index.js new file mode 100644 index 00000000..552a77a0 --- /dev/null +++ b/frontend/src/components/MapView/index.js @@ -0,0 +1,114 @@ +import React, { Component, PropTypes } from 'react' + +import MapInfoBox from './MapInfoBox' +import MapChat from './MapChat' +import TopicCard from '../TopicCard' + +class MapView extends Component { + + static propTypes = { + mapId: PropTypes.string, + map: PropTypes.object, + mapIsStarred: PropTypes.bool, + currentUser: PropTypes.object, + endActiveMap: PropTypes.func, + launchNewMap: PropTypes.func + } + + constructor(props) { + super(props) + this.state = {} + } + + componentDidMount() { + window && window.addEventListener('resize', this.resize) + this.resize() + } + + componentDidUpdate(prevProps) { + const oldMapId = prevProps.mapId + const { mapId, endActiveMap, launchNewMap } = this.props + if (!oldMapId && mapId) launchNewMap(mapId) + else if (oldMapId && mapId && oldMapId !== mapId) { + endActiveMap() + launchNewMap(mapId) + } + else if (oldMapId && !mapId) endActiveMap() + } + + componentWillUnmount() { + window && window.removeEventListener('resize', this.resize) + } + + resize = () => { + + } + + render = () => { + const { mapIsStarred } = this.props + const starclassName = mapIsStarred ? 'starred' : '' + const tooltip = mapIsStarred ? 'Star' : 'Unstar' + + return
+
+
+ +
+
+ +
+
+
Center View
+
Zoom In
+
Zoom Out
+
+
+ +
+
{tooltip}
+
+
+
Map Info
+
+
+
Help
+
+
+
+
+ } +} + +export default MapView + +/* + +
+<% if authenticated? %> + <% # for creating and pulling in topics and synapses %> + <% if controller_name == 'maps' && action_name == "conversation" %> + <%= render :partial => 'maps/newtopicsecret' %> + <% else %> + <%= render :partial => 'maps/newtopic' %> + <% end %> + <%= render :partial => 'maps/newsynapse' %> + <% # for populating the change metacode list on the topic card %> + <%= render :partial => 'shared/metacodeoptions' %> +<% end %> +<%= render :partial => 'layouts/lowermapelements' %> + +
+ +
+
+ Double-click to
add a topic +
+
+ Use Tab & Shift+Tab to select a metacode +
+
+ Press Enter to add the topic +
+
+ +*/ diff --git a/frontend/src/components/Maps/Header.js b/frontend/src/components/Maps/Header.js index 9de1eb9b..65a23aa4 100644 --- a/frontend/src/components/Maps/Header.js +++ b/frontend/src/components/Maps/Header.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react' +import { Link } from 'react-router' import _ from 'lodash' const MapLink = props => { @@ -9,10 +10,10 @@ const MapLink = props => { } return ( - +
{text} -
+ ) } @@ -39,31 +40,26 @@ class Header extends Component { diff --git a/frontend/src/components/Maps/MapCard.js b/frontend/src/components/Maps/MapCard.js index 4dd9ea18..fa57af71 100644 --- a/frontend/src/components/Maps/MapCard.js +++ b/frontend/src/components/Maps/MapCard.js @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react' +import { Link } from 'react-router' import { find, values } from 'lodash' import Util from '../../Metamaps/Util' @@ -78,7 +79,7 @@ const Metadata = (props) => { } const checkAndWrapInA = (shouldWrap, classString, mapId, element) => { - if (shouldWrap) return { element } + if (shouldWrap) return { element } else return element } diff --git a/frontend/src/components/Maps/index.js b/frontend/src/components/Maps/index.js index 18d0fe92..e45b94b0 100644 --- a/frontend/src/components/Maps/index.js +++ b/frontend/src/components/Maps/index.js @@ -12,6 +12,25 @@ const MAX_COLUMNS = 4 class Maps extends Component { + static propTypes = { + section: PropTypes.string, + maps: PropTypes.object, + juntoState: PropTypes.object, + moreToLoad: PropTypes.bool, + user: PropTypes.object, + currentUser: PropTypes.object, + loadMore: PropTypes.func, + pending: PropTypes.bool, + setCollection: PropTypes.func, + onStar: PropTypes.func, + onRequest: PropTypes.func, + onMapFollow: PropTypes.func + } + + static contextTypes = { + location: PropTypes.object + } + constructor(props) { super(props) this.state = { mapsWidth: 0 } @@ -19,7 +38,7 @@ class Maps extends Component { componentDidMount() { window && window.addEventListener('resize', this.resize) - this.refs.maps.addEventListener('scroll', throttle(this.scroll, 500, { leading: true, trailing: false })) + this.refs.maps && this.refs.maps.addEventListener('scroll', throttle(this.scroll, 500, { leading: true, trailing: false })) this.resize() } @@ -29,6 +48,7 @@ class Maps extends Component { resize = () => { const { maps, user, currentUser } = this.props + if (!maps) return const numCards = maps.length + (user || currentUser ? 1 : 0) const mapSpaces = Math.floor(document.body.clientWidth / MAP_WIDTH) const mapsWidth = document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT @@ -46,41 +66,30 @@ class Maps extends Component { } render = () => { - const { maps, currentUser, juntoState, pending, section, user, onStar, onRequest, onFollow } = this.props + const { maps, currentUser, juntoState, pending, section, user, onStar, onRequest, onMapFollow } = this.props const style = { width: this.state.mapsWidth + 'px' } const mobile = document && document.body.clientWidth <= MOBILE_VIEW_BREAKPOINT + if (!maps) return null // do loading here instead + return (
{ user ? : null } { currentUser && !user && !(pending && maps.length === 0) ? : null } - { maps.models.map(map => ) } + { maps.models.map(map => ) }
) } } -Maps.propTypes = { - section: PropTypes.string.isRequired, - maps: PropTypes.object.isRequired, - juntoState: PropTypes.object.isRequired, - moreToLoad: PropTypes.bool.isRequired, - user: PropTypes.object, - currentUser: PropTypes.object, - loadMore: PropTypes.func, - pending: PropTypes.bool.isRequired, - onStar: PropTypes.func.isRequired, - onRequest: PropTypes.func.isRequired, - onFollow: PropTypes.func.isRequired -} - export default Maps diff --git a/frontend/src/components/TopicCard/Follow.js b/frontend/src/components/TopicCard/Follow.js index 786001d7..653d8b18 100644 --- a/frontend/src/components/TopicCard/Follow.js +++ b/frontend/src/components/TopicCard/Follow.js @@ -2,8 +2,8 @@ import React, { PropTypes, Component } from 'react' class Follow extends Component { render = () => { - const { isFollowing, onFollow } = this.props - return
+ const { isFollowing, onTopicFollow } = this.props + return
{isFollowing ? 'Unfollow' : 'Follow'}
} @@ -11,7 +11,7 @@ class Follow extends Component { Follow.propTypes = { isFollowing: PropTypes.bool, - onFollow: PropTypes.func + onTopicFollow: PropTypes.func } export default Follow diff --git a/frontend/src/components/TopicCard/index.js b/frontend/src/components/TopicCard/index.js index 2c30d45e..089d942a 100644 --- a/frontend/src/components/TopicCard/index.js +++ b/frontend/src/components/TopicCard/index.js @@ -7,12 +7,15 @@ import Attachments from './Attachments' import Follow from './Follow' import Util from '../../Metamaps/Util' - class ReactTopicCard extends Component { render = () => { - const { topic, ActiveMapper, onFollow } = this.props - const authorizedToEdit = topic.authorizeToEdit(ActiveMapper) - const isFollowing = topic.isFollowedBy(ActiveMapper) + const { currentUser, onTopicFollow } = this.props + const topic = this.props.openTopic + + if (!topic) return null + + const authorizedToEdit = topic.authorizeToEdit(currentUser) + const isFollowing = topic.isFollowedBy(currentUser) const hasAttachment = topic.get('link') && topic.get('link') !== '' let classname = 'permission' @@ -21,7 +24,7 @@ class ReactTopicCard extends Component { } else { classname += ' cannotEdit' } - if (topic.authorizePermissionChange(ActiveMapper)) classname += ' yourTopic' + if (topic.authorizePermissionChange(currentUser)) classname += ' yourTopic' return (
@@ -31,7 +34,7 @@ class ReactTopicCard extends Component { onChange={this.props.updateTopic} /> - {Util.isTester(ActiveMapper) && } + {Util.isTester(currentUser) && }
@@ -53,10 +56,10 @@ class ReactTopicCard extends Component { } ReactTopicCard.propTypes = { - topic: PropTypes.object, - ActiveMapper: PropTypes.object, + openTopic: PropTypes.object, + currentUser: PropTypes.object, updateTopic: PropTypes.func, - onFollow: PropTypes.func, + onTopicFollow: PropTypes.func, metacodeSets: PropTypes.arrayOf(PropTypes.shape({ name: PropTypes.string, metacodes: PropTypes.arrayOf(PropTypes.shape({ diff --git a/frontend/src/components/TopicView/index.js b/frontend/src/components/TopicView/index.js new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/components/makeRoutes.js b/frontend/src/components/makeRoutes.js new file mode 100644 index 00000000..8564376a --- /dev/null +++ b/frontend/src/components/makeRoutes.js @@ -0,0 +1,20 @@ +import React from 'react' +import { Route, IndexRoute } from 'react-router' +import App from './App' +import Maps from './Maps' +import MapView from './MapView' + +export default function makeRoutes () { + return + + + + + + + + + + + +} diff --git a/package.json b/package.json index 84eff416..c4f3a9fa 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-dom": "15.4.2", "react-dropzone": "3.9.1", "react-onclickoutside": "5.9.0", + "react-router": "^3.0.2", "redux": "3.6.0", "riek": "1.0.7", "simplewebrtc": "2.2.2",