initial restructuring

This commit is contained in:
Connor Turland 2017-03-11 01:49:27 -05:00
parent 1124d76475
commit 701198ef14
46 changed files with 760 additions and 796 deletions

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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 {

View file

@ -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

View file

@ -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" %>
<h3 class="accountHeader"><%= account.name.split[0...1][0] %></h3>
<ul>
<li class="accountListItem accountSettings">
<div class="accountIcon"></div>
<%= link_to "Settings", edit_user_url(account) %>
</li>
<% if account.admin %>
<li class="accountListItem accountAdmin">
<div class="accountIcon"></div>
<%= link_to "Admin", metacodes_path %>
</li>
<% end %>
<li class="accountListItem accountApps">
<div class="accountIcon"></div>
<%= link_to "Apps", oauth_authorized_applications_path %>
</li>
<li class="accountListItem accountInvite openLightbox" data-open="invite">
<div class="accountIcon"></div>
<span>Share Invite</span>
</li>
<li class="accountListItem accountLogout">
<div class="accountIcon"></div>
<%= link_to "Sign Out", "/logout", id: "Logout" %>
</li>
</ul>
<% else %>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { class: "loginAnywhere" }) do |f| %>
<div class="accountImage"></div>
<div class="accountInput accountEmail">
<%= f.email_field :email, :placeholder => "Email" %>
</div>
<div class="accountInput accountPassword">
<%= f.password_field :password, :placeholder => "Password" %>
</div>
<div class="accountSubmit"><%= f.submit "SIGN IN" %></div>
<% if devise_mapping.rememberable? -%>
<div class="accountRememberMe">
<%= f.label :remember_me, "Stay signed in" %>
<%= f.check_box :remember_me %>
<div class="clearfloat"></div>
</div>
<% end -%>
<div class="clearfloat"></div>
<div class="accountForgotPass">
<%- if devise_mapping.recoverable? && controller_name != 'passwords' %>
<%= link_to "Forgot password?", new_password_path(resource_name) %>
<% end -%>
</div>
<% end %>
<% end %>
<% # Rails.logger.info(stored_location_for(:user)) %>

View file

@ -1,17 +0,0 @@
<div class="mapControls mapElement">
<div class="zoomExtents mapControl"><div class="tooltips">Center View</div></div>
<div class="zoomIn mapControl"><div class="tooltips">Zoom In</div></div>
<div class="zoomOut mapControl"><div class="tooltips">Zoom Out</div></div>
</div>
<div class="infoAndHelp">
<%= render :partial => 'maps/mapinfobox' %>
<% starred = current_user && @map && current_user.starred_map?(@map)
starClass = starred ? 'starred' : ''
tooltip = starred ? 'Star' : 'Unstar' %>
<div class="starMap infoElement mapElement <%= starClass %>"><div class="tooltipsAbove"><%= tooltip %></div></div>
<div class="mapInfoIcon infoElement mapElement"><div class="tooltipsAbove">Map Info</div></div>
<div class="openCheatsheet openLightbox infoElement mapElement" data-open="cheatsheet"><div class="tooltipsAbove">Help</div></div>
<div class="clearfloat"></div>
</div>

View file

