Merge branch 'frontendonly' of https://github.com/metamaps/metamaps into frontendonly

This commit is contained in:
Connor Turland 2018-03-10 07:29:14 -05:00
commit 48fb750560
64 changed files with 1748 additions and 1314 deletions

View file

@ -1,2 +1 @@
https://github.com/heroku/heroku-buildpack-nodejs.git
https://github.com/heroku/heroku-buildpack-ruby.git

View file

@ -1,16 +1,10 @@
---
engines:
brakeman:
enabled: true
bundler-audit:
enabled: true
duplication:
enabled: true
config:
languages:
count_threshold: 3 # rule of three
ruby:
mass_threshold: 36 # default: 18
javascript:
mass_threshold: 80 # default: 40
eslint:
@ -18,21 +12,11 @@ engines:
channel: "eslint-3"
fixme:
enabled: true
rubocop:
enabled: true
exclude_fingerprints:
- 74f18007b920e8d81148d2f6a2756534
ratings:
paths:
- 'Gemfile.lock'
- '**.erb'
- '**.rb'
- '**.js'
- '**.jsx'
exclude_paths:
- app/assets/images/
- app/assets/javascripts/lib/
- frontend/src/patched/
- db/
- script/
- spec/
- public/images
- public/lib
- src/patched/

2
.nvmrc
View file

@ -1 +1 @@
6.2.2
8.9.4

4
.travis.yml Normal file
View file

@ -0,0 +1,4 @@
language: node_js
addons:
code_climate:
repo_token: 479d3bf56798fbc7fff3fc8151a5ed09e8ac368fd5af332c437b9e07dbebb44e

View file

@ -1,25 +1,28 @@
Make sure you have `nodemon` and `node-sass` installed
`$ npm install -g nodemon node-sass`
Make sure you're running a good up to date LTS version of `node`, like 8.9.4
Make sure you have `node-sass` installed
`$ npm install -g node-sass`
Run the following at the same time, in two terminals
Run the following at the same time, in TWO SEPARATE terminals. We tell the server where the backend process is running with the API environment variable
JS files, and CSS will rebuild automatically, just refresh the page
If coding the server itself, you will have to use nodemon, or kill and restart the server process manually
```
$ API=http://localhost:3001 nodemon server.js
$ API=http://localhost:3001 node server.js
$ node-sass -w sass/application.scss public/css/application.css
```
To run the server as a daemon that will be re-run if it crashes, you can
use the forever node package.
To make sure the css files get built, use the following in another terminal
```
$ npm install -g forever
$ forever start server.js
touch sass/application.scss
```
Run the metamaps api in another terminal using
Run the metamaps api in another terminal using (on port 3001, so the UI can talk to it)
For now, make sure you are running on the `add-user-route` branch of Metamaps, and that it's up to date with the latest on that branch
`$ rails s -p 3001`
open up http://localhost:3000 and start coding!
Checklist
- [x] Get the Import lightbox working, and not conflicting on screen
- [x] Handling CSRF
@ -27,32 +30,52 @@ Checklist
- [x] Figure out how authentication of requests from the frontend to the API works
- [x] Figure out how to combine the nodejs realtime server into server.js
- [x] Notifications: make sure loading states are working for popup and page
- [x] Request unreadNotificationCount
- [x] Request invite code
- [x] Request user object itself
- [x] Load the metacodes
- [x] move ImportDialog lightbox into main app
- [x] create topic form
- [x] Fork map lightbox / component
- [ ] fix other places where metacode sets are used
- [ ] make newtopic form load metacodes from users selected ones
- [ ] create synapse form
- [ ] replace old loader with react loader
- [ ] ensure exports of maps work
- [ ] Notifications: make sure notifications either look nice, or redirect
- [ ] Notifications: pagination
- [ ] Notifications: Request unreadNotificationCount
- [ ] Notifications: CSS fixes related to 'controller-x' in body classes
- [ ] Make sure loading state for explore maps pages work
- [ ] Get actioncable working
- [ ] Request invite code
- [x] Request user object itself
- [ ] lightboxes
- [ ] About lightbox
- [ ] Switch Metacodes lightbox / component
- [ ] break up index.html into parts
- [ ] Handle CSS metacode colors
- [ ] Fix Request An Invite page
- [ ] Make 'new map' action work
- [ ] Modify the remaining rails templates into JSX templates
- [x] notifications list
- [x] notification page
- [ ] list metacodes
- [ ] new metacode
- [ ] edit metacode
- [ ] list metacode_sets
- [ ] new metacode set
- [ ] edit metacode set
- [x] list metacodes
- [x] new metacode
- [x] edit metacode
- [x] list metacode_sets
- [x] new metacode set
- [x] edit metacode set
- [ ] authorized apps
- [ ] registered apps
- [ ] authorize
- [ ] user passwords
- [ ] Modify the RubyOnRails app to only serve JSON responses, no HTML pages anymore
- [ ] Modify the RubyOnRails app to include an endpoint that responds with basic data the front end needs to display (such as the invite code for the user, and the current metamaps build) (a bunch of the data found here: https://github.com/metamaps/metamaps/blob/frontendonly/Metamaps.ServerData.js.erb)
- [ ] Modify the frontend to request that data from the API which is necessary at first to load the page
- [x] Load the metacodes
- [ ] Load the metacode sets
- [x] Load the metacode sets
To run the server as a daemon that will be re-run if it crashes, you can
use the forever node package.
```
$ npm install -g forever
$ forever start server.js
```

View file

@ -1,7 +1,8 @@
const request = require('request')
function apiProxyMiddleware (req, res, next) {
if (!(req.xhr || req.originalUrl.indexOf('.json') > -1 || req.method !== 'GET')) {
// TODO: tidy this up!
if (!(req.xhr || req.headers['content-type'] === 'application/json' || req.originalUrl.indexOf('.json') > -1 || req.method !== 'GET')) {
return next()
}
const method = req.method.toLowerCase()

View file

@ -7,9 +7,9 @@
"server": "node server.js",
"build": "webpack",
"build:watch": "webpack --watch",
"test": "mocha-webpack --webpack-config webpack.test.config.js --require frontend/test_support/dom.js --recursive frontend/test",
"eslint": "eslint frontend",
"eslint:fix": "eslint --fix frontend"
"test": "mocha-webpack --webpack-config webpack.test.config.js --require test_support/dom.js --recursive test",
"eslint": "eslint src",
"eslint:fix": "eslint --fix src"
},
"repository": {
"type": "git",

View file

@ -11,17 +11,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Metamaps</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Metamaps</title>
<link rel="stylesheet" media="all" href="/css/application.css">
<link rel="stylesheet" media="all" href="/css/application.css">
<!-- typekit for vinyl font -->
<!-- typekit for vinyl font -->
<script type="text/javascript" src="https://use.typekit.net/tki2nyo.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
<script type="text/javascript">try { Typekit.load(); } catch (e) { }</script>
<!--[if (IE)]>
<style type="text/css">
@ -62,35 +63,33 @@
});
</script>
<![endif]-->
</head>
<body class="unauthenticated">
<div class="main">
<div id="app"></div>
<div id="loading"></div>
</div>
<script src="/lib/jquery-1.12.4.min.js"></script>
<script src="/lib/jquery-ui.min.js"></script>
<script src="/lib/ajaxq.js"></script>
<script src="/lib/best_in_place.js"></script>
<script src="/lib/bitcoinsrc.js"></script>
<script src="/lib/canvas-to-blob.min.js"></script>
<script src="/lib/canvasloader.min.js"></script>
<script src="/lib/cloudcarousel.js"></script>
<script src="/lib/Countable.js"></script>
<script src="/lib/embedly.js"></script>
<script src="/lib/hogan-2.0.0.js"></script>
<script src="/lib/jquery.lettering.js"></script>
<script src="/lib/jquery.mousewheel.min.js"></script>
<script src="/lib/jquery.roundabout.min.js"></script>
<script src="/lib/jquery.typing-0.2.0.min.js"></script>
<script src="/lib/typeahead.bundle.js"></script>
<script type="text/javascript" src="/metamaps.bundle.js"></script>
<!--
Metamaps.ServerData.unreadNotificationsCount = { current_user ? user_unread_notification_count : 0 }
Metamaps.ServerData.mapIsStarred = { current_user && @map && current_user.starred_map?(@map) ? true : false }
<div class="main">
<div id="app"></div>
<div id="loading"></div>
</div>
<script src="/lib/jquery-1.12.4.min.js"></script>
<script src="/lib/jquery-ui.min.js"></script>
<script src="/lib/ajaxq.js"></script>
<script src="/lib/best_in_place.js"></script>
<script src="/lib/bitcoinsrc.js"></script>
<script src="/lib/canvas-to-blob.min.js"></script>
<script src="/lib/canvasloader.min.js"></script>
<script src="/lib/cloudcarousel.js"></script>
<script src="/lib/Countable.js"></script>
<script src="/lib/embedly.js"></script>
<script src="/lib/hogan-2.0.0.js"></script>
<script src="/lib/jquery.lettering.js"></script>
<script src="/lib/jquery.mousewheel.min.js"></script>
<script src="/lib/jquery.roundabout.min.js"></script>
<script src="/lib/jquery.typing-0.2.0.min.js"></script>
<script src="/lib/typeahead.bundle.js"></script>
<script type="text/javascript" src="/metamaps.bundle.js"></script>
<!--
Metamaps.ServerData.mobileTitle = "{ yield(:mobile_title) }"
Metamaps.ServerData.ActiveMapper = { current_user ? current_user.to_json({follows: true, email: true, follow_settings: true}).html_safe : nil }
{ if devise_error_messages? }
Metamaps.ServerData.toast = "{ devise_error_messages! }"
{ elsif notice }
@ -103,312 +102,201 @@
{ render :partial => 'layouts/googleanalytics' if ENV["GA_TRACKING_CODE"].present? }
-->
<script type="text/javascript">
Metamaps.ServerData = Metamaps.ServerData || {}
Metamaps.ServerData.RAILS_ENV = 'development'
Metamaps.ServerData['junto_spinner_darkgrey.gif'] = '/images/junto_spinner_darkgrey.gif'
Metamaps.ServerData['user.png'] = '/images/user.png'
Metamaps.ServerData['icons/wildcard.png'] = '/images/icons/wildcard.png'
Metamaps.ServerData['topic_description_signifier.png'] = '/images/topic_description_signifier.png'
Metamaps.ServerData['topic_link_signifier.png'] = '/images/topic_link_signifier.png'
Metamaps.ServerData['synapse16.png'] = '/images/synapse16.png'
Metamaps.ServerData['sounds/MM_sounds.mp3'] = '/images/sounds/MM_sounds.mp3'
Metamaps.ServerData['sounds/MM_sounds.ogg'] = '/images/sounds/MM_sounds.ogg'
Metamaps.ServerData['exploremaps_sprite.png'] = '/images/exploremaps_sprite.png'
Metamaps.ServerData['map_control_sprite.png'] = '/images/map_control_sprite.png'
Metamaps.ServerData['user_sprite.png'] = '/images/user_sprite.png'
Metamaps.Loading.setup()
<script type="text/javascript">
Metamaps.ServerData = Metamaps.ServerData || {}
Metamaps.ServerData.RAILS_ENV = 'development'
Metamaps.ServerData['junto_spinner_darkgrey.gif'] = '/images/junto_spinner_darkgrey.gif'
Metamaps.ServerData['user.png'] = '/images/user.png'
Metamaps.ServerData['icons/wildcard.png'] = '/images/icons/wildcard.png'
Metamaps.ServerData['topic_description_signifier.png'] = '/images/topic_description_signifier.png'
Metamaps.ServerData['topic_link_signifier.png'] = '/images/topic_link_signifier.png'
Metamaps.ServerData['synapse16.png'] = '/images/synapse16.png'
Metamaps.ServerData['sounds/MM_sounds.mp3'] = '/images/sounds/MM_sounds.mp3'
Metamaps.ServerData['sounds/MM_sounds.ogg'] = '/images/sounds/MM_sounds.ogg'
Metamaps.ServerData['exploremaps_sprite.png'] = '/images/exploremaps_sprite.png'
Metamaps.ServerData['map_control_sprite.png'] = '/images/map_control_sprite.png'
Metamaps.ServerData['user_sprite.png'] = '/images/user_sprite.png'
Metamaps.Loading.setup()
</script>
<div class="templates">
<script type="text/template" id="mapInfoBoxTemplate">
<div class="requestTitle">Click here to name this map</div>
<div class="mapInfoName" id="mapInfoName">{{{name}}}</div>
<div class="mapInfoStat">
<div class="infoStatIcon mapContributors hoverForTip">
<img id="mapContribs" class="{{contributors_class}}"
width="25" height="25" src="{{contributor_image}}" />
<span class="count">{{contributor_count}}</span>
<div class="tip">{{{contributor_list}}}</div>
</div>
<div class="infoStatIcon mapTopics">
{{topic_count}}
</div>
<div class="infoStatIcon mapSynapses">
{{synapse_count}}
</div>
<div class="infoStatIcon mapPermission {{permission}} hoverForTip">
{{{map_creator_tip}}}
</div>
<div class="clearfloat"></div>
</div>
<div class="mapInfoDesc" id="mapInfoDesc">
{{{desc}}}
</div>
<div class="mapInfoMeta">
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
<div class="mapInfoButtonsWrapper">
<div class="mapInfoThumbnail">
<div class="thumbnail"></div>
<div class="tooltip">Update Thumbnail</div>
<span>Thumb</span>
</div>
<div class="mapInfoDelete">
<div class="deleteMap"></div>
<span>Delete</span>
</div>
<div class="mapInfoShare">
<div class="mapInfoShareIcon"></div>
<span>Share</span>
</div>
</div>
</div>
</script>
<div class="templates">
<script type="text/template" id="mapInfoBoxTemplate">
<div class="requestTitle">Click here to name this map</div>
<div class="mapInfoName" id="mapInfoName">{{{name}}}</div>
<div class="mapInfoStat">
<div class="infoStatIcon mapContributors hoverForTip">
<img id="mapContribs" class="{{contributors_class}}"
width="25" height="25" src="{{contributor_image}}" />
<span class="count">{{contributor_count}}</span>
<div class="tip">{{{contributor_list}}}</div>
</div>
<div class="infoStatIcon mapTopics">
{{topic_count}}
</div>
<div class="infoStatIcon mapSynapses">
{{synapse_count}}
</div>
<div class="infoStatIcon mapPermission {{permission}} hoverForTip">
{{{map_creator_tip}}}
</div>
<div class="clearfloat"></div>
<script type="text/template" id="topicSearchTemplate">
<div class="result{{rtype}}">
<div class="topicMetacode searchResIconWrapper">
<img src="{{typeImageURL}}" class="topicIcon" />
<div class="metacodeTip">{{type}}</div>
</div>
<div class="resultText">
<p class="resultTitle">{{label}}</p>
<p class="resultDesc">{{description}}</p>
</div>
<div class="autoOptions">
<button class="addToMap hoverForTip" onclick="return Metamaps.Topic.getTopicFromSearch(event, {{id}})">
<span class="tip">add to map</span>
</button>
<div class="mapCount">
{{mapCount}}
</div>
<div class="mapInfoDesc" id="mapInfoDesc">
{{{desc}}}
<div class="synapseCount">
{{synapseCount}}
</div>
<div class="mapInfoMeta">
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
<div class="mapInfoButtonsWrapper">
<div class="mapInfoThumbnail">
<div class="thumbnail"></div>
<div class="tooltip">Update Thumbnail</div>
<span>Thumb</span>
</div>
<div class="mapInfoDelete">
<div class="deleteMap"></div>
<span>Delete</span>
</div>
<div class="mapInfoShare">
<div class="mapInfoShareIcon"></div>
<span>Share</span>
</div>
</div>
<div class="topicOriginatorIcon hoverForTip">
<img width="18" height="18" src="{{originatorImage}}">
<span class="tip topicOriginator">{{originator}}</span>
</div>
</script>
<script type="text/template" id="topicSearchTemplate">
<div class="result{{rtype}}">
<div class="topicMetacode searchResIconWrapper">
<img src="{{typeImageURL}}" class="topicIcon" />
<div class="metacodeTip">{{type}}</div>
</div>
<div class="resultText">
<p class="resultTitle">{{label}}</p>
<p class="resultDesc">{{description}}</p>
</div>
<div class="autoOptions">
<button class="addToMap hoverForTip" onclick="return Metamaps.Topic.getTopicFromSearch(event, {{id}})">
<span class="tip">add to map</span>
</button>
<div class="mapCount">
{{mapCount}}
</div>
<div class="synapseCount">
{{synapseCount}}
</div>
<div class="topicOriginatorIcon hoverForTip">
<img width="18" height="18" src="{{originatorImage}}">
<span class="tip topicOriginator">{{originator}}</span>
</div>
<div class="topicPermission {{permission}}">
</div>
</div>
<div class="clearfloat"></div>
<div class="topicPermission {{permission}}">
</div>
</script>
<script type="text/template" id="mapSearchTemplate">
<div class="result{{rtype}}">
<div class="searchResIconWrapper">
<img class="icon" src="/images/metamap36c.png">
</div>
<div class="resultText">
<p class="resultTitle">{{label}}</p>
<p class="resultDesc">{{description}}</p>
</div>
<div class="autoOptions">
<div class="topicCount">
{{topicCount}}
</div>
<div class="synapseCount">
{{synapseCount}}
</div>
<div class="mapContributorsIcon hoverForTip">
<img id="mapContribs" width="25" height="25" src="{{mapContributorImage}}" />
<div class="tip">
<ul>
{{{contributorTip}}}
</ul>
</div>
<span>{{contributorCount}}</span>
</div>
<div class="mapPermission {{permission}}">
</div>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="mapperSearchTemplate">
<div class="result{{rtype}}">
<div class="searchResIconWrapper">
<img class="icon" width="32" height="32" src="{{profile}}">
</div>
<div class="resultText">
<p class="resultTitle">{{label}}</p>
</div>
<div class="autoOptions">
<div class="mapperCreated">
<p>Mapping since: {{created_at}}</p>
</div>
<div class="mapperGeneration">
<p>Generation: {{generation}}</p>
</div>
<div class="mapCount">
{{mapCount}}
</div>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="collaboratorSearchTemplate">
<div class="collabResult">
<div class="collabIconWrapper">
<img class="icon" width="25" height="25" src="{{profile}}">
</div>
<div class="collabNameWrapper">
<p class="collabName">{{label}}</p>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="synapseAutocompleteTemplate">
<div class="result{{rtype}}">
<p class="autocompleteSection synapseDesc">{{label}}</p>
<div class="synapseMetadata">
<div class="synapseOriginatorIcon hoverForTip">
<img width="24" height="24" src="{{originatorImage}}" />
<span class="tooltips synapseOriginator">{{originator}}</span>
</div>
<div class="synapsePermission {{permission}}"></div>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="topicAutocompleteTemplate">
<div>
<img class="autocompleteSection topicType" width="24" height="24"
src="{{typeImageURL}}" alt="{{type}}" title="{{type}}" />
<p class="autocompleteSection topicTitle">{{label}}</p>
<div class="expandTopicMetadata"></div>
<div class="topicMetadata">
<div class="topicNumMaps">{{mapCount}}</div>
<div class="topicNumSynapses">{{synapseCount}}</div>
<div class="topicOriginatorIcon hoverForTip">
<img width="24" height="24" src="{{originatorImage}}" />
<span class="tooltips topicOriginator">{{originator}}</span>
</div>
<div class="topicPermission {{permission}}"></div>
</div>
<div class="clearfloat"></div>
</div>
</script>
</div>
<div class="clearfloat"></div>
</div>
</script>
<div id="lightbox_overlay">
<div id="lightbox_main">
<a id="lightbox_close" href="#"></a>
<div id="lightbox_content">
<div class="lightboxContent" id="about">
<h3>About Metamaps.cc</h3>
<div id="aboutParms">
<div id="leftAboutParms">
<p>STATUS: </p>
<p>VERSION:</p>
<p>BUILD:</p>
<p>LAST UPDATE:</p>
</div>
<div id="rightAboutParms">
<p>PRIVATE BETA</p>
<p></p>
<p></p>
<p></p>
</div>
<div class="clearfloat"></div>
</div>
<p>Metamaps.cc is a free and open source web platform that supports real-time sense-making and distributed collaboration between individuals, communities and organizations.</p>
<p>Using an intuitive graph-based interface, Metamaps.cc helps map out networks of people, ideas, resources, stories, experiences, conversations and much more. The platform is evolving for a range of applications amidst a growing network of designers, developers, facilitators, practitioners, entrepreneurs, and artists.</p>
<p>Metamaps.cc is created and maintained by a distributed community of contributors passionate about the evolution of collaboration, alternative forms of value creation and increase of collective intelligence through the lens of the open culture and the peer-to-peer revolution.</p>
<ul class="lightbox_links">
<li>
<a class="icon_twitter" href="https://twitter.com/metamapps" target="_blank">
<div class="lightboxAboutIcon"></div>
@metamapps
</a>
</li>
<li>
<a class="icon_community" href="https://www.hylo.com/c/metamaps/join/mice-late-hit-two-shown" target="_blank">
<div class="lightboxAboutIcon"></div>
community
</a>
</li>
<li>
<a class="icon_source_code" href="https://github.com/metamaps/metamaps" target="_blank">
<div class="lightboxAboutIcon"></div>
source code
</a>
</li>
<li>
<a class="icon_howtos" href="https://docs.metamaps.cc" target="_blank">
<div class="lightboxAboutIcon"></div>
howtos
</a>
</li>
<li>
<a class="icon_terms" href="https://metamaps.cc/maps/331" target="_blank">
<div class="lightboxAboutIcon"></div>
terms
</a>
</li>
</ul>
<div class="clearfloat"></div>
</div>
<div class="lightboxContent" id="noIE">
<h3>OOPS! <br> YOUR BROWSER IS NOT SUPPORTED.</h3>
<p id="noIEsubheading">To view this experience, please upgrade to the latest one of these browsers:</p>
<a id="chromeIcon" href="https://www.google.com/chrome/browser/" target="_blank">Chrome</a>
<a id="fireFoxIcon" href="https://www.mozilla.org/en-US/firefox/new/" target="_blank">Firefox</a>
<a id="safariIcon" href="http://support.apple.com/downloads/#safari" target="_blank">Safari</a>
<p id="noIEbody">While it's downloading, explore our <a href="http://blog.metamaps.cc/">blog</a>,<br> watch the <a href="http://vimeo.com/88334167">tutorials</a>, or visit our <a href="https://docs.metamaps.cc">knowledge base</a>!
</div>
<div class="lightboxContent" id="tutorial">
<h3>Tutorial</h3>
<iframe src="//player.vimeo.com/video/88334167?title=0&amp;byline=0&amp;portrait=0" width="510" height="319" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>
<div class="lightboxContent" id="cheatsheet">
<!--<%= render :partial => 'shared/cheatsheet' %>-->
</div>
<div class="lightboxContent" id="invite">
<h3>SHARE INVITE</h3>
<div class="leaveSpace"></div>
<p>The Metamaps platform is currently in an invite-only beta with the express purpose of creating a high value knowledge ecosystem, a diverse community of contributors and a culture of collaboration and curiosity.</p>
<p>As a valued beta tester, you have the ability to invite your peers, colleagues and collaborators onto the platform.</p>
<p>Below is a personal invite link containing your unique access code, which can be used multiple times.</p>
<div id="joinCodesBox">
<p class="joinCodes">
<button class="button" onclick="Metamaps.GlobalUI.shareInvite('<%= @invite_link %>');">COPY INVITE LINK!</button>
</div>
</div>
<div class="lightboxContent" id="forkmap">
<!--<%= render :partial => 'shared/forkmap' %>-->
</div>
<div class="lightboxContent" id="switchMetacodes">
<!--<%= render :partial => 'shared/switchmetacodes' %>-->
</div>
</div>
<script type="text/template" id="mapSearchTemplate">
<div class="result{{rtype}}">
<div class="searchResIconWrapper">
<img class="icon" src="/images/metamap36c.png">
</div>
<div class="resultText">
<p class="resultTitle">{{label}}</p>
<p class="resultDesc">{{description}}</p>
</div>
<div class="autoOptions">
<div class="topicCount">
{{topicCount}}
</div>
<div class="synapseCount">
{{synapseCount}}
</div>
<div class="mapContributorsIcon hoverForTip">
<img id="mapContribs" width="25" height="25" src="{{mapContributorImage}}" />
<div class="tip">
<ul>
{{{contributorTip}}}
</ul>
</div>
<div id="lightbox_screen" style="height: 100%;"></div>
</div>
<span>{{contributorCount}}</span>
</div>
<div class="mapPermission {{permission}}">
</div>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="mapperSearchTemplate">
<div class="result{{rtype}}">
<div class="searchResIconWrapper">
<img class="icon" width="32" height="32" src="{{profile}}">
</div>
<div class="resultText">
<p class="resultTitle">{{label}}</p>
</div>
<div class="autoOptions">
<div class="mapperCreated">
<p>Mapping since: {{created_at}}</p>
</div>
<div class="mapperGeneration">
<p>Generation: {{generation}}</p>
</div>
<div class="mapCount">
{{mapCount}}
</div>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="collaboratorSearchTemplate">
<div class="collabResult">
<div class="collabIconWrapper">
<img class="icon" width="25" height="25" src="{{profile}}">
</div>
<div class="collabNameWrapper">
<p class="collabName">{{label}}</p>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="synapseAutocompleteTemplate">
<div class="result{{rtype}}">
<p class="autocompleteSection synapseDesc">{{label}}</p>
<div class="synapseMetadata">
<div class="synapseOriginatorIcon hoverForTip">
<img width="24" height="24" src="{{originatorImage}}" />
<span class="tooltips synapseOriginator">{{originator}}</span>
</div>
<div class="synapsePermission {{permission}}"></div>
</div>
<div class="clearfloat"></div>
</div>
</script>
<script type="text/template" id="topicAutocompleteTemplate">
<div>
<img class="autocompleteSection topicType" width="24" height="24"
src="{{typeImageURL}}" alt="{{type}}" title="{{type}}" />
<p class="autocompleteSection topicTitle">{{label}}</p>
<div class="expandTopicMetadata"></div>
<div class="topicMetadata">
<div class="topicNumMaps">{{mapCount}}</div>
<div class="topicNumSynapses">{{synapseCount}}</div>
<div class="topicOriginatorIcon hoverForTip">
<img width="24" height="24" src="{{originatorImage}}" />
<span class="tooltips topicOriginator">{{originator}}</span>
</div>
<div class="topicPermission {{permission}}"></div>
</div>
<div class="clearfloat"></div>
</div>
</script>
</div>
</body>
</html>

View file

@ -81,67 +81,69 @@ $unread_notifications_dot_size: 8px;
}
}
.controller-notifications {
.notificationPage,
.notificationsPage {
font-family: 'din-regular', Sans-Serif;
.notificationPage,
.notificationsPage {
font-family: 'din-regular', Sans-Serif;
& a:hover {
text-decoration: none;
& a:hover {
text-decoration: none;
}
& > .notification-title {
border-bottom: 1px solid #eee;
padding-bottom: 0.25em;
margin-bottom: 0.5em;
}
.back {
margin-top: 1em;
}
}
.notificationsPage {
header {
margin-bottom: 0;
}
.emptyInbox {
padding-top: 15px;
}
}
.notificationPage {
.thirty-two-avatar {
display: inline-block;
width: 32px;
height: 32px;
border-radius: 16px;
vertical-align: middle;
margin-right: 8px;
}
.button {
line-height: 32px;
img {
margin-top: 8px;
}
& > .notification-title {
border-bottom: 1px solid #eee;
padding-bottom: 0.25em;
margin-bottom: 0.5em;
}
.back {
margin-top: 1em;
&.decline {
margin-left: 8px;
background: #DB5D5D;
&:hover {
background: #DC4B4B;
}
}
}
.notificationsPage {
header {
margin-bottom: 0;
.notification-body {
p, div {
margin: 1em auto;
line-height: 20px;
}
.emptyInbox {
padding-top: 15px;
}
}
.notificationPage {
.thirty-two-avatar {
display: inline-block;
width: 32px;
height: 32px;
border-radius: 16px;
vertical-align: middle;
}
.button {
line-height: 32px;
img {
margin-top: 8px;
}
&.decline {
background: #DB5D5D;
&:hover {
background: #DC4B4B;
}
}
}
.notification-body {
p, div {
margin: 1em auto;
line-height: 20px;
}
.accessRequestError {
color: #DB5D5D;
}
}
}

View file

@ -1,49 +0,0 @@
/* global $ */
const Admin = {
selectMetacodes: [],
allMetacodes: [],
init: function() {
var self = Admin
$('#metacodes_value').val(self.selectMetacodes.toString())
},
selectAll: function() {
var self = Admin
$('.editMetacodes li').removeClass('toggledOff')
self.selectMetacodes = self.allMetacodes.slice(0)
$('#metacodes_value').val(self.selectMetacodes.toString())
},
deselectAll: function() {
var self = Admin
$('.editMetacodes li').addClass('toggledOff')
self.selectMetacodes = []
$('#metacodes_value').val(0)
},
liClickHandler: function() {
var self = Admin
if ($(this).attr('class') !== 'toggledOff') {
$(this).addClass('toggledOff')
const valueToRemove = $(this).attr('id')
self.selectMetacodes.splice(self.selectMetacodes.indexOf(valueToRemove), 1)
$('#metacodes_value').val(self.selectMetacodes.toString())
} else if ($(this).attr('class') === 'toggledOff') {
$(this).removeClass('toggledOff')
self.selectMetacodes.push($(this).attr('id'))
$('#metacodes_value').val(self.selectMetacodes.toString())
}
},
validate: function() {
var self = Admin
if (self.selectMetacodes.length === 0) {
window.alert('Would you pretty please select at least one metacode for the set?')
return false
}
}
}
export default Admin

View file

@ -19,8 +19,6 @@ const Create = {
newSelectedMetacodes: [],
init: function() {
var self = Create
self.newTopic.init()
self.newSynapse.init()
// // SWITCHING METACODE SETS

View file

@ -1,20 +1,166 @@
function fetchWithCookies(url) {
return fetch(url, { credentials: 'same-origin' })
function get(url) {
return fetch(url, {
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
}
})
}
function post(url, data = {}) {
return fetch(url, {
credentials: 'same-origin',
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
function postNoStringify(url, data = {}) {
return fetch(url, {
credentials: 'same-origin',
method: 'POST',
body: data
})
}
function put(url, data = {}) {
return fetch(url, {
credentials: 'same-origin',
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
}
function putNoStringify(url, data = {}) {
return fetch(url, {
credentials: 'same-origin',
method: 'PUT',
body: data
})
}
function deleteReq(url) {
return fetch(url, {
credentials: 'same-origin',
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
})
}
async function getMetacodes() {
const res = await fetchWithCookies('/metacodes.json')
const res = await get('/metacodes')
const data = await res.json()
return data
}
async function getCurrentUser() {
const res = await fetchWithCookies('/users/current.json')
async function getMetacodeSets() {
const res = await get('/metacode_sets')
const data = await res.json()
return data
}
async function createMetacodeSet(metacodes, name, desc) {
const res = await post(`/metacode_sets`, {
metacodes: {
value: metacodes.toString()
},
metacode_set: {
name,
desc
}
})
if (!res.ok) {
throw new Error()
return
}
const data = await res.json()
return data
}
async function updateMetacodeSet(id, metacodes, name, desc) {
const res = await put(`/metacode_sets/${id}`, {
metacodes: {
value: metacodes.toString()
},
metacode_set: {
name,
desc
}
})
if (!res.ok) {
throw new Error()
return
}
return true
}
async function deleteMetacodeSet(id) {
const res = await deleteReq(`/metacode_sets/${id}`)
return res.ok
}
async function createMetacode(name, color, icon) {
const formdata = new FormData()
formdata.append('metacode[name]', name)
formdata.append('metacode[color]', color)
formdata.append('metacode[aws_icon]', icon)
const res = await postNoStringify(`/metacodes`, formdata)
if (!res.ok) {
throw new Error()
return
}
const data = await res.json()
return data
}
async function updateMetacode(id, name, color, icon) {
const formdata = new FormData()
formdata.append('metacode[name]', name)
formdata.append('metacode[color]', color)
if (icon) formdata.append('metacode[aws_icon]', icon)
const res = await putNoStringify(`/metacodes/${id}`, formdata)
return res.ok
}
async function getCurrentUser() {
const res = await get('/users/current')
const data = await res.json()
return data
}
async function approveAccessRequest(mapId, requestId) {
const res = await post(`/maps/${mapId}/approve_access/${requestId}`)
return res.ok
}
async function denyAccessRequest(mapId, requestId) {
const res = await post(`/maps/${mapId}/deny_access/${requestId}`)
return res.ok
}
async function requestAccess(mapId) {
const res = await post(`/maps/${mapId}/access_request`)
return res.ok
}
module.exports = {
getMetacodes,
getCurrentUser
getMetacodeSets,
createMetacodeSet,
updateMetacodeSet,
deleteMetacodeSet,
createMetacode,
updateMetacode,
getCurrentUser,
approveAccessRequest,
denyAccessRequest,
requestAccess
}

View file

@ -1,36 +1,14 @@
/* global $ */
import React from 'react'
import ReactDOM from 'react-dom'
import outdent from 'outdent'
import ImportDialogBox from '../../routes/MapView/ImportDialogBox'
import PasteInput from '../PasteInput'
import Map from '../Map'
const ImportDialog = {
openLightbox: null,
closeLightbox: null,
init: function(serverData, openLightbox, closeLightbox) {
const self = ImportDialog
self.openLightbox = openLightbox
self.closeLightbox = closeLightbox
$('#lightbox_content').append($(outdent`
<div class="lightboxContent" id="import-dialog">
<div class="importDialogWrapper" />
</div>
`))
ReactDOM.render(React.createElement(ImportDialogBox, {
onFileAdded: PasteInput.handleFile,
exampleImageUrl: serverData['import-example.png'],
downloadScreenshot: ImportDialog.downloadScreenshot,
onExport: format => () => {
window.open(`${window.location.pathname}/export.${format}`, '_blank')
}
}), $('.importDialogWrapper').get(0))
},
show: function() {
ImportDialog.openLightbox('import-dialog')

View file

@ -7,7 +7,9 @@ const Notifications = {
notificationsLoading: false,
unreadNotificationsCount: 0,
init: serverData => {
Notifications.unreadNotificationsCount = serverData.unreadNotificationsCount
if (serverData.ActiveMapper) {
Notifications.unreadNotificationsCount = serverData.ActiveMapper.unread_notifications_count
}
},
fetchNotifications: render => {
Notifications.notificationsLoading = true

View file

@ -10,10 +10,13 @@ import { notifyUser } from './index.js'
import ImportDialog from './ImportDialog'
import Notifications from './Notifications'
import Active from '../Active'
import Create from '../Create'
import DataModel from '../DataModel'
import DataFetcher from '../DataFetcher'
import { ExploreMaps, ChatView, TopicCard, ContextMenu } from '../Views'
import Filter from '../Filter'
import JIT from '../JIT'
import PasteInput from '../PasteInput'
import Realtime from '../Realtime'
import Map, { InfoBox } from '../Map'
import Topic from '../Topic'
@ -107,7 +110,10 @@ const ReactApp = {
fetchNotifications: apply(Notifications.fetchNotifications, ReactApp.render),
fetchNotification: apply(Notifications.fetchNotification, ReactApp.render),
markAsRead: apply(Notifications.markAsRead, ReactApp.render),
markAsUnread: apply(Notifications.markAsUnread, ReactApp.render)
markAsUnread: apply(Notifications.markAsUnread, ReactApp.render),
denyAccessRequest: DataFetcher.denyAccessRequest,
approveAccessRequest: DataFetcher.approveAccessRequest,
metacodes: DataModel.Metacodes.toJSON()
},
self.getMapProps(),
self.getTopicProps(),
@ -116,7 +122,8 @@ const ReactApp = {
self.getMapsProps(),
self.getContextMenuProps(),
self.getTopicCardProps(),
self.getChatProps())
self.getChatProps(),
self.getAdminProps())
},
getMapProps: function() {
const self = ReactApp
@ -134,9 +141,18 @@ const ReactApp = {
toggleMapInfoBox: InfoBox.toggleBox,
infoBoxHtml: InfoBox.html,
openImportLightbox: () => ImportDialog.show(),
openMetacodeSwitcher: () => self.openLightbox('metacodeSwitcher'),
forkMap: Map.fork,
onMapStar: Map.star,
onMapUnstar: Map.unstar
onMapUnstar: Map.unstar,
initNewTopic: Create.newTopic.init,
initNewSynapse: Create.newSynapse.init,
importHandleFile: PasteInput.handleFile,
downloadScreenshot: ImportDialog.downloadScreenshot,
onExport: format => () => {
window.open(`${window.location.pathname}/export.${format}`, '_blank')
},
requestAccess: DataFetcher.requestAccess
}
},
getCommonProps: function() {
@ -240,6 +256,16 @@ const ReactApp = {
filterAllSynapses: Filter.filterAllSynapses
}
},
getAdminProps: function() {
const self = ReactApp
return {
createMetacodeSet: DataFetcher.createMetacodeSet,
updateMetacodeSet: DataFetcher.updateMetacodeSet,
deleteMetacodeSet: DataFetcher.deleteMetacodeSet,
createMetacode: DataFetcher.createMetacode,
updateMetacode: DataFetcher.updateMetacode
}
},
resize: function() {
const self = ReactApp
const maps = ExploreMaps.collection

View file

@ -1,7 +1,5 @@
/* global $ */
import clipboard from 'clipboard-js'
import Create from '../Create'
import Notifications from './Notifications'
@ -34,6 +32,11 @@ const GlobalUI = {
})
$('#lightbox_screen, #lightbox_close').click(self.closeLightbox)
// tab the cheatsheet
$('#cheatSheet').tabs()
$('#quickReference').tabs().addClass('ui-tabs-vertical ui-helper-clearfix')
$('#quickReference .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left')
},
showDiv: function(selector) {
$(selector).show()
@ -137,19 +140,6 @@ const GlobalUI = {
ReactApp.render()
self.notifying = false
}
},
shareInvite: function(inviteLink) {
clipboard.copy({
'text/plain': inviteLink
}).then(() => {
$('#joinCodesBox .popup').remove()
$('#joinCodesBox').append('<p class="popup" style="text-align: center">Copied!</p>')
window.setTimeout(() => $('#joinCodesBox .popup').remove(), 1500)
}, () => {
$('#joinCodesBox .popup').remove()
$('#joinCodesBox').append(`<p class="popup" style="text-align: center">Your browser doesn't support copying, please copy manually.</p>`)
window.setTimeout(() => $('#joinCodesBox .popup').remove(), 1500)
})
}
}

View file

@ -1,12 +0,0 @@
/* global $ */
const CheatSheet = {
init: function() {
// tab the cheatsheet
$('#cheatSheet').tabs()
$('#quickReference').tabs().addClass('ui-tabs-vertical ui-helper-clearfix')
$('#quickReference .ui-tabs-nav li').removeClass('ui-corner-top').addClass('ui-corner-left')
}
}
export default CheatSheet

View file

@ -20,7 +20,6 @@ import ContextMenu from '../Views/ContextMenu'
import TopicCard from '../Views/TopicCard'
import Visualize from '../Visualize'
import CheatSheet from './CheatSheet'
import InfoBox from './InfoBox'
const Map = {
@ -45,7 +44,6 @@ const Map = {
InfoBox.init(serverData, function updateThumbnail() {
self.uploadMapScreenshot()
})
CheatSheet.init(serverData)
$(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper)
},
setHasLearnedTopicCreation: function(value) {
@ -130,6 +128,14 @@ const Map = {
DataModel.attachCollectionEvents()
self.requests = data.requests
isLoaded()
},
error: function(res) {
// forbidden
if (res.status === 403) {
browserHistory.push(`/maps/${id}/request_access`)
} else {
GlobalUI.notifyUser('There was an error fetching the map')
}
}
})
}
@ -383,5 +389,5 @@ const Map = {
}
}
export { CheatSheet, InfoBox }
export { InfoBox }
export default Map

View file

@ -1,5 +1,4 @@
import Active from './Active'
import Admin from './Admin'
import AutoLayout from './AutoLayout'
import Cable from './Cable'
import Control from './Control'
@ -15,7 +14,7 @@ import Import from './Import'
import JIT from './JIT'
import Listeners from './Listeners'
import Loading from './Loading'
import Map, { CheatSheet, InfoBox } from './Map'
import Map, { InfoBox } from './Map'
import Mapper from './Mapper'
import Mouse from './Mouse'
import Organize from './Organize'
@ -32,7 +31,6 @@ import Visualize from './Visualize'
const Metamaps = window.Metamaps || {}
Metamaps.Active = Active
Metamaps.Admin = Admin
Metamaps.AutoLayout = AutoLayout
Metamaps.Cable = Cable
Metamaps.Control = Control
@ -52,7 +50,6 @@ Metamaps.JIT = JIT
Metamaps.Listeners = Listeners
Metamaps.Loading = Loading
Metamaps.Map = Map
Metamaps.Map.CheatSheet = CheatSheet
Metamaps.Map.InfoBox = InfoBox
Metamaps.Maps = {}
Metamaps.Mapper = Mapper
@ -87,9 +84,13 @@ function runInitFunctions(serverData) {
document.addEventListener('DOMContentLoaded', async function() {
Metamaps.ServerData = Metamaps.ServerData || {}
try {
// TODO: do these in parallel (Promise.all)
const metacodes = await DataFetcher.getMetacodes()
Metamaps.ServerData.Metacodes = metacodes
const metacodeSets = await DataFetcher.getMetacodeSets()
Metamaps.ServerData.metacodeSets = metacodeSets
const activeMapper = await DataFetcher.getCurrentUser()
if (activeMapper) {
Metamaps.ServerData.ActiveMapper = activeMapper

View file

@ -0,0 +1,64 @@
import React, { Component } from 'react'
class About extends Component {
render = () => {
return (
<div className="lightboxContent" id="about">
<h3>About Metamaps.cc</h3>
<div id="aboutParms">
<div id="leftAboutParms">
<p>STATUS: </p>
<p>VERSION: </p>
<p>BUILD: </p>
<p>LAST UPDATE: </p>
</div>
<div id="rightAboutParms">
<p>PRIVATE BETA</p>
<p></p>
<p></p>
<p></p>
</div>
<div className="clearfloat"></div>
</div>
<p>Metamaps.cc is a free and open source web platform that supports real-time sense-making and distributed collaboration between individuals, communities and organizations.</p>
<p>Using an intuitive graph-based interface, Metamaps.cc helps map out networks of people, ideas, resources, stories, experiences, conversations and much more. The platform is evolving for a range of applications amidst a growing network of designers, developers, facilitators, practitioners, entrepreneurs, and artists.</p>
<p>Metamaps.cc is created and maintained by a distributed community of contributors passionate about the evolution of collaboration, alternative forms of value creation and increase of collective intelligence through the lens of the open culture and the peer-to-peer revolution.</p>
<ul className="lightbox_links">
<li>
<a className="icon_twitter" href="https://twitter.com/metamapps" target="_blank">
<div className="lightboxAboutIcon"></div>
@metamapps
</a>
</li>
<li>
<a className="icon_community" href="https://www.hylo.com/c/metamaps/join/mice-late-hit-two-shown" target="_blank">
<div className="lightboxAboutIcon"></div>
community
</a>
</li>
<li>
<a className="icon_source_code" href="https://github.com/metamaps/metamaps" target="_blank">
<div className="lightboxAboutIcon"></div>
source code
</a>
</li>
<li>
<a className="icon_howtos" href="https://docs.metamaps.cc" target="_blank">
<div className="lightboxAboutIcon"></div>
howtos
</a>
</li>
<li>
<a className="icon_terms" href="https://metamaps.cc/maps/331" target="_blank">
<div className="lightboxAboutIcon"></div>
terms
</a>
</li>
</ul>
<div className="clearfloat"></div>
</div>
)
}
}
export default About

View file

@ -0,0 +1,163 @@
import React, { Component } from 'react'
class CheatSheet extends Component {
render = () => {
return (
<div className="lightboxContent" id="cheatsheet">
<h3>HELP</h3>
<div id="cheatSheet">
<ul id="helpWrapper">
<li><a href="#quickReference">QUICK REFERENCE</a></li>
<li><a href="#tutorials">TUTORIAL</a></li>
<li><a href="#moreResources">MORE RESOURCES</a></li>
</ul>
<div id="quickReference">
<ul>
<li><a href="#csCreatingTopics">Creating Topics</a></li>
<li><a href="#csEditingTopics">Editing Topics</a></li>
<li><a href="#csCreatingSynapses">Creating Synapses</a></li>
<li><a href="#csEditingSynapses">Editing Synapses</a></li>
<li><a href="#csNavigation">Navigation</a></li>
<li><a href="#csSelection">Selection</a></li>
<li><a href="#csSearch">Search</a></li>
<li><a href="#csTopicView">Topic View</a></li>
<li><a href="#csKeyboardShortcuts">Keyboard Shortcuts</a></li>
</ul>
<div id="csTopicView">
<div className="csItem"><span className="csTitle">Enter Topic (radial) View:</span> Click on a Topic result from Search, or click the synapse <img src="/images/synapse16.png" width="16" /> icon inside open Topic Card on map</div>
<div className="csItem"><span className="csTitle">Recenter Topics around chosen Topic:</span> Alt + click on the topic OR Alt + E</div>
<div className="csItem"><span className="csTitle">Reveal the siblings for a Topic:</span> Right-click and choose 'Reveal siblings' OR Alt + R</div>
<div className="csItem"><span className="csTitle">Center topic and reveal siblings:</span> Alt + T</div>
<div className="csItem"><span className="csTitle">Filter out visible Topics:</span> Open Filter menu *** and toggle off/on</div>
</div>
<div id="csCreatingTopics">
<div className="csItem"><span className="csTitle">Double-click on canvas:</span> Bring up the metacode spinner</div>
<div className="csItem indented"><span className="csTitle">Scroll:</span> change metacode spinner selection</div>
<div className="csItem indented"><span className="csTitle">Tab:</span> rotate spinner counter-clockwise</div>
<div className="csItem indented"><span className="csTitle">Shift + tab:</span> rotate spinner clockwise</div>
<div className="csItem indented"><span className="csTitle">Esc:</span> Hides auto-suggestion results</div>
<div className="csItem indented"><span className="csTitle">Enter:</span> create a new topic</div>
<div className="csItem indented"><span className="csTitle">Gear Icon:</span> open up metacode settings</div>
<div className="csItem"><br /><a href="https://docs.metamaps.cc/creating_topics.html" target="_blank">Learn More</a></div>
</div>
<div id="csEditingTopics">
<div className="csItem">
<span className="csTitle">Open Topic card:</span> Double-click on topic icon
</div>
<div className="csItem indented">
<span className="csTitle">Move Topic card:</span> Click and drag on topic card metacode
</div>
<div className="csItem indented">
<span className="csTitle">Change metacode:</span> Mouse over metacode icon, then click on solid colored bar for metacode menu
</div>
<div className="csItem indented">
<span className="csTitle">Edit Topic title, description, link:</span> Click on text in respective area (click small "X" to reset link)
</div>
<div className="csItem indented">
<span className="csTitle">Save Topic title, description, link:</span> Hit enter, or click away
</div>
<div className="csItem indented">
<span className="csTitle">Change Topic permission:</span> Click on 'Permission' icon (only for topic creator)
</div>
<div className="csItem indented">
<span className="csTitle">Open Topic view:</span> Click on <img src="/images/synapse16.png" width="16" /> icon within topic card bar
</div>
<div className="csItem indented">
<span className="csTitle">Close Topic card:</span> Click on canvas
</div>
<div className="csItem">
<span className="csTitle">Open 'Context Menu':</span> Right-click/alt+click on topic icon or synapse or selection (multiple) to Hide/Remove/Delete, change metacode or permission
</div>
<div className="csItem"><br /><a href="https://docs.metamaps.cc/creating_topics.html" target="_blank">Learn More</a></div>
</div>
<div id="csCreatingSynapses">
<div className="csItem"><span className="csTitle">Open 'Create Synapse' prompt:</span> Right-click & drag from one topic to another</div>
<div className="csItem indented"><span className="csTitle">Enter a label</span> Begin typing (or leave blank)</div>
<div className="csItem indented"><span className="csTitle">Confirm new Synapse:</span> Enter or Tab</div>
<div className="csItem indented"><span className="csTitle">Cancel new Synapse:</span> Escape or Delete</div>
<div className="csItem"><span className="csTitle">Create new Topic with Synapse:</span> Right-click + drag from existing topic to open canvas</div>
<div className="csItem indented"><span className="csTitle">Create Topic:</span> Same as elsewhere</div>
<div className="csItem indented"><span className="csTitle">Create Synapse:</span> Same as above</div>
<div className="csItem"><br /><a href="https://docs.metamaps.cc/creating_synapses.html" target="_blank">Learn More</a></div>
</div>
<div id="csEditingSynapses">
<div className="csItem"><span className="csTitle">Open Synapse card:</span> Double-click on Synapse </div>
<div className="csItem indented"><span className="csTitle">Edit Synapse description:</span> Click on current description text</div>
<div className="csItem indented"><span className="csTitle">Save Synapse description:</span> Hit enter</div>
<div className="csItem indented"><span className="csTitle">Edit directionality:</span> Select appropriate arrow boxes</div>
<div className="csItem indented"><span className="csTitle">Change synapse permission:</span> Click on 'permission' icon (only for synapse creator)</div>
<div className="csItem indented"><span className="csTitle">Browse / select from multiple (stacked) synapses:</span> Click dropdown icon and select desired synapse</div>
<div className="csItem"><span className="csTitle">Open 'Context Menu':</span> Right-click/alt-click on Synapse</div>
<div className="csItem indented">*Hide/Remove/Delete synapse within context menu</div>
<div className="csItem"><br /><a href="https://docs.metamaps.cc/creating_synapses.html" target="_blank">Learn More</a></div>
</div>
<div id="csNavigation">
<div className="csItem"><span className="csTitle">Move around Canvas:</span> Click and drag</div>
<div className="csItem indented"><span className="csTitle">Zoom in/out:</span> Scroll OR click on <div id="zoomIn"> </div> & <div id="zoomOut"> </div></div>
<div className="csItem indented"><span className="csTitle">Zoom to see all:</span> Click <div id="centerMap"></div> OR Ctrl + E</div>
<div className="csItem"><span className="csTitle">Filter Map Contents:</span> Open the Filter Menu *** and toggle items off/on</div>
<div className="csItem"><span className="csTitle">Return to 'Explore Maps' (home) page:</span> Click the Metamaps logo in the upper left corner</div>
<div className="csItem"><br /><a href="https://docs.metamaps.cc/exploring_maps.html" target="_blank">Learn More</a></div>
</div>
<div id="csSelection">
<div className="csItem"><span className="csTitle">Select/Deselect Topic:</span> Click on topic icon</div>
<div className="csItem"><span className="csTitle">Select/Deselect Synapse:</span> Click on synapse</div>
<div className="csItem"><span className="csTitle">Select multiple Topics/Synapses:</span> Shift + click to include each</div>
<div className="csItem"><span className="csTitle">Select multiple with Selection Box:</span> Right-click/Shift-click + drag on Canvas</div>
<div className="csItem"><span className="csTitle">Move all selected Topics & Synapses:</span> Click + drag on selected topic(s)/synapse(s)</div>
<div className="csItem"><span className="csTitle">Open 'Context Menu':</span> Right-click/Alt-click on selected topic(s)</div>
<div className="csItem indented">*Hide/Remove/Delete/Change permissions of multiple topics & synapses within context menu</div>
<div className="csItem"><span className="csTitle">Zoom to selection box</span> Ctrl-click + drag</div>
<div className="csItem"><span className="csTitle">Deselect all topics & Synapses:</span> Click on background or Esc</div>
</div>
<div id="csSearch">
<div className="csItem"><span className="csTitle">Search for Topics and Maps:</span> Type query terms into search bar, wait for results below</div>
<div className="csItem"><span className="csTitle">Limit search results:</span> Click checkbox for only items you created; click arrow above Topics or Maps section to collapse</div>
<div className="csItem"><span className="csTitle">Add Topic to current Map:</span> Click "+" on a topic result</div>
<div className="csItem"><span className="csTitle">Jump to Topic View:</span> Click anywhere else on a topic result</div>
<div className="csItem"><span className="csTitle">Search by metacode:</span> type "[name of metacode]:", then your search query. i.e. idea:create...</div>
<div className="csItem"><span className="csTitle">Search for map:</span> type "map:", then your search query. i.e. map:exploring...</div>
<div className="csItem"><span className="csTitle">Search for mapper:</span> type "mapper:", then your search query. i.e. mapper:Robert</div>
</div>
<div id="csKeyboardShortcuts">
<div className="csItem"><span className="csTitle">Ctrl + /:</span> Open 'Search' prompt</div>
<div className="csItem"><span className="csTitle">Ctrl + H:</span> Hide selection on map</div>
<div className="csItem"><span className="csTitle">Ctrl + M:</span> Remove selection from map</div>
<div className="csItem"><span className="csTitle">Ctrl + D:</span> Delete selection</div>
<div className="csItem"><span className="csTitle">Ctrl + A:</span> Select all topics</div>
<div className="csItem"><span className="csTitle">Ctrl + E:</span> See all on map (zoom to extents)</div>
<div className="csItem"><span className="csTitle">Esc (while in search):</span> Close search</div>
<div className="csItem"><span className="csTitle">Esc (with selection):</span> Deselect all</div>
</div>
</div>
<div id="tutorials">
<iframe id="tutorialVideo" src="//player.vimeo.com/video/88334167" width="552" height="320" frameBorder="0" allowFullScreen></iframe>
</div>
<div id="moreResources">
<p>For more information about Metamaps.cc, visit our Knowledge Base or skip directly to a section by clicking on one of the categories below.</p>
<div className="resourcesColumnOne resourcesColumn">
<a href="https://hylo.com/c/metamaps" target="_blank" className="button">Hylo User Community</a>
<ul>
<li><a href="https://docs.metamaps.cc/getting_started.html" target="_blank">Getting Started</a></li>
<li><a href="https://docs.metamaps.cc/best_practices.html" target="_blank">Best Practices</a></li>
<li><a href="https://docs.metamaps.cc/applications_and_use_cases.html" target="_blank">Applications & Use Cases</a></li>
<li><a href="https://docs.metamaps.cc/advanced_features.html" target="_blank">Advanced Features</a></li>
</ul>
</div>
<div className="resourcesColumnTwo resourcesColumn">
<a href="https://docs.metamaps.cc" target="_blank" className="button">KNOWLEDGE BASE</a>
<ul>
<li><a href="https://docs.metamaps.cc/general_questions.html" target="_blank">General Questions</a></li>
<li><a href="https://docs.metamaps.cc/project_organization_and_governance.html" target="_blank">Organization & Governance</a></li>
<li><a href="https://docs.metamaps.cc/realtime_collaboration_junto.html" target="_blank">Realtime Collaboration</a></li>
<li><a href="https://docs.metamaps.cc/importing_and_exporting_data.html" target="_blank">Importing and Exporting Data</a></li>
</ul>
</div>
</div>
</div>
</div>
)
}
}
export default CheatSheet

View file

@ -0,0 +1,66 @@
import React, { Component } from 'react'
class ForkMap extends Component {
render = () => {
return (
<div className="lightboxContent" id="forkmap">
<div className="onConsole">
<form className="new_map" id="fork_map" action="/maps" acceptCharset="UTF-8" data-remote="true" method="post">
<input name="utf8" type="hidden" value="✓" />
<h3 className="forCreateMap">Save To New Map</h3>
<div className="inputGroup">
<label htmlFor="map_name">Name: </label>
<input maxLength="140" size="140" type="text" name="map[name]" id="map_name" />
<div className="clearfloat"></div>
</div>
<div className="inputGroup">
<label htmlFor="map_desc">Description: </label>
<textarea className="description" rows="5" cols="43" name="map[desc]" id="map_desc"></textarea>
<div className="clearfloat"></div>
</div>
<div className="inputGroup">
<label htmlFor="map_permission">Permission*: </label>
<p className="permHelper">*new topics and synapses take on the same permission as the map they are created on</p>
<div className="permIconWrapper">
<div className="permIcon" data-permission="commons">
<div id="newmap_co" className="mapCommonsIcon mapPermIcon selected">
<div className="tip">
Anyone with an account can edit this map. Anyone without an account can only view it.
</div>
</div>
<h4>COMMONS</h4>
</div>
<div className="permIcon" data-permission="public">
<div id="newmap_pu" className="mapPublicIcon mapPermIcon">
<div className="tip">
Only people you allow can edit this map. Anyone can view it.
</div>
</div>
<h4>PUBLIC</h4>
</div>
<div className="permIcon" data-permission="private">
<div id="newmap_pr" className="mapPrivateIcon mapPermIcon">
<div className="tip">
Only people you allow can edit this map. No one else can view it.
</div>
</div>
<h4>PRIVATE</h4>
</div>
<div className="clearfloat"></div>
</div>
<p className="permText">Anyone with an account can edit this map. Anyone without an account can only view it.</p>
<div className="clearfloat"></div>
</div>
<div className="buttonWrapper">
<button className="button cancel">Cancel</button>
<button className="button submitMap">Create!</button>
</div>
<div className="clearfloat"></div>
</form>
</div>
</div>
)
}
}
export default ForkMap

View file

@ -0,0 +1,46 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Dropzone from 'react-dropzone'
class ImportDialogBox extends Component {
handleFile = (files, e) => {
e.preventDefault() // prevent it from triggering the default drag-drop handler
this.props.onFileAdded(files[0])
}
render = () => {
return (
<div className="lightboxContent" id="import-dialog">
<div className="importDialogWrapper">
<div className="import-dialog">
<h3>EXPORT</h3>
<div className="export-csv import-blue-button" onClick={this.props.onExport('csv')}>
Export as CSV
</div>
<div className="export-json import-blue-button" onClick={this.props.onExport('json')}>
Export as JSON
</div>
<div className="download-screenshot import-blue-button" onClick={this.props.downloadScreenshot}>
Download screenshot
</div>
<h3>IMPORT</h3>
<p>To upload a file, drop it here:</p>
<Dropzone onDropAccepted={this.handleFile}
className="fileupload">
Drop files here!
</Dropzone>
<p>See <a href="https://docs.metamaps.cc/importing_and_exporting_data.html">docs.metamaps.cc</a> for instructions.</p>
</div>
</div>
</div>
)
}
}
ImportDialogBox.propTypes = {
onFileAdded: PropTypes.func,
downloadScreenshot: PropTypes.func,
onExport: PropTypes.func
}
export default ImportDialogBox

View file

@ -0,0 +1,56 @@
import React, { Component } from 'react'
import clipboard from 'clipboard-js'
class Invite extends Component {
constructor(props) {
super(props)
this.state = {
copied: false,
unable: false
}
}
inviteLink = () => {
const { host, protocol } = window ? window.location : {}
const inviteLink = `${protocol}//${host}/join?code=${this.props.inviteCode}`
return inviteLink
}
shareInvite = () => {
const inviteLink = this.inviteLink()
clipboard.copy({
'text/plain': inviteLink
}).then(() => {
this.setState({ copied: true })
window.setTimeout(() => this.setState({ copied: false }), 1500)
}, () => {
this.setState({ unable: true })
window.setTimeout(() => this.setState({ unable: false }), 1500)
})
}
render = () => {
const inviteLink = this.inviteLink()
return (
<div className="lightboxContent" id="invite">
<h3>SHARE INVITE</h3>
<div className="leaveSpace"></div>
<p>The Metamaps platform is currently in an invite-only beta with the express purpose of creating a high value knowledge ecosystem, a diverse community of contributors and a culture of collaboration and curiosity.</p>
<p>As a valued beta tester, you have the ability to invite your peers, colleagues and collaborators onto the platform.</p>
<p>Below is a personal invite link containing your unique access code, which can be used multiple times.</p>
<div id="joinCodesBox">
<p className="joinCodes">
{inviteLink}
<button className="button" onClick={this.shareInvite}>COPY INVITE LINK!</button>
</p>
<p className="popup" style={{textAlign: 'center'}}>
{this.state.copied && 'Copied!'}
{this.state.unable && "Your browser doesn't support copying, please copy manually."}
</p>
</div>
</div>
)
}
}
export default Invite

View file

@ -0,0 +1,22 @@
import React, { Component } from 'react'
class NoIE extends Component {
render = () => {
return (
<div className="lightboxContent" id="noIE">
<h3>OOPS! <br /> YOUR BROWSER IS NOT SUPPORTED.</h3>
<p id="noIEsubheading">To view this experience, please upgrade to the latest one of these browsers:</p>
<a id="chromeIcon" href="https://www.google.com/chrome/browser/" target="_blank">Chrome</a>
<a id="fireFoxIcon" href="https://www.mozilla.org/en-US/firefox/new/" target="_blank">Firefox</a>
<a id="safariIcon" href="http://support.apple.com/downloads/#safari" target="_blank">Safari</a>
<p id="noIEbody">
While it's downloading, explore our <a href="http://blog.metamaps.cc/">blog</a>,<br />
watch the <a href="http://vimeo.com/88334167">tutorials</a>,
or visit our <a href="https://docs.metamaps.cc">knowledge base</a>!
</p>
</div>
)
}
}
export default NoIE

View file

@ -0,0 +1,12 @@
import React, { Component } from 'react'
class SwitchMetacodes extends Component {
render = () => {
return (
<div className="lightboxContent" id="switchMetacodes">
</div>
)
}
}
export default SwitchMetacodes

View file

@ -0,0 +1,14 @@
import React, { Component } from 'react'
class Tutorial extends Component {
render = () => {
return (
<div className="lightboxContent" id="tutorial">
<h3>Tutorial</h3>
<iframe src="//player.vimeo.com/video/88334167?title=0&amp;byline=0&amp;portrait=0" width="510" height="319" frameBorder="0" allowFullScreen></iframe>
</div>
)
}
}
export default Tutorial

View file

@ -0,0 +1,40 @@
import React, { Component } from 'react'
import About from './About'
import CheatSheet from './CheatSheet'
import ForkMap from './ForkMap'
import ImportDialogBox from './ImportDialogBox'
import Invite from './Invite'
import NoIE from './NoIE'
import SwitchMetacodes from './SwitchMetacodes'
import Tutorial from './Tutorial'
class LightBoxes extends Component {
render = () => {
const importProps = {
onFileAdded: this.props.importHandleFile,
downloadScreenshot: this.props.downloadScreenshot,
onExport: this.props.onExport
}
return (
<div id="lightbox_overlay">
<div id="lightbox_main">
<a id="lightbox_close" href="#"></a>
<div id="lightbox_content">
<About />
<CheatSheet />
<ForkMap />
<ImportDialogBox {...importProps} />
<Invite inviteCode={this.props.inviteCode} />
<NoIE />
<SwitchMetacodes />
<Tutorial />
</div>
</div>
<div id="lightbox_screen" style={{height: '100%'}}></div>
</div>
)
}
}
export default LightBoxes

View file

@ -44,11 +44,11 @@ class MobileHeader extends Component {
</Link>
</li>
<li>
<a href="/maps/new">
<Link to="/maps/new">
<Sprite src={serverData['map_control_sprite.png']}
width={32} height={32} xIndex={4} yIndex={0} />
New Map
</a>
</Link>
</li>
<li>
<Link to="/explore/mine">
@ -79,34 +79,34 @@ class MobileHeader extends Component {
</Link>
</li>
<li>
<a href={`/users/${currentUser.id}/edit`}>
<Link to={`/users/${currentUser.id}/edit`}>
<Sprite src={serverData['user_sprite.png']}
width={32} height={32} xIndex={0} yIndex={0} />
Account
</a>
</Link>
</li>
<li className="notifications">
<a href="/notifications">
<Link to="/notifications">
<Sprite src={serverData['map_control_sprite.png']}
width={32} height={32} xIndex={0} yIndex={0} />
Notifications
</a>
</Link>
{unreadNotificationsCount > 0 && <div className="unread-notifications-dot"></div>}
</li>
<li>
<a id="Logout" href="/logout">
<Link id="Logout" to="/logout">
<Sprite src={serverData['user_sprite.png']}
width={32} height={32} xIndex={0} yIndex={3} />
Sign Out
</a>
</Link>
</li>
</ul>}
{!currentUser && <ul onClick={this.toggle}>
<li><a href="/">Home</a></li>
<li><Link to="/">Home</Link></li>
<li><Link to="/explore/active">All Maps</Link></li>
<li><Link to="/explore/featured">Featured Maps</Link></li>
<li><a href="/request">Request Invite</a></li>
<li><a href="/login">Login</a></li>
<li><Link to="/request">Request Invite</Link></li>
<li><Link to="/login">Login</Link></li>
</ul>}
</div>}
</div>

View file

@ -0,0 +1,18 @@
import React, { Component } from 'react'
class NewSynapse extends Component {
componentDidMount() {
this.props.initNewSynapse()
}
render = () => {
return (
<form className="new_synapse" id="new_synapse" action="/synapses" acceptCharset="UTF-8" data-remote="true" method="post">
<input name="utf8" type="hidden" value="✓" />
<input placeholder="describe the connection..." type="text" name="synapse[desc]" id="synapse_desc" className="tt-input" autoComplete="off" spellCheck="false" dir="auto" />
</form>
)
}
}
export default NewSynapse

View file

@ -0,0 +1,54 @@
import React, { Component } from 'react'
class NewTopic extends Component {
componentDidMount() {
this.props.initNewTopic()
}
render = () => {
const metacodes = [
{
"id": 1,
"name": "Action",
"created_at": "2017-03-04T17:33:07.394Z",
"updated_at": "2017-03-04T17:33:07.394Z",
"color": "#BD6C85",
"icon": "https://s3.amazonaws.com/metamaps-assets/metacodes/blueprint/96px/bp_action.png"
}
]
return (
<form className="new_topic" id="new_topic" action="/topics" acceptCharset="UTF-8" data-remote="true" method="post">
<input name="utf8" type="hidden" value="✓" />
<div className="openMetacodeSwitcher" onClick={() => this.props.openMetacodeSwitcher()}>
<div className="tooltipsAbove">Switch Metacodes</div>
</div>
<div className="pinCarousel">
<div className="tooltipsAbove helpPin">Pin Open</div>
<div className="tooltipsAbove helpUnpin">Unpin</div>
</div>
<div id="metacodeImg">
{metacodes.map(m => <img key={m.id} className="cloudcarousel" width="40" height="40" src={m.icon} alt={m.name} title={m.name} data-id={m.id} />)}
</div>
<input maxLength="140" placeholder="title..." size="140" type="text" name="topic[name]" id="topic_name" className="tt-input" autoComplete="off" spellCheck="false" dir="auto" />
<div id="metacodeImgTitle"></div>
<div className="clearfloat"></div>
</form>
)
}
}
export default NewTopic
/*
TODO:
{ @metacodes.each do |metacode| }
Metamaps.Create.selectedMetacodes.push("{ metacode.id }");
Metamaps.Create.newSelectedMetacodes.push("{ metacode.id }");
Metamaps.Create.selectedMetacodeNames.push("{ metacode.name }");
Metamaps.Create.newSelectedMetacodeNames.push("{ metacode.name }");
{ end }
*/

View file

@ -61,11 +61,11 @@ class NotificationBox extends Component {
}
render = () => {
const { loading } = this.props
const { notifications, loading } = this.props
return <div className='notificationsBox'>
<div className='notificationsBoxTriangle' />
<ul className='notifications'>
{loading ? this.showLoading() : this.showNotifications()}
{notifications.length === 0 && loading ? this.showLoading() : this.showNotifications()}
</ul>
</div>
}

View file

@ -1,8 +1,8 @@
import React, { Component } from 'react'
import NavBar from '../components/NavBar'
import NavBarLink from '../components/NavBarLink'
import NavBar from '../../components/NavBar'
import NavBarLink from '../../components/NavBarLink'
class Admin extends Component {
class AdminHeader extends Component {
render = () => {
return (
<NavBar>
@ -15,4 +15,4 @@ class Admin extends Component {
}
}
export default Admin
export default AdminHeader

View file

@ -0,0 +1,98 @@
import React, { Component } from 'react'
import { Link, browserHistory } from 'react-router'
import AdminHeader from './AdminHeader'
class EditMetacode extends Component {
constructor(props) {
super(props)
this.state = {
existingIcon: null,
icon: null,
name: '',
color: ''
}
}
componentDidMount() {
const { metacodes } = this.props
const id = parseInt(this.props.params.id, 10)
const metacode = metacodes.find(m => m.id === id)
this.setState({
existingIcon: metacode.icon,
name: metacode.name,
color: metacode.color
})
}
validate = (event) => {
if (this.state.name.length === 0) {
event.preventDefault()
window.alert('A name must be provided')
} else if (!this.state.color.startsWith('#')) {
event.preventDefault()
window.alert('Please begin color with a # symbol')
}
}
updateForKey = (key) => event => this.setState({[key]: event.target.value})
handleFile = (event) => {
this.setState({
icon: event.target.files[0]
})
}
onSubmit = async (event) => {
event.preventDefault()
const { name, color, icon } = this.state
const { updateMetacode, params: { id } } = this.props
try {
const result = await updateMetacode(id, name, color, icon)
browserHistory.push(`/metacodes`)
} catch (e) {
console.log(e)
window.alert('There was an error updating the metacode, check the console')
}
}
render = () => {
return (
<div>
<div id="yield">
<div className="centerContent">
<form onSubmit={this.onSubmit} className="edit_metacode" id="edit_metacode" encType="multipart/form-data" acceptCharset="UTF-8">
<input name="utf8" type="hidden" value="✓" />
<div className="field">
<label htmlFor="metacode_name">Name</label>
<input value={this.state.name} onChange={this.updateForKey('name')} type="text" name="metacode[name]" id="metacode_name" />
<div className="clearfloat"></div>
</div>
<div className="field">
<label>Current Icon</label>
<img width="96" src={this.state.existingIcon} alt={this.state.name} />
</div>
<div className="field">
<label htmlFor="metacode_Icon">Replace Icon</label>
<input type="hidden" name="metacode[manual_icon]" id="metacode_manual_icon" />
<input onChange={this.handleFile} type="file" name="metacode[aws_icon]" id="metacode_aws_icon" />
<div className="clearfloat"></div>
</div>
<div className="field">
<label htmlFor="metacode_color">Color (hex with # sign)</label>
<input value={this.state.color} onChange={this.updateForKey('color')} type="text" name="metacode[color]" id="metacode_color" />
<div className="clearfloat"></div>
</div>
<div className="actions">
<Link className="button" to="/metacodes">Cancel</Link>
<input onClick={this.validate} type="submit" name="commit" value="Update Metacode" className="add" />
</div>
</form>
</div>
</div>
<AdminHeader />
</div>
)
}
}
export default EditMetacode

View file

@ -0,0 +1,41 @@
import React, { Component } from 'react'
import { browserHistory } from 'react-router'
import AdminHeader from './AdminHeader'
import MetacodeSetEditor from './MetacodeSetEditor'
/*
TODO:
get the data actually updating after the network response
*/
class EditMetacodeSet extends Component {
onSubmit = async (metacodes, name, desc) => {
const { updateMetacodeSet, params: { id } } = this.props
try {
const result = await updateMetacodeSet(id, metacodes, name, desc)
browserHistory.push(`/metacode_sets`)
} catch (e) {
console.log(e)
window.alert('There was an error updating the metacode set')
}
}
render = () => {
const { metacodeSets, metacodes } = this.props
const id = parseInt(this.props.params.id, 10)
const metacodeSet = metacodeSets.find(m => m.id === id)
return (
<div>
<div id="yield">
<div className="centerContent">
<MetacodeSetEditor metacodeSet={metacodeSet} metacodes={metacodes} onSubmit={this.onSubmit} forEdit />
</div>
</div>
<AdminHeader />
</div>
)
}
}
export default EditMetacodeSet

View file

@ -0,0 +1,135 @@
import React, { Component } from 'react'
import { Link } from 'react-router'
class MetacodeSetEditor extends Component {
constructor(props) {
super(props)
this.state = {
selectMetacodes: [],
name: '',
desc: ''
}
}
componentDidMount() {
const { forEdit, metacodeSet } = this.props
if (forEdit) {
this.setState({
selectMetacodes: metacodeSet.metacodes,
name: metacodeSet.name,
desc: metacodeSet.desc
})
}
}
selectAll = () => {
this.setState({
selectMetacodes: this.props.metacodes.map(m => m.id)
})
}
deselectAll = () => {
this.setState({ selectMetacodes: [] })
}
liClickHandler = (metacodeId) => {
const { selectMetacodes } = this.state
if (selectMetacodes.indexOf(metacodeId) > -1) {
this.setState({
selectMetacodes: selectMetacodes.filter(id => id !== metacodeId)
})
} else {
this.setState({
selectMetacodes: selectMetacodes.concat([metacodeId])
})
}
}
updateForKey = (key) => event => this.setState({[key]: event.target.value})
validate = (event) => {
if (this.state.selectMetacodes.length === 0) {
event.preventDefault()
window.alert('Please select at least one metacode for the set')
} else if (this.state.name.length === 0) {
event.preventDefault()
window.alert('A name must be provided')
}
}
onSubmit = (event) => {
event.preventDefault()
const { selectMetacodes, name, desc } = this.state
this.props.onSubmit(selectMetacodes, name, desc)
}
render = () => {
const { selectMetacodes } = this.state
const { metacodes, forNew, forEdit } = this.props
const { length } = metacodes
return (
<form className={forNew ? "new_metacode_set" : "edit_metacode_set"} id={forNew ? "new_metacode_set" : "edit_metacode_set"} onSubmit={this.onSubmit} acceptCharset="UTF-8">
<input name="utf8" type="hidden" value="✓" />
<div className="field">
<label htmlFor="metacode_set_name">Name</label>
<input value={this.state.name} onChange={this.updateForKey('name')} type="text" name="metacode_set[name]" id="metacode_set_name" />
<div className="clearfloat"></div>
</div>
<div className="field">
<label htmlFor="metacode_set_desc">Description</label>
<textarea value={this.state.desc} onChange={this.updateForKey('desc')} cols="40" rows="4" name="metacode_set[desc]" id="metacode_set_desc" />
<div className="clearfloat"></div>
</div>
<br />
<p>Choose Metacodes</p>
<div className="allMetacodes">
<span id="showAll" onClick={this.selectAll}>Select All</span>
<span id="hideAll" onClick={this.deselectAll}>Unselect All</span>
</div>
<div className="clearfloat"></div>
<div className="editMetacodes">
<ul id="filters-one">
{metacodes.filter((m, i) => i < length/4).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
<ul id="filters-two">
{metacodes.filter((m, i) => i >= length/4 && i < length/4*2).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
<ul id="filters-three">
{metacodes.filter((m, i) => i >= length/4*2 && i < length/4*3).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
<ul id="filters-four">
{metacodes.filter((m, i) => i >= length/4*3 && i < length).map((m, i) => {
return <MetacodeListItem selected={selectMetacodes.indexOf(m.id) > -1} metacode={m} key={i} onClick={() => this.liClickHandler(m.id)} />
})}
</ul>
</div>
<div className="clearfloat"></div>
<div className="actions">
<Link className="button" to="/metacode_sets">Cancel</Link>
<input onClick={this.validate} type="submit" name="commit" value={forNew ? "Create Metacode Set" : "Update Metacode Set"} className="add" />
</div>
</form>
)
}
}
class MetacodeListItem extends Component {
render = () => {
const { selected, onClick, metacode } = this.props
return (
<li id={metacode.id} className={selected ? "" : "toggledOff"} onClick={onClick}>
<img src={metacode.icon} alt={metacode.name} />
<p>{metacode.name.toLowerCase()}</p>
<div className="clearfloat"></div>
</li>
)
}
}
export default MetacodeSetEditor

View file

@ -0,0 +1,70 @@
import React, { Component } from 'react'
import { Link } from 'react-router'
import AdminHeader from './AdminHeader'
/*
TODO:
make the delete metacode set button work
*/
class MetacodeSets extends Component {
render = () => {
const { metacodeSets, metacodes } = this.props
return (
<div>
<div id="yield">
<div className="centerContent">
<br />
<table>
<tbody>
<tr>
<th>Name</th>
<th>Description</th>
<th>Metacodes</th>
</tr>
{metacodeSets.filter(m => m.id).map((metacodeSet, i) => {
return <MetacodeSetRow key={i} metacodeSet={metacodeSet} metacodes={metacodes} />
})}
</tbody>
</table>
</div>
</div>
<AdminHeader />
</div>
)
}
}
class MetacodeSetRow extends Component {
render = () => {
const { metacodeSet, metacodes } = this.props
return (
<tr>
<td>
{metacodeSet.name}
<br />
<Link to={`/metacode_sets/${metacodeSet.id}/edit`}>Edit</Link>
<br />
<a data-confirm="Are you sure?" rel="nofollow" data-method="delete" href={`/metacode_sets/${metacodeSet.id}`}>Delete</a>
</td>
<td className="metacodeSetDesc">
{metacodeSet.desc}
</td>
<td>
{metacodeSet.metacodes.map((mId, index) => {
const metacode = metacodes.find(m => m.id === mId)
return (
<span key={index}>
<img className="metacodeSetImage" src={metacode.icon} />
{(index+1) % 4 === 0 && <div className='clearfloat'></div>}
</span>
)
})}
<div className='clearfloat'></div>
</td>
</tr>
)
}
}
export default MetacodeSets

View file

@ -0,0 +1,43 @@
import React, { Component } from 'react'
import { Link } from 'react-router'
import AdminHeader from './AdminHeader'
class Metacodes extends Component {
render = () => {
return (
<div>
<div id="yield">
<div className="centerContent">
<br />
<table>
<tbody>
<tr>
<th>Name</th>
<th>Icon</th>
<th>Color</th>
<th></th>
<th></th>
</tr>
{this.props.metacodes.map(metacode => {
return (
<tr key={metacode.id}>
<td>{metacode.name}</td>
<td className="iconURL">{metacode.icon}</td>
{metacode.color && <td className="iconColor" style={{backgroundColor: metacode.color}}>{metacode.color}</td>}
{!metacode.color && <td></td>}
<td><img width="40" src={metacode.icon} alt="metacode image" /></td>
<td><Link to={`/metacodes/${metacode.id}/edit`}>Edit</Link></td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
<AdminHeader />
</div>
)
}
}
export default Metacodes

View file

@ -0,0 +1,82 @@
import React, { Component } from 'react'
import { Link, browserHistory } from 'react-router'
import AdminHeader from './AdminHeader'
class NewMetacode extends Component {
constructor(props) {
super(props)
this.state = {
icon: null,
name: '',
color: ''
}
}
validate = (event) => {
if (this.state.name.length === 0) {
event.preventDefault()
window.alert('A name must be provided')
} else if (!this.state.color.startsWith('#')) {
event.preventDefault()
window.alert('Please begin color with a # symbol')
}
}
updateForKey = (key) => event => this.setState({[key]: event.target.value})
handleFile = (event) => {
this.setState({
icon: event.target.files[0]
})
}
onSubmit = async (event) => {
event.preventDefault()
const { name, color, icon } = this.state
const { createMetacode } = this.props
try {
const result = await createMetacode(name, color, icon)
browserHistory.push(`/metacodes`)
} catch (e) {
console.log(e)
window.alert('There was an error creating the metacode, check the console')
}
}
render = () => {
return (
<div>
<div id="yield">
<div className="centerContent">
<form onSubmit={this.onSubmit} className="new_metacode" id="new_metacode" encType="multipart/form-data" acceptCharset="UTF-8">
<input name="utf8" type="hidden" value="✓" />
<div className="field">
<label htmlFor="metacode_name">Name</label>
<input value={this.state.name} onChange={this.updateForKey('name')} type="text" name="metacode[name]" id="metacode_name" />
<div className="clearfloat"></div>
</div>
<div className="field">
<label htmlFor="metacode_Icon">Icon</label>
<input type="hidden" name="metacode[manual_icon]" id="metacode_manual_icon" />
<input onChange={this.handleFile} type="file" name="metacode[aws_icon]" id="metacode_aws_icon" />
<div className="clearfloat"></div>
</div>
<div className="field">
<label htmlFor="metacode_color">Color (hex with # sign)</label>
<input value={this.state.color} onChange={this.updateForKey('color')} type="text" name="metacode[color]" id="metacode_color" />
<div className="clearfloat"></div>
</div>
<div className="actions">
<Link className="button" to="/metacodes">Cancel</Link>
<input onClick={this.validate} type="submit" name="commit" value="Create Metacode" className="add" />
</div>
</form>
</div>
</div>
<AdminHeader />
</div>
)
}
}
export default NewMetacode

View file

@ -0,0 +1,38 @@
import React, { Component } from 'react'
import { browserHistory } from 'react-router'
import AdminHeader from './AdminHeader'
import MetacodeSetEditor from './MetacodeSetEditor'
/*
TODO:
get the data actually updating after the network response
*/
class NewMetacodeSet extends Component {
onSubmit = async (metacodes, name, desc) => {
const { createMetacodeSet } = this.props
try {
const result = await createMetacodeSet(metacodes, name, desc)
browserHistory.push(`/metacode_sets`)
} catch (e) {
console.log(e)
window.alert('There was an error creating the metacode set')
}
}
render = () => {
const { metacodes } = this.props
return (
<div>
<div id="yield">
<div className="centerContent">
<MetacodeSetEditor metacodes = {metacodes} onSubmit={this.onSubmit} forNew />
</div>
</div>
<AdminHeader />
</div>
)
}
}
export default NewMetacodeSet

View file

@ -5,6 +5,7 @@ import MobileHeader from '../components/MobileHeader'
import UpperLeftUI from '../components/UpperLeftUI'
import UpperRightUI from '../components/UpperRightUI'
import Toast from '../components/Toast'
import LightBoxes from '../components/LightBoxes'
class App extends Component {
static propTypes = {
@ -43,7 +44,8 @@ class App extends Component {
mobile, mobileTitle, mobileTitleWidth, mobileTitleClick, location,
map, userRequested, requestAnswered, requestApproved, serverData,
onRequestAccess, notifications, fetchNotifications,
markAsRead, markAsUnread, notificationsLoading } = this.props
markAsRead, markAsUnread, notificationsLoading,
importHandleFile, downloadScreenshot, onExport } = this.props
const { pathname } = location || {}
// this fixes a bug that happens otherwise when you logout
const currentUser = this.props.currentUser && this.props.currentUser.id ? this.props.currentUser : null
@ -72,6 +74,10 @@ class App extends Component {
signInPage={pathname === '/login'} />}
<Toast message={toast} />
{children}
<LightBoxes inviteCode={currentUser && currentUser.get('invite_code')}
importHandleFile={importHandleFile}
downloadScreenshot={downloadScreenshot}
onExport={onExport} />
</div>
}
}

View file

@ -1,43 +0,0 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Dropzone from 'react-dropzone'
class ImportDialogBox extends Component {
handleFile = (files, e) => {
e.preventDefault() // prevent it from triggering the default drag-drop handler
this.props.onFileAdded(files[0])
}
render = () => {
return (
<div className="import-dialog">
<h3>EXPORT</h3>
<div className="export-csv import-blue-button" onClick={this.props.onExport('csv')}>
Export as CSV
</div>
<div className="export-json import-blue-button" onClick={this.props.onExport('json')}>
Export as JSON
</div>
<div className="download-screenshot import-blue-button" onClick={this.props.downloadScreenshot}>
Download screenshot
</div>
<h3>IMPORT</h3>
<p>To upload a file, drop it here:</p>
<Dropzone onDropAccepted={this.handleFile}
className="fileupload"
>
Drop files here!
</Dropzone>
<p>See <a href="https://docs.metamaps.cc/importing_and_exporting_data.html">docs.metamaps.cc</a> for instructions.</p>
</div>
)
}
}
ImportDialogBox.propTypes = {
onFileAdded: PropTypes.func,
downloadScreenshot: PropTypes.func,
onExport: PropTypes.func
}
export default ImportDialogBox

View file

@ -9,6 +9,8 @@ import Instructions from './Instructions'
import VisualizationControls from '../../components/VisualizationControls'
import MapChat from './MapChat'
import TopicCard from '../../components/TopicCard'
import NewTopic from '../../components/NewTopic'
import NewSynapse from '../../components/NewSynapse'
export default class MapView extends Component {
@ -82,7 +84,7 @@ export default class MapView extends Component {
openImportLightbox, forkMap, openHelpLightbox,
mapIsStarred, onMapStar, onMapUnstar, openTopic,
onZoomExtents, onZoomIn, onZoomOut, hasLearnedTopicCreation,
contextMenu } = this.props
contextMenu, initNewTopic, initNewSynapse, openMetacodeSwitcher } = this.props
const { chatOpen } = this.state
const onChatOpen = () => {
this.setState({chatOpen: true})
@ -111,6 +113,8 @@ export default class MapView extends Component {
filterAllMappers={filterAllMappers}
filterAllSynapses={filterAllSynapses} />
<DataVis />
<NewTopic initNewTopic={initNewTopic} openMetacodeSwitcher={openMetacodeSwitcher} />
<NewSynapse initNewSynapse={initNewSynapse} />
{openTopic && <TopicCard {...this.props} />}
{contextMenu && <ContextMenu {...this.props} />}
{currentUser && <Instructions mobile={mobile} hasLearnedTopicCreation={hasLearnedTopicCreation} />}

View file

@ -7,12 +7,18 @@ import LoadingPage from '../helpers/LoadingPage'
import Loading from '../../components/Loading'
import NotificationBody from '../../components/NotificationBody'
/* TODO:
allow / decline access loading states
make backend serve HTML for raw body too
*/
class NotificationPage extends Component {
constructor(props) {
super(props)
this.state = {
allowPending: false,
declinePending: false,
allowed: false,
declined: false,
error: false
}
}
componentDidMount() {
// the notification id
const id = parseInt(this.props.params.id, 10)
@ -20,6 +26,35 @@ class NotificationPage extends Component {
this.props.fetchNotification(id)
}
}
deny = async () => {
const id = parseInt(this.props.params.id, 10)
const notification = this.props.notifications.find(n => n.id === id)
const request = notification.data.object
const map = notification.data.map
this.setState({ declinePending: true })
const success = await this.props.denyAccessRequest(map.id, request.id)
if (success) {
this.setState({ declined: true, declinePending: false })
} else {
this.setState({ error: true })
}
}
approve = async () => {
const id = parseInt(this.props.params.id, 10)
const notification = this.props.notifications.find(n => n.id === id)
const request = notification.data.object
const map = notification.data.map
this.setState({ allowPending: true })
const success = await this.props.approveAccessRequest(map.id, request.id)
if (success) {
this.setState({ allowed: true, allowPending: false })
} else {
this.setState({ error: true })
}
}
render = () => {
const id = parseInt(this.props.params.id, 10)
const notification = this.props.notifications.find(n => n.id === id)
@ -34,8 +69,9 @@ class NotificationPage extends Component {
const request = notification.data.object
const map = notification.data.map
const subject = notification.type === MAP_ACCESS_REQUEST ?
(<span><span style={{ fontWeight: 'bold' }} className='requesterName'>{request.user.name}</span> wants to collaborate on map <span style={{fontWeight: 'bold'}}>{ map.name }</span></span>)
(<span><span style={{ fontWeight: 'bold' }} className='requesterName'>{notification.actor.name}</span> wants to collaborate on map <span style={{fontWeight: 'bold'}}>{ map.name }</span></span>)
: notification.subject
const localAnswered = this.state.allowed || this.state.declined
return (
<div>
<div id="yield">
@ -48,21 +84,34 @@ class NotificationPage extends Component {
{subject}
</h2>
{notification.type === MAP_ACCESS_REQUEST && <div className="notification-body">
<p className="main-text">
{request.answered && <span>
<div className="main-text">
{this.state.error && <div className="accessRequestError">There was an error, please refresh and try again</div>}
{request.answered && <div>
{request.approved && <span>You already responded to this access request, and allowed access.</span>}
{!request.approved && <span>You already responded to this access request, and declined access. If you changed your mind, you can still grant
them access by going to the map and adding them as a collaborator.</span>}
</span>}
{!request.answered && <span>
</div>}
{!localAnswered && !request.answered && <div>
<img src='/images/ellipsis.gif' className='hidden' />
<a className="button allow" data-remote="true" rel="nofollow" data-method="post" href={`/maps/${map.id}/approve_access/${request.id}`}>Allow</a>
<a className="button decline" data-remote="true" rel="nofollow" data-method="post" href={`/maps/${map.id}/deny_access/${request.id}`}>Decline</a>
</span>}
</p>
<Link to={`/maps/${map.id}`}>Go to map</Link>
&nbsp;&nbsp;
<Link to={`/explore/mapper/${request.user.id}`}>View mapper profile</Link>
{!this.state.declined && !this.state.declinePending && <button onClick={this.approve} className="button allow">
{this.state.allowPending ? <img src='/images/ellipsis.gif' /> : 'Allow'}
</button>}
{!this.state.allowed && !this.state.allowPending && <button onClick={this.deny} className="button decline">
{this.state.declinePending ? <img src='/images/ellipsis.gif' /> : 'Decline'}
</button>}
</div>}
{this.state.allowed && <div>
{notification.actor.name} has been shared on the map and notified.
</div>}
{this.state.declined && <div>
Fair enough.
</div>}
</div>
<div>
<Link to={`/maps/${map.id}`}>Go to map</Link>
&nbsp;&nbsp;
<Link to={`/explore/mapper/${notification.actor.id}`}>View mapper profile</Link>
</div>
</div>}
{notification.type !== MAP_ACCESS_REQUEST && <NotificationBody notification={notification} />}
</div>
@ -73,14 +122,4 @@ class NotificationPage extends Component {
}
}
export default NotificationPage
/*
<script>
$(document).ready(function() {
$('.notification-body .button').click(function() {
$(this).html('<img src="{ asset_path('ellipsis.gif') }" />')
})
})
</script>
*/
export default NotificationPage

View file

@ -27,7 +27,7 @@ class Notifications extends Component {
render = () => {
const { notificationsLoading, markAsRead, markAsUnread } = this.props
const notifications = (this.props.notifications || []).filter(n => !(BLACKLIST.indexOf(n.type) > -1 && (!n.data.object || !n.data.map)))
if (notificationsLoading) {
if (notifications.length === 0 && notificationsLoading) {
return (
<div>
<LoadingPage />

View file

@ -1,7 +1,35 @@
import React, { Component } from 'react'
class RequestAccess extends Component {
constructor(props) {
super(props)
this.state = {
requestPending: false,
requestSent: false,
error: false
}
}
requestAccess = async () => {
if (this.state.requestSent || this.state.requestPending || this.state.error) {
return
}
this.setState({ requestPending: true })
const success = this.props.requestAccess(this.props.params.id)
if (success) {
this.setState({
requestPending: false,
requestSent: true
})
} else {
this.setState({
requestPending: false,
error: true
})
}
}
render = () => {
const { requestPending, requestSent, error } = this.state
return (
<div id="yield">
<div className='request_access'>
@ -9,32 +37,16 @@ class RequestAccess extends Component {
<div className='explainer_text'>
Hmmm. This map is private, but you can request to edit it from the map creator.
</div>
<div className='make_request'>REQUEST ACCESS</div>
<div className='make_request' onClick={this.requestAccess}>
{!requestPending && !requestSent && !error && 'REQUEST ACCESS'}
{requestSent && 'Request Sent'}
{requestPending && 'requesting...'}
{error && 'There was an error'}
</div>
</div>
</div>
)
}
}
export default RequestAccess
/*
<script>
$(document).ready(function() {
$('.make_request').click(function() {
var that = $(this)
that.off('click')
that.text('requesting...')
$.ajax({
url: '/maps/<%= params[:id] %>/access_request',
type: 'POST',
contentType: 'application/json',
statusCode: {
200: function () { that.text('Request Sent'); setTimeout(function () {window.location.href = '/'}, 2000) },
400: function () { that.text('An error occurred') }
}
})
})
})
</script>
*/
export default RequestAccess

View file

@ -1,10 +1,15 @@
import React from 'react'
import { Route, IndexRoute } from 'react-router'
import Admin from './Admin'
import { Route, IndexRoute, Redirect } from 'react-router'
import App from './App'
import Apps from './Apps'
import Maps from './Maps'
import MapView from './MapView'
import Metacodes from './Admin/Metacodes'
import NewMetacode from './Admin/NewMetacode'
import EditMetacode from './Admin/EditMetacode'
import MetacodeSets from './Admin/MetacodeSets'
import NewMetacodeSet from './Admin/NewMetacodeSet'
import EditMetacodeSet from './Admin/EditMetacodeSet'
import Notifications from './Notifications/Notifications'
import NotificationPage from './Notifications/NotificationPage'
import TopicView from './TopicView'
@ -36,9 +41,12 @@ export default function makeRoutes (currentUser) {
<Route path="request_access" component={RequestAccess} />
</Route>
<Route path="topics/:id" component={TopicView} />
<Route path="login" component={Login} />
<Route path="join" component={Join} />
<Route path="request" component={RequestInvite} />
{!currentUser && <Route path="login" component={Login} />}
{!currentUser && <Route path="join" component={Join} />}
{!currentUser && <Route path="request" component={RequestInvite} />}
{currentUser && <Redirect path="login" to="/" />}
{currentUser && <Redirect path="join" to="/" />}
{currentUser && <Redirect path="request" to="/" />}
<Route path="notifications">
<IndexRoute component={Notifications} />
<Route path=":id" component={NotificationPage} />
@ -50,14 +58,14 @@ export default function makeRoutes (currentUser) {
<Route path="password/edit" component={nullComponent} />
</Route>
<Route path="metacodes">
<IndexRoute component={Admin} />
<Route path="new" component={Admin} />
<Route path=":id/edit" component={Admin} />
<IndexRoute component={Metacodes} />
<Route path="new" component={NewMetacode} />
<Route path=":id/edit" component={EditMetacode} />
</Route>
<Route path="metacode_sets">
<IndexRoute component={Admin} />
<Route path="new" component={Admin} />
<Route path=":id/edit" component={Admin} />
<IndexRoute component={MetacodeSets} />
<Route path="new" component={NewMetacodeSet} />
<Route path=":id/edit" component={EditMetacodeSet} />
</Route>
<Route path="oauth">
<Route path="token/info" component={Apps} />

View file

@ -1,6 +1,6 @@
/* global describe, it */
import React from 'react'
import ImportDialogBox from '../../../src/routes/MapView/ImportDialogBox.js'
import ImportDialogBox from '../../../src/components/LightBoxes/ImportDialogBox.js'
import Dropzone from 'react-dropzone'
import { expect } from 'chai'
import { shallow } from 'enzyme'

View file

@ -1 +0,0 @@
$('.main-text').text($('.requesterName').text() + ' has been shared on the map and notified.')

View file

@ -1 +0,0 @@
$('.main-text').text('Fair enough.')

View file

@ -1,7 +0,0 @@
$('#notification-{ @notification.id } .notification-read-unread > a')
.text('mark as unread')
.attr('href', '{ mark_unread_notification_path(@notification.id) }')
$('#notification-{ @notification.id }')
.removeClass('unread')
.addClass('read')
Metamaps.GlobalUI.Notifications.decrementUnread(Metamaps.GlobalUI.ReactApp.render)

View file

@ -1,7 +0,0 @@
$('#notification-{ @notification.id } .notification-read-unread > a')
.text('mark as read')
.attr('href', '{ mark_read_notification_path(@notification.id) }')
$('#notification-{ @notification.id }')
.removeClass('read')
.addClass('unread')
Metamaps.GlobalUI.Notifications.incrementUnread(Metamaps.GlobalUI.ReactApp.render)

View file

@ -1,113 +0,0 @@
<div id="lightbox_overlay">
<div id="lightbox_main">
<a id="lightbox_close" href="#"></a>
<div id="lightbox_content">
<div className="lightboxContent" id="about">
<h3>About Metamaps.cc</h3>
<div id="aboutParms">
<div id="leftAboutParms">
<p>STATUS: </p>
<p>VERSION:</p>
<p>BUILD:</p>
<p>LAST UPDATE:</p>
</div>
<div id="rightAboutParms">
<p>PRIVATE BETA</p>
<p>{ METAMAPS_VERSION }</p>
<p>{ METAMAPS_BUILD }</p>
<p>{ METAMAPS_LAST_UPDATED }</p>
</div>
<div className="clearfloat"></div>
</div>
<p>Metamaps.cc is a free and open source web platform that supports real-time sense-making and distributed collaboration between individuals, communities and organizations.</p>
<p>Using an intuitive graph-based interface, Metamaps.cc helps map out networks of people, ideas, resources, stories, experiences, conversations and much more. The platform is evolving for a range of applications amidst a growing network of designers, developers, facilitators, practitioners, entrepreneurs, and artists.</p>
<p>Metamaps.cc is created and maintained by a distributed community of contributors passionate about the evolution of collaboration, alternative forms of value creation and increase of collective intelligence through the lens of the open culture and the peer-to-peer revolution.</p>
<ul className="lightbox_links">
<li>
<a className="icon_twitter" href="https://twitter.com/metamapps" target="_blank">
<div className="lightboxAboutIcon"></div>
@metamapps
</a>
</li>
<li>
<a className="icon_community" href="https://www.hylo.com/c/metamaps/join/mice-late-hit-two-shown" target="_blank">
<div className="lightboxAboutIcon"></div>
community
</a>
</li>
<li>
<a className="icon_source_code" href="https://github.com/metamaps/metamaps" target="_blank">
<div className="lightboxAboutIcon"></div>
source code
</a>
</li>
<li>
<a className="icon_howtos" href="https://docs.metamaps.cc" target="_blank">
<div className="lightboxAboutIcon"></div>
howtos
</a>
</li>
<li>
<a className="icon_terms" href="https://metamaps.cc/maps/331" target="_blank">
<div className="lightboxAboutIcon"></div>
terms
</a>
</li>
</ul>
<div className="clearfloat"></div>
</div>
<div className="lightboxContent" id="noIE">
<h3>OOPS! <br> YOUR BROWSER IS NOT SUPPORTED.</h3>
<p id="noIEsubheading">To view this experience, please upgrade to the latest one of these browsers:</p>
<a id="chromeIcon" href="https://www.google.com/chrome/browser/" target="_blank">Chrome</a>
<a id="fireFoxIcon" href="https://www.mozilla.org/en-US/firefox/new/" target="_blank">Firefox</a>
<a id="safariIcon" href="http://support.apple.com/downloads/#safari" target="_blank">Safari</a>
<p id="noIEbody">While it's downloading, explore our <a href="http://blog.metamaps.cc/">blog</a>,<br> watch the <a href="http://vimeo.com/88334167">tutorials</a>, or visit our <a href="https://docs.metamaps.cc">knowledge base</a>!
</div>
<div className="lightboxContent" id="tutorial">
<h3>Tutorial</h3>
<iframe src="//player.vimeo.com/video/88334167?title=0&amp;byline=0&amp;portrait=0" width="510" height="319" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>
<div className="lightboxContent" id="cheatsheet">
{ render :partial => 'shared/cheatsheet' }
</div>
{ if current_user }
<div className="lightboxContent" id="invite">
<h3>SHARE INVITE</h3>
<div className="leaveSpace"></div>
<p>The Metamaps platform is currently in an invite-only beta with the express purpose of creating a high value knowledge ecosystem, a diverse community of contributors and a culture of collaboration and curiosity.</p>
<p>As a valued beta tester, you have the ability to invite your peers, colleagues and collaborators onto the platform.</p>
<p>Below is a personal invite link containing your unique access code, which can be used multiple times.</p>
<div id="joinCodesBox">
<p className="joinCodes">{ invite_link() }
<button className="button" onclick="Metamaps.GlobalUI.shareInvite('{ @invite_link }');">COPY INVITE LINK!</button>
</div>
</div>
<div className="lightboxContent" id="forkmap">
{ render :partial => 'shared/forkmap' }
</div>
<div className="lightboxContent" id="switchMetacodes">
{ render :partial => 'shared/switchmetacodes' }
</div>
{ end }
</div>
</div>
<div id="lightbox_screen" style="height: 100%;"></div>
</div>

View file

@ -1,181 +0,0 @@
<div className="templates">
<script type="text/template" id="mapInfoBoxTemplate">
<div className="requestTitle">Click here to name this map</div>
<div className="mapInfoName" id="mapInfoName">{{{name}}}</div>
<div className="mapInfoStat">
<div className="infoStatIcon mapContributors hoverForTip">
<img id="mapContribs" className="{{contributors_className}}"
width="25" height="25" src="{{contributor_image}}" />
<span className="count">{{contributor_count}}</span>
<div className="tip">{{{contributor_list}}}</div>
</div>
<div className="infoStatIcon mapTopics">
{{topic_count}}
</div>
<div className="infoStatIcon mapSynapses">
{{synapse_count}}
</div>
<div className="infoStatIcon mapPermission {{permission}} hoverForTip">
{{{map_creator_tip}}}
</div>
<div className="clearfloat"></div>
</div>
<div className="mapInfoDesc" id="mapInfoDesc">
{{{desc}}}
</div>
<div className="mapInfoMeta">
<p className="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
<p className="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
<div className="mapInfoButtonsWrapper">
<div className="mapInfoThumbnail">
<div className="thumbnail"></div>
<div className="tooltip">Update Thumbnail</div>
<span>Thumb</span>
</div>
<div className="mapInfoDelete">
<div className="deleteMap"></div>
<span>Delete</span>
</div>
<div className="mapInfoShare">
<div className="mapInfoShareIcon"></div>
<span>Share</span>
</div>
</div>
</div>
</script>
<script type="text/template" id="topicSearchTemplate">
<div className="result{{rtype}}">
<div className="topicMetacode searchResIconWrapper">
<img src="{{typeImageURL}}" className="topicIcon" />
<div className="metacodeTip">{{type}}</div>
</div>
<div className="resultText">
<p className="resultTitle">{{label}}</p>
<p className="resultDesc">{{description}}</p>
</div>
<div className="autoOptions">
<button className="addToMap hoverForTip" onclick="return Metamaps.Topic.getTopicFromSearch(event, {{id}})">
<span className="tip">add to map</span>
</button>
<div className="mapCount">
{{mapCount}}
</div>
<div className="synapseCount">
{{synapseCount}}
</div>
<div className="topicOriginatorIcon hoverForTip">
<img width="18" height="18" src="{{originatorImage}}">
<span className="tip topicOriginator">{{originator}}</span>
</div>
<div className="topicPermission {{permission}}">
</div>
</div>
<div className="clearfloat"></div>
</div>
</script>
<script type="text/template" id="mapSearchTemplate">
<div className="result{{rtype}}">
<div className="searchResIconWrapper">
<img className="icon" src="/images/metamap36c.png">
</div>
<div className="resultText">
<p className="resultTitle">{{label}}</p>
<p className="resultDesc">{{description}}</p>
</div>
<div className="autoOptions">
<div className="topicCount">
{{topicCount}}
</div>
<div className="synapseCount">
{{synapseCount}}
</div>
<div className="mapContributorsIcon hoverForTip">
<img id="mapContribs" width="25" height="25" src="{{mapContributorImage}}" />
<div className="tip">
<ul>
{{{contributorTip}}}
</ul>
</div>
<span>{{contributorCount}}</span>
</div>
<div className="mapPermission {{permission}}">
</div>
</div>
<div className="clearfloat"></div>
</div>
</script>
<script type="text/template" id="mapperSearchTemplate">
<div className="result{{rtype}}">
<div className="searchResIconWrapper">
<img className="icon" width="32" height="32" src="{{profile}}">
</div>
<div className="resultText">
<p className="resultTitle">{{label}}</p>
</div>
<div className="autoOptions">
<div className="mapperCreated">
<p>Mapping since: {{created_at}}</p>
</div>
<div className="mapperGeneration">
<p>Generation: {{generation}}</p>
</div>
<div className="mapCount">
{{mapCount}}
</div>
</div>
<div className="clearfloat"></div>
</div>
</script>
<script type="text/template" id="collaboratorSearchTemplate">
<div className="collabResult">
<div className="collabIconWrapper">
<img className="icon" width="25" height="25" src="{{profile}}">
</div>
<div className="collabNameWrapper">
<p className="collabName">{{label}}</p>
</div>
<div className="clearfloat"></div>
</div>
</script>
<script type="text/template" id="synapseAutocompleteTemplate">
<div className="result{{rtype}}">
<p className="autocompleteSection synapseDesc">{{label}}</p>
<div className="synapseMetadata">
<div className="synapseOriginatorIcon hoverForTip">
<img width="24" height="24" src="{{originatorImage}}" />
<span className="tooltips synapseOriginator">{{originator}}</span>
</div>
<div className="synapsePermission {{permission}}"></div>
</div>
<div className="clearfloat"></div>
</div>
</script>
<script type="text/template" id="topicAutocompleteTemplate">
<div>
<img className="autocompleteSection topicType" width="24" height="24"
src="{{typeImageURL}}" alt="{{type}}" title="{{type}}" />
<p className="autocompleteSection topicTitle">{{label}}</p>
<div className="expandTopicMetadata"></div>
<div className="topicMetadata">
<div className="topicNumMaps">{{mapCount}}</div>
<div className="topicNumSynapses">{{synapseCount}}</div>
<div className="topicOriginatorIcon hoverForTip">
<img width="24" height="24" src="{{originatorImage}}" />
<span className="tooltips topicOriginator">{{originator}}</span>
</div>
<div className="topicPermission {{permission}}"></div>
</div>
<div className="clearfloat"></div>
</div>
</script>
</div>

View file

@ -1,3 +0,0 @@
{ form_for Synapse.new, url: synapses_url, remote: true do |form| }
{ form.text_field :desc, :placeholder => "describe the connection..." }
{ end }

View file

@ -1,32 +0,0 @@
{ @metacodes = user_metacodes() }
{ form_for Topic.new, url: topics_url, remote: true do |form| }
<div className="openMetacodeSwitcher openLightbox" data-open="switchMetacodes">
<div className="tooltipsAbove">Switch Metacodes</div>
</div>
<div className="pinCarousel">
<div className="tooltipsAbove helpPin">Pin Open</div>
<div className="tooltipsAbove helpUnpin">Unpin</div>
</div>
<div id="metacodeImg">
{ @metacodes.each do |metacode| }
<img className="cloudcarousel" width="40" height="40" src="{ asset_path metacode.icon }" alt="{ metacode.name }" title="{ metacode.name }" data-id="{ metacode.id }" />
{ end }
</div>
{ form.text_field :name, :maxlength => 140, :placeholder => "title..." }
<div id="metacodeImgTitle"></div>
<div className="clearfloat"></div>
<script>
{ @metacodes.each do |metacode| }
Metamaps.Create.selectedMetacodes.push("{ metacode.id }");
Metamaps.Create.newSelectedMetacodes.push("{ metacode.id }");
Metamaps.Create.selectedMetacodeNames.push("{ metacode.name }");
Metamaps.Create.newSelectedMetacodeNames.push("{ metacode.name }");
{ end }
</script>
{ end }

View file

@ -1,89 +0,0 @@
{ form_for(@metacode_set) do |f| }
{ if @metacode_set.errors.any? }
<div id="error_explanation">
<h2>{ pluralize(@metacode_set.errors.count, "error") } prohibited this metacode set from being saved:</h2>
<ul>
{ @metacode_set.errors.full_messages.each do |msg| }
<li>{ msg }</li>
{ end }
</ul>
</div>
{ end }
<div className="field">
{ f.label :name }
{ f.text_field :name }
<div className="clearfloat"></div>
</div>
<div className="field">
{ f.label :desc, "Description" }
{ f.text_area :desc, :cols => "40", :rows => "4" }
<div className="clearfloat"></div>
</div>
<br />
<p>Choose Metacodes</p>
<div className="allMetacodes">
<span id="showAll" onclick="Metamaps.Admin.selectAll();">Select All</span>
<span id="hideAll" onclick="Metamaps.Admin.deselectAll();">Unselect All</span>
</div>
<div className="clearfloat"></div>
<div className="editMetacodes">
<ul id="filters-one">
{ $i = 0 }
{ @m = Metacode.order("name").all }
{ while $i < (Metacode.all.length / 4) do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
<ul id="filters-two">
{ while $i < (Metacode.all.length / 4 * 2) do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
<ul id="filters-three">
{ while $i < (Metacode.all.length / 4 * 3) do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
<ul id="filters-four">
{ while $i < Metacode.all.length do }
<li id="{ @m[$i].id }" { if not @m[$i].in_metacode_set(@metacode_set) }className="toggledOff"{ end }
onclick="Metamaps.Admin.liClickHandler.call(this);">
<img src="{ asset_path @m[$i].icon }" alt="{ @m[$i].name }" />
<p>{ @m[$i].name.downcase }</p>
<div className="clearfloat"></div>
</li>
{ $i += 1 }
{ end }
</ul>
</div>
{ hidden_field(:metacodes, :value, {:value => 0}) }
<div className="clearfloat"></div>
<div className="actions">
{ link_to 'Cancel', metacode_sets_path,
{ :className => 'button' } }
{ f.submit :className => 'add', :onclick => "return Metamaps.Admin.validate();" }
</div>
{ end }

View file

@ -1,24 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
{ render 'form' }
</div>
</div>
)
}
}
export default MyComponent
<script>
{ Metacode.all.each do |m| }
{ if m.in_metacode_set(@metacode_set) }
Metamaps.Admin.selectMetacodes.push("{ m.id }");
{ end }
Metamaps.Admin.allMetacodes.push("{ m.id }");
{ end }
</script>

View file

@ -1,46 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
<br />
<table>
<tr>
<th>Name</th>
<th className='metacodeSetsDescription'>Description</th>
<th>Metacodes</th>
</tr>
{ @metacode_sets.each do |metacode_set| }
<tr>
<td>
{ metacode_set.name }<br />
{ link_to 'Edit',
edit_metacode_set_path(metacode_set) }
<br />
{ link_to 'Delete',
metacode_set, method: :delete,
data: { confirm: 'Are you sure?' } }
</td>
<td className='metacodeSetDesc'>{ metacode_set.desc }</td>
<td>
{ metacode_set.metacodes.each_with_index do |metacode, index| }
<img className='metacodeSetImage' src='{ asset_path metacode.icon }' />
{ if (index+1)%4 == 0 }
<div className='clearfloat'></div>
{ end }
{ end }
<div className='clearfloat'></div>
</td>
</tr>
{ end }
</table>
</div>
</div>
)
}
}
export default MyComponent

View file

@ -1,24 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
{ render 'form' }
</div>
</div>
)
}
}
export default MyComponent
<script>
{ Metacode.all.each do |m| }
{ if m.in_metacode_set(@metacode_set) }
Metamaps.Admin.selectMetacodes.push("{ m.id }");
{ end }
Metamaps.Admin.allMetacodes.push("{ m.id }");
{ end }
</script>

View file

@ -1,43 +0,0 @@
{ form_for(@metacode) do |f| }
{ if @metacode.errors.any? }
<div id="error_explanation">
<h2>{ pluralize(@metacode.errors.count, "error") } prohibited this metacode from being saved:</h2>
<ul>
{ @metacode.errors.full_messages.each do |msg| }
<li>{ msg }</li>
{ end }
</ul>
</div>
{ end }
<div className="field">
{ f.label :name }
{ f.text_field :name }
<div className="clearfloat"></div>
</div>
{ unless @metacode.new_record? }
<div className="field">
{ f.label 'Current Icon' }
{ image_tag @metacode.icon, width: 96 }
</div>
{ end }
<div className="field">
{ if @metacode.new_record? }
{ f.label 'Icon' }
{ else }
{ f.label 'Replace Icon: ' }
{ end }
{ f.hidden_field :manual_icon, value: nil }
{ f.file_field :aws_icon }
<div className="clearfloat"></div>
</div>
<div className="field">
{ f.label :color, "Color (hex with # sign)" }
{ f.text_field :color }
<div className="clearfloat"></div>
</div>
<div className="actions">
{ link_to 'Cancel', metacodes_path, { :className => 'button' } }
{ f.submit :className => 'add' }
</div>
{ end }

View file

@ -1,15 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
{ render 'form' }
</div>
</div>
)
}
}
export default MyComponent

View file

@ -1,40 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
<br />
<table>
<tr>
<th>Name</th>
<th>Icon</th>
<th>Color</th>
<th></th>
<th></th>
</tr>
{ @metacodes.each do |metacode| }
<tr>
<td>{ metacode.name }</td>
<td className="iconURL">{ metacode.icon }</td>
{ if metacode.color }
<td className="iconColor" style="background-color: { metacode.color }">
{ metacode.color }
</td>
{ else }
<td></td>
{ end }
<td>{ image_tag metacode.icon, width: 40 }</td>
<td>{ link_to 'Edit', edit_metacode_path(metacode) }</td>
</tr>
{ end }
</table>
</div>
</div>
)
}
}
export default MyComponent

View file

@ -1,15 +0,0 @@
import React, { Component } from react
class MyComponent extends Component {
render = () => {
return (
<div id="yield">
<div className='centerContent'>
{ render 'form' }
</div>
</div>
)
}
}
export default MyComponent