@ -1,107 +0,0 @@
<!-- from left to right on the screen -->
<div class="upperLeftUI">
<!-- home button -->
<div class="homeButton">
<a href="<%= root_url %>" <% if current_user && !noHardHomeLink %><%= 'data-router=true' %><% end %>>METAMAPS</a>
</div> <!-- end homeButton -->
<!-- search box -->
<div class="sidebarSearch">
<input type="text" class="sidebarSearchField" placeholder="Search for topics, maps, and mappers..." />
<div id="searchLoading"></div>
<div class="sidebarSearchIcon"></div>
<div class="clearfloat"></div>
</div> <!-- end sidebarSearch -->
<% 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 %>
<div class="viewOnly <%= className %>">
<div class="eyeball">View Only</div>
<% if current_user %>
<div class="requestAccess requestNotice">Request Access</div>
<div class="requestPending requestNotice">Request Pending</div>
<div class="requestNotAccepted requestNotice">Request Not Accepted</div>
<% end %>
</div>
<div class="clearfloat"></div>
</div><!-- end upperLeftUI -->
<div class="upperRightUI">
<div class="mapElement upperRightEl upperRightMapButtons">
<% if current_user %>
<div class="importDialog upperRightEl upperRightIcon mapElement openLightbox" data-open="import-dialog-lightbox">
<div class="tooltipsUnder">
Import Data
</div>
</div>
<% end %>
<!-- filtering -->
<div class="sidebarFilter upperRightEl">
<div class="sidebarFilterIcon upperRightIcon"><div class="tooltipsUnder">Filter</div></div>
<div class="sidebarFilterBox upperRightBox">
<%= render :partial => 'shared/filterBox' %>
</div>
</div> <!-- end sidebarFilter -->
<% if current_user %>
<!-- fork map -->
<div class="sidebarFork upperRightEl">
<div class="sidebarForkIcon upperRightIcon"><div class="tooltipsUnder">Save To New Map</div></div>
</div> <!-- end sidebarFork -->
<% end %>
<div class="clearfloat"></div>
</div> <!-- end mapElement -->
<% if current_user %>
<!-- create new map -->
<a href="<%= new_map_path %>" target="_blank" class="addMap upperRightEl upperRightIcon">
<div class="tooltipsUnder">
Create New Map
</div>
</a><!-- end addMap -->
<% end %>
<script type="text/javascript">
Metamaps.ServerData.unreadNotificationsCount = <%= user_unread_notification_count %>
</script>
<% if current_user.present? %>
<span id="notification_icon">
<%= link_to notifications_path, class: "notificationsIcon upperRightEl upperRightIcon #{user_unread_notification_count > 0 ? 'unread' : 'read'}" do %>
<div class="tooltipsUnder">
Notifications
</div>
<% if user_unread_notification_count > 0 %>
<div class="unread-notifications-dot"></div>
<% end %>
<% end %>
</span>
<% end %>
<!-- Account / Sign in -->
<% if !(controller_name == "sessions" && action_name == "new") %>
<div class="sidebarAccount upperRightEl">
<div class="sidebarAccountIcon"><div class="tooltipsUnder">Account</div>
<% if current_user && current_user.image %>
<%= image_tag current_user.image.url(:thirtytwo), :size => "32x32" %>
<% elsif !current_user %>
SIGN IN
<div class="accountInnerArrow"></div>
<% end %>
</div>
<div class="sidebarAccountBox upperRightBox">
<%= render :partial => 'layouts/account' %>
</div>
</div><!-- end sidebarAccount -->
<% end %>
<div class="clearfloat"></div>
</div><!-- end upperRightUI -->

View file

@ -6,83 +6,34 @@
#%>
<%= render :partial => 'layouts/head' %>
<body class="<%= authenticated? ? "authenticated" : "unauthenticated" %> controller-<%= controller_name %> action-<%= action_name %>">
<div id="chat-box-wrapper"></div>
<a class='feedback-icon' target='_blank' href='https://hylo.com/c/metamaps'></a>
<%= 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" : ""
%>
<div class="wrapper <%= classes %>" id="wrapper">
<%= render :partial => 'layouts/upperelements', :locals => { :noHardHomeLink => controller_name == "notifications" ? true : false } %>
<%= yield %>
<div class="showcard mapElement mapElementHidden" id="showcard"></div> <!-- the topic card -->
<% 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' %>
<div id="explore"></div>
<% if !(controller_name == 'maps' && action_name == "conversation") %>
<div id="instructions">
<div class="addTopic">
Double-click to<br>add a topic
</div>
<div class="tabKey">
Use Tab & Shift+Tab to select a metacode
</div>
<div class="enterKey">
Press Enter to add the topic
</div>
</div>
<% end %>
<div id="infovis"></div>
<%= render :partial => 'layouts/mobilemenu' %>
<p id="toast" class="toast">
<% if devise_error_messages? %>
<%= devise_error_messages! %>
<% end %>
<% if notice %>
<%= notice %>
<% end %>
<% if alert %>
<%= alert %>
<% end %>
</p>
<div id="loading"></div>
</div>
<%= 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 %>
<div id="loading"></div>
<script type="text/javascript">
Metamaps.ServerData.unreadNotificationsCount = <%= user_unread_notification_count %>
Metamaps.ServerData.mapIsStarred = <%= current_user && @map && current_user.starred_map?(@map) ? true : false %>
</script>
<%= render :partial => 'layouts/foot' %>

View file

@ -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'

View file

@ -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

View file

@ -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) => <Component {...props} {...self.getProps()}/>
const app = <Router createElement={createElement} routes={routes} history={browserHistory} onUpdate={self.handleUpdate} />
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

View file

@ -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
}
}
},

View file

@ -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

View file

@ -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?")

View file

@ -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() {

View file

@ -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

View file

@ -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)
}
},

View file

@ -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)

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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()

View file

@ -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

View file

@ -0,0 +1,38 @@
import React, { Component, PropTypes } from 'react'
class AccountMenu extends Component {
static propTypes = {
currentUser: PropTypes.object
}
render () {
return <div>
<img className="sidebarAccountImage" src="https://metamaps-live.s3.amazonaws.com/users/images/555/629/996/sixtyfour/11835c3.png?1417298429" alt="11835c3" width="48" height="48" />
<h3 className="accountHeader">Connor</h3>
<ul>
<li className="accountListItem accountSettings">
<div className="accountIcon"></div>
<a href="https://metamaps.cc/users/555629996/edit">Settings</a>
</li>
<li className="accountListItem accountAdmin">
<div className="accountIcon"></div>
<a href="/metacodes">Admin</a>
</li>
<li className="accountListItem accountApps">
<div className="accountIcon"></div>
<a href="/oauth/authorized_applications">Apps</a>
</li>
<li className="accountListItem accountInvite openLightbox" data-open="invite">
<div className="accountIcon"></div>
<span>Share Invite</span>
</li>
<li className="accountListItem accountLogout">
<div className="accountIcon"></div>
<a id="Logout" href="/logout">Sign Out</a>
</li>
</ul>
</div>
}
}
export default AccountMenu

View file

@ -0,0 +1,12 @@
import React, { Component, PropTypes } from 'react'
class FilterBox extends Component {
static propTypes = {
}
render () {
return null
}
}
export default FilterBox

View file

@ -0,0 +1,36 @@
import React, { Component, PropTypes } from 'react'
class LoginForm extends Component {
static propTypes = {
loginFormAuthToken: PropTypes.string
}
render () {
return <form className="loginAnywhere" id="new_user" action="/login" acceptCharset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="authenticity_token" value="9z5D3vUGKM5ExKJ0CmhweE8qysvUqjFMwgMvbYXIlrnvg9sqJWIWgCt9lq28NZgyCaNudF+w+dRPD1pybeT4mg==" />
<div className="accountImage"></div>
<div className="accountInput accountEmail">
<input placeholder="Email" type="email" name="user[email]" id="user_email" />
</div>
<div className="accountInput accountPassword">
<input placeholder="Password" type="password" name="user[password]" id="user_password" />
</div>
<div className="accountSubmit">
<input type="submit" name="commit" value="SIGN IN" />
</div>
<div className="accountRememberMe">
<label htmlFor="user_remember_me">Stay signed in</label>
<input name="user[remember_me]" type="hidden" value="0" />
<input type="checkbox" value="1" name="user[remember_me]" id="user_remember_me" />
<div className="clearfloat"></div>
</div>
<div className="clearfloat"></div>
<div className="accountForgotPass">
<a href="/users/password/new">Forgot password?</a>
</div>
</form>
}
}
export default LoginForm

View file

@ -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

View file

@ -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 <p id="toast" className="toast" dangerouslySetInnerHTML={html} />
}
}
export default Toast

View file

@ -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 <div className="upperLeftUI">
<div className="homeButton">
<Link to="/">METAMAPS</Link>
</div>
<div className="sidebarSearch">
<input type="text" className="sidebarSearchField" placeholder="Search for topics, maps, and mappers..." />
<div id="searchLoading"></div>
<div className="sidebarSearchIcon"></div>
<div className="clearfloat"></div>
</div>
<div className="viewOnly">
<div className="eyeball">View Only</div>
<div className="requestAccess requestNotice">Request Access</div>
<div className="requestPending requestNotice">Request Pending</div>
<div className="requestNotAccepted requestNotice">Request Not Accepted</div>
</div>
<div className="clearfloat"></div>
</div>
}
}
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 %>
*/

View file

@ -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 <div className="upperRightUI">
{isMapPage && <div className="mapElement upperRightEl upperRightMapButtons">
{currentUser && <div className="importDialog upperRightEl upperRightIcon mapElement openLightbox" data-open="import-dialog-lightbox">
<div className="tooltipsUnder">
Import Data
</div>
</div>}
<div className="sidebarFilter upperRightEl">
<div className="sidebarFilterIcon upperRightIcon"><div className="tooltipsUnder">Filter</div></div>
<div className="sidebarFilterBox upperRightBox">
<FilterBox />
</div>
</div>
{currentUser && <div className="sidebarFork upperRightEl">
<div className="sidebarForkIcon upperRightIcon"><div className="tooltipsUnder">Save To New Map</div></div>
</div>}
<div className="clearfloat"></div>
</div>}
{currentUser && <a href="/maps/new" target="_blank" className="addMap upperRightEl upperRightIcon">
<div className="tooltipsUnder">
Create New Map
</div>
</a>}
{currentUser && <span id="notification_icon">
<NotificationIcon unreadNotificationsCount={unreadNotificationsCount} />
</span>}
{!signInPage && <div className="sidebarAccount upperRightEl">
<div className="sidebarAccountIcon"><div className="tooltipsUnder">Account</div>
{currentUser && <img src={currentUser.get('image')} />}
{!currentUser && 'SIGN IN'}
{!currentUser && <div className="accountInnerArrow"></div>}
</div>
<div className="sidebarAccountBox upperRightBox">
{currentUser ? <AccountMenu /> : <LoginForm />}
</div>
</div>}
<div className="clearfloat"></div>
</div>
}
}
export default UpperRightUI

View file

@ -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 <div className="wrapper" id="wrapper">
<UpperLeftUI currentUser={currentUser} />
<UpperRightUI currentUser={currentUser} unreadNotificationsCount={unreadNotificationsCount} />
<Toast message={toast} />
{currentUser && <a className='feedback-icon' target='_blank' href='https://hylo.com/c/metamaps'></a>}
{this.props.children}
</div>
}
}
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" : ""
%>
*/

View file

@ -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 })

View file

@ -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

View file

@ -0,0 +1,12 @@
import React, { Component, PropTypes } from 'react'
class MapInfoBox extends Component {
static propTypes = {
}
render () {
return null
}
}
export default MapInfoBox

View file

@ -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 <div className="mapWrapper">
<div id="infovis" />
<div className="showcard mapElement mapElementHidden" id="showcard">
<TopicCard {...this.props} />
</div>
<div id="chat-box-wrapper">
<MapChat {...this.props} />
</div>
<div className="mapControls mapElement">
<div className="zoomExtents mapControl"><div className="tooltips">Center View</div></div>
<div className="zoomIn mapControl"><div className="tooltips">Zoom In</div></div>
<div className="zoomOut mapControl"><div className="tooltips">Zoom Out</div></div>
</div>
<div className="infoAndHelp">
<MapInfoBox />
<div className={`starMap infoElement mapElement ${starclassName}`}>
<div className="tooltipsAbove">{tooltip}</div>
</div>
<div className="mapInfoIcon infoElement mapElement">
<div className="tooltipsAbove">Map Info</div>
</div>
<div className="openCheatsheet openLightbox infoElement mapElement" data-open="cheatsheet">
<div className="tooltipsAbove">Help</div>
</div>
<div className="clearfloat"></div>
</div>
</div>
}
}
export default MapView
/*
<div className="showcard mapElement mapElementHidden" id="showcard"></div>
<% 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' %>
<div id="loading"></div>
<div id="instructions">
<div className="addTopic">
Double-click to<br>add a topic
</div>
<div className="tabKey">
Use Tab & Shift+Tab to select a metacode
</div>
<div className="enterKey">
Press Enter to add the topic
</div>
</div>
*/

View file

@ -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 (
<a { ...otherProps } href={href} className={linkClass}>
<Link { ...otherProps } to={href} className={linkClass}>
<div className="exploreMapsIcon"></div>
{text}
</a>
</Link>
)
}
@ -39,31 +40,26 @@ class Header extends Component {
<MapLink show={explore}
href={signedIn ? '/' : '/explore/active'}
linkClass={activeClass('active')}
data-router="true"
text="All Maps"
/>
<MapLink show={signedIn && explore}
href="/explore/mine"
linkClass={activeClass('my')}
data-router="true"
text="My Maps"
/>
<MapLink show={signedIn && explore}
href="/explore/shared"
linkClass={activeClass('shared')}
data-router="true"
text="Shared With Me"
/>
<MapLink show={signedIn && explore}
href="/explore/starred"
linkClass={activeClass('starred')}
data-router="true"
text="Starred By Me"
/>
<MapLink show={!signedIn && explore}
href="/explore/featured"
linkClass={activeClass('featured')}
data-router="true"
text="Featured Maps"
/>

View file

@ -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 <a className={ classString } href={ `/maps/${mapId}` } data-router="true">{ element }</a>
if (shouldWrap) return <Link className={ classString } to={ `/maps/${mapId}` } >{ element }</Link>
else return element
}

View file

@ -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 (
<div>
<div id='exploreMaps' ref='maps'>
<div style={ style }>
{ user ? <MapperCard user={ user } /> : null }
{ currentUser && !user && !(pending && maps.length === 0) ? <div className="map newMap"><a href="/maps/new"><div className="newMapImage"></div><span>Create new map...</span></a></div> : null }
{ maps.models.map(map => <MapCard key={ map.id } map={ map } mobile={ mobile } juntoState={ juntoState } currentUser={ currentUser } onStar={ onStar } onRequest={ onRequest } onFollow={ onFollow } />) }
{ maps.models.map(map => <MapCard key={ map.id } map={ map } mobile={ mobile } juntoState={ juntoState } currentUser={ currentUser } onStar={ onStar } onRequest={ onRequest } onMapFollow={ onMapFollow } />) }
<div className='clearfloat'></div>
</div>
</div>
<Header signedIn={ !!currentUser }
section={ section }
user={ user }
setCollection={ setCollection }
/>
</div>
)
}
}
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

View file

@ -2,8 +2,8 @@ import React, { PropTypes, Component } from 'react'
class Follow extends Component {
render = () => {
const { isFollowing, onFollow } = this.props
return <div className='topicFollow' onClick={onFollow}>
const { isFollowing, onTopicFollow } = this.props
return <div className='topicFollow' onClick={onTopicFollow}>
{isFollowing ? 'Unfollow' : 'Follow'}
</div>
}
@ -11,7 +11,7 @@ class Follow extends Component {
Follow.propTypes = {
isFollowing: PropTypes.bool,
onFollow: PropTypes.func
onTopicFollow: PropTypes.func
}
export default Follow

View file

@ -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 (
<div className={classname}>
@ -31,7 +34,7 @@ class ReactTopicCard extends Component {
onChange={this.props.updateTopic}
/>
<Links topic={topic}
ActiveMapper={this.props.ActiveMapper}
ActiveMapper={this.props.currentUser}
updateTopic={this.props.updateTopic}
metacodeSets={this.props.metacodeSets}
redrawCanvas={this.props.redrawCanvas}
@ -44,7 +47,7 @@ class ReactTopicCard extends Component {
authorizedToEdit={authorizedToEdit}
updateTopic={this.props.updateTopic}
/>
{Util.isTester(ActiveMapper) && <Follow isFollowing={isFollowing} onFollow={onFollow} />}
{Util.isTester(currentUser) && <Follow isFollowing={isFollowing} onTopicFollow={onTopicFollow} />}
<div className="clearfloat"></div>
</div>
</div>
@ -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({

View file

@ -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 <Route path="/" component={App} >
<IndexRoute component={Maps} />
<Route path="explore">
<Route path="active" component={Maps} />
<Route path="featured" component={Maps} />
<Route path="mine" component={Maps} />
<Route path="shared" component={Maps} />
<Route path="starred" component={Maps} />
<Route path="mapper/:id" component={Maps} />
</Route>
<Route path="maps/:id" component={MapView} />
</Route>
}

View file

@ -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",