user editing in jsx
This commit is contained in:
parent
0220874590
commit
d765487635
19 changed files with 326 additions and 275 deletions
|
@ -4,18 +4,18 @@ function apiProxyMiddleware (req, res, next) {
|
|||
if (!(req.xhr || req.originalUrl.indexOf('.json') > -1 || req.method !== 'GET')) {
|
||||
return next()
|
||||
}
|
||||
console.log('xhr request', req.originalUrl)
|
||||
const method = req.method.toLowerCase()
|
||||
req.pipe(
|
||||
request[method](process.env.API + req.originalUrl, {
|
||||
headers: {
|
||||
...req.headers,
|
||||
host: process.env.API
|
||||
cookie: `_Metamaps_session=${req.cookies._Metamaps_session}`,
|
||||
host: 'localhost:3001'
|
||||
},
|
||||
followRedirect: false
|
||||
})
|
||||
)
|
||||
.on('error', console.log)
|
||||
.on('error', (err) => console.log(err))
|
||||
.pipe(res)
|
||||
}
|
||||
|
||||
|
|
34
package-lock.json
generated
34
package-lock.json
generated
|
@ -67,9 +67,7 @@
|
|||
}
|
||||
},
|
||||
"action-cable-node": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/action-cable-node/-/action-cable-node-1.2.2.tgz",
|
||||
"integrity": "sha1-KwXl/iR+qqRXfwct2lDOCiEYOS0="
|
||||
"version": "github:Connoropolous/action-cable-node#346d691343251b4148ccc0607f87728d4d97f2cd"
|
||||
},
|
||||
"after": {
|
||||
"version": "0.8.1",
|
||||
|
@ -1909,7 +1907,7 @@
|
|||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
|
||||
"dev": true
|
||||
},
|
||||
"content-type-parser": {
|
||||
|
@ -1926,14 +1924,21 @@
|
|||
"cookie": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
||||
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
|
||||
"dev": true
|
||||
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
||||
},
|
||||
"cookie-parser": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
|
||||
"integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
|
||||
"requires": {
|
||||
"cookie": "0.3.1",
|
||||
"cookie-signature": "1.0.6"
|
||||
}
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
|
||||
"dev": true
|
||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
||||
},
|
||||
"copy-descriptor": {
|
||||
"version": "0.1.1",
|
||||
|
@ -5479,6 +5484,11 @@
|
|||
"jquery": "3.2.1"
|
||||
}
|
||||
},
|
||||
"js-cookie": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.0.tgz",
|
||||
"integrity": "sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
|
@ -6297,7 +6307,7 @@
|
|||
"mime": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
|
||||
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
|
||||
"integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=",
|
||||
"dev": true
|
||||
},
|
||||
"mime-db": {
|
||||
|
@ -8276,7 +8286,7 @@
|
|||
"send": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
|
||||
"integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
|
||||
"integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "2.6.9",
|
||||
|
@ -8320,7 +8330,7 @@
|
|||
"serve-static": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
|
||||
"integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
|
||||
"integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"encodeurl": "1.0.2",
|
||||
|
@ -8368,7 +8378,7 @@
|
|||
"setprototypeof": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
|
||||
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
|
||||
"integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=",
|
||||
"dev": true
|
||||
},
|
||||
"sha.js": {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
"backbone": "1.3.3",
|
||||
"clipboard-js": "0.3.5",
|
||||
"commonmark": "0.28.1",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"csv-parse": "1.2.1",
|
||||
"emoji-mart": "1.0.1",
|
||||
"getscreenmedia": "4.0.1",
|
||||
|
|
|
@ -64,8 +64,7 @@
|
|||
<![endif]-->
|
||||
|
||||
</head>
|
||||
<!-- TODO: make 'authenticated' class dynamic -->
|
||||
<body class="authenticated">
|
||||
<body class="unauthenticated">
|
||||
<div class="main">
|
||||
<div id="app"></div>
|
||||
<div id="loading"></div>
|
||||
|
@ -107,13 +106,6 @@
|
|||
<script type="text/javascript">
|
||||
Metamaps.ServerData = Metamaps.ServerData || {}
|
||||
Metamaps.ServerData.RAILS_ENV = 'development'
|
||||
Metamaps.ServerData.ActiveMapper = {
|
||||
id: 10,
|
||||
name: 'Connor',
|
||||
email: 'co@co.com',
|
||||
admin: false,
|
||||
image: 'https://metamaps-live.s3.amazonaws.com/users/images/555/629/996/sixtyfour/11835c3.png?1417298429'
|
||||
}
|
||||
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'
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
@import 'admin';
|
||||
@import 'apps';
|
||||
@import 'base';
|
||||
@import 'clean';
|
||||
@import 'emoji-mart-0.3.5';
|
||||
@import 'feedback';
|
||||
@import 'jquery-ui';
|
||||
@import 'junto';
|
||||
@import 'mapcard';
|
||||
@import 'mobile';
|
||||
@import 'notifications';
|
||||
@import 'request_access';
|
||||
@import 'search';
|
||||
|
||||
/* clear styles */
|
||||
|
||||
html,
|
||||
|
@ -243,10 +229,6 @@ button.button.btn-no:hover {
|
|||
color:#424242;
|
||||
}
|
||||
|
||||
.toHide {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.forgotPassword {
|
||||
height: 134px;
|
||||
font-family: din-medium;
|
||||
|
@ -327,7 +309,6 @@ button.button.btn-no:hover {
|
|||
position: absolute;
|
||||
top: 88px;
|
||||
left: -18px;
|
||||
display: none;
|
||||
}
|
||||
.userMenuArrow {
|
||||
border-color: transparent;
|
||||
|
@ -436,7 +417,6 @@ button.button.btn-no:hover {
|
|||
margin:5px 0px 0px 5px;
|
||||
}
|
||||
.changeName {
|
||||
display: none;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
|
@ -3029,3 +3009,17 @@ script.data-gratipay-username {
|
|||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@import 'admin';
|
||||
@import 'apps';
|
||||
@import 'base';
|
||||
@import 'clean';
|
||||
@import 'emoji-mart-0.3.5';
|
||||
@import 'feedback';
|
||||
@import 'jquery-ui';
|
||||
@import 'junto';
|
||||
@import 'mapcard';
|
||||
@import 'mobile';
|
||||
@import 'notifications';
|
||||
@import 'request_access';
|
||||
@import 'search';
|
|
@ -1,5 +1,6 @@
|
|||
const path = require('path')
|
||||
const express = require('express')
|
||||
const cookieParser = require('cookie-parser')
|
||||
const webpack = require('webpack')
|
||||
const socketio = require('socket.io')
|
||||
const { createServer } = require('http')
|
||||
|
@ -13,6 +14,8 @@ const server = createServer(app)
|
|||
const io = socketio(server)
|
||||
realtime(io) // sets up the socketio event listeners
|
||||
|
||||
app.use(cookieParser())
|
||||
|
||||
// serve the whole public folder as static files
|
||||
app.use(express.static(path.join(__dirname, 'public')))
|
||||
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
/* global $, CanvasLoader */
|
||||
|
||||
const Account = {
|
||||
init: function(serverData) {
|
||||
Account.userIconUrl = serverData['user.png']
|
||||
},
|
||||
listenersInitialized: false,
|
||||
userIconUrl: null,
|
||||
initListeners: function() {
|
||||
var self = Account
|
||||
|
||||
$('#user_image').change(self.showImagePreview)
|
||||
self.listenersInitialized = true
|
||||
},
|
||||
toggleChangePicture: function() {
|
||||
var self = Account
|
||||
|
||||
$('.userImageMenu').toggle()
|
||||
if (!self.listenersInitialized) self.initListeners()
|
||||
},
|
||||
openChangePicture: function() {
|
||||
var self = Account
|
||||
|
||||
$('.userImageMenu').show()
|
||||
if (!self.listenersInitialized) self.initListeners()
|
||||
},
|
||||
closeChangePicture: function() {
|
||||
$('.userImageMenu').hide()
|
||||
},
|
||||
showLoading: function() {
|
||||
var loader = new CanvasLoader('accountPageLoading')
|
||||
loader.setColor('#4FC059') // default is '#000000'
|
||||
loader.setDiameter(28) // default is 40
|
||||
loader.setDensity(41) // default is 40
|
||||
loader.setRange(0.9) // default is 1.3
|
||||
loader.show() // Hidden by default
|
||||
$('#accountPageLoading').show()
|
||||
},
|
||||
showImagePreview: function() {
|
||||
var file = $('#user_image')[0].files[0]
|
||||
|
||||
var reader = new window.FileReader()
|
||||
|
||||
reader.onload = function(e) {
|
||||
var $canvas = $('<canvas>').attr({
|
||||
width: 84,
|
||||
height: 84
|
||||
})
|
||||
var context = $canvas[0].getContext('2d')
|
||||
var imageObj = new window.Image()
|
||||
|
||||
imageObj.onload = function() {
|
||||
$('.userImageDiv canvas').remove()
|
||||
$('.userImageDiv img').hide()
|
||||
|
||||
var imgWidth = imageObj.width
|
||||
var imgHeight = imageObj.height
|
||||
|
||||
var dimensionToMatch = imgWidth > imgHeight ? imgHeight : imgWidth
|
||||
// draw cropped image
|
||||
var nonZero = Math.abs(imgHeight - imgWidth) / 2
|
||||
var sourceX = dimensionToMatch === imgWidth ? 0 : nonZero
|
||||
var sourceY = dimensionToMatch === imgHeight ? 0 : nonZero
|
||||
var sourceWidth = dimensionToMatch
|
||||
var sourceHeight = dimensionToMatch
|
||||
var destX = 0
|
||||
var destY = 0
|
||||
var destWidth = 84
|
||||
var destHeight = 84
|
||||
|
||||
context.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight)
|
||||
$('.userImageDiv').prepend($canvas)
|
||||
}
|
||||
imageObj.src = reader.result
|
||||
}
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
$('.userImageMenu').hide()
|
||||
$('#remove_image').val('0')
|
||||
}
|
||||
},
|
||||
removePicture: function() {
|
||||
var self = Account
|
||||
|
||||
$('.userImageDiv canvas').remove()
|
||||
$('.userImageDiv img').attr('src', self.userIconUrl).show()
|
||||
$('.userImageMenu').hide()
|
||||
|
||||
var input = $('#user_image')
|
||||
input.replaceWith(input.val('').clone(true))
|
||||
$('#remove_image').val('1')
|
||||
},
|
||||
changeName: function() {
|
||||
$('.accountName').hide()
|
||||
$('.changeName').show()
|
||||
},
|
||||
showPass: function() {
|
||||
$('.toHide').show()
|
||||
$('.changePass').hide()
|
||||
},
|
||||
hidePass: function() {
|
||||
$('.toHide').hide()
|
||||
$('.changePass').show()
|
||||
|
||||
$('#current_password').val('')
|
||||
$('#user_password').val('')
|
||||
$('#user_password_confirmation').val('')
|
||||
}
|
||||
}
|
||||
|
||||
export default Account
|
|
@ -1,10 +1,20 @@
|
|||
function fetchWithCookies(url) {
|
||||
return fetch(url, { credentials: 'same-origin' })
|
||||
}
|
||||
|
||||
async function getMetacodes() {
|
||||
const res = await fetch('/metacodes.json')
|
||||
const res = await fetchWithCookies('/metacodes.json')
|
||||
const data = await res.json()
|
||||
return data
|
||||
}
|
||||
|
||||
async function getCurrentUser() {
|
||||
const res = await fetchWithCookies('/users/current.json')
|
||||
const data = await res.json()
|
||||
return data
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMetacodes
|
||||
getMetacodes,
|
||||
getCurrentUser
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import Account from './Account'
|
||||
import Active from './Active'
|
||||
import Admin from './Admin'
|
||||
import AutoLayout from './AutoLayout'
|
||||
|
@ -32,7 +31,6 @@ import Views from './Views'
|
|||
import Visualize from './Visualize'
|
||||
|
||||
const Metamaps = window.Metamaps || {}
|
||||
Metamaps.Account = Account
|
||||
Metamaps.Active = Active
|
||||
Metamaps.Admin = Admin
|
||||
Metamaps.AutoLayout = AutoLayout
|
||||
|
@ -91,6 +89,12 @@ document.addEventListener('DOMContentLoaded', async function() {
|
|||
try {
|
||||
const metacodes = await DataFetcher.getMetacodes()
|
||||
Metamaps.ServerData.Metacodes = metacodes
|
||||
|
||||
const activeMapper = await DataFetcher.getCurrentUser()
|
||||
if (activeMapper) {
|
||||
Metamaps.ServerData.ActiveMapper = activeMapper
|
||||
$('body').removeClass('unauthenticated').addClass('authenticated')
|
||||
}
|
||||
runInitFunctions(Metamaps.ServerData)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import onClickOutsideAddon from 'react-onclickoutside'
|
||||
|
||||
|
@ -22,15 +23,15 @@ class AccountMenu extends Component {
|
|||
<ul>
|
||||
<li className="accountListItem accountSettings">
|
||||
<div className="accountIcon"></div>
|
||||
<a href={`/users/${currentUser.id}/edit`}>Settings</a>
|
||||
<Link to={`/users/${currentUser.id}/edit`}>Settings</Link>
|
||||
</li>
|
||||
<li className="accountListItem accountAdmin">
|
||||
<div className="accountIcon"></div>
|
||||
<a href="/metacodes">Admin</a>
|
||||
<Link to="/metacodes">Admin</Link>
|
||||
</li>
|
||||
<li className="accountListItem accountApps">
|
||||
<div className="accountIcon"></div>
|
||||
<a href="/oauth/authorized_applications">Apps</a>
|
||||
<Link to="/oauth/authorized_applications">Apps</Link>
|
||||
</li>
|
||||
<li className="accountListItem accountInvite" onClick={onInviteClick}>
|
||||
<div className="accountIcon"></div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
import onClickOutsideAddon from 'react-onclickoutside'
|
||||
|
||||
|
@ -43,7 +44,7 @@ class LoginForm extends Component {
|
|||
</div>
|
||||
<div className="clearfloat"></div>
|
||||
<div className="accountForgotPass">
|
||||
<a href="/users/password/new">Forgot password?</a>
|
||||
<Link to="/users/password/new">Forgot password?</Link>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import _ from 'lodash'
|
|||
|
||||
const PROP_LIST = [
|
||||
'matchChildRoutes',
|
||||
'hardReload',
|
||||
'show',
|
||||
'text',
|
||||
'href',
|
||||
|
@ -15,7 +14,6 @@ const PROP_LIST = [
|
|||
class NavBarLink extends Component {
|
||||
static propTypes = {
|
||||
matchChildRoutes: PropTypes.bool,
|
||||
hardReload: PropTypes.bool,
|
||||
show: PropTypes.bool,
|
||||
text: PropTypes.string,
|
||||
href: PropTypes.string,
|
||||
|
@ -29,7 +27,6 @@ class NavBarLink extends Component {
|
|||
render = () => {
|
||||
const {
|
||||
matchChildRoutes,
|
||||
hardReload,
|
||||
show,
|
||||
text,
|
||||
href,
|
||||
|
@ -45,14 +42,6 @@ class NavBarLink extends Component {
|
|||
if (!show) {
|
||||
return null
|
||||
}
|
||||
if (hardReload) {
|
||||
return (
|
||||
<a { ...otherProps } href={href} className={classes.join(' ')}>
|
||||
{linkClass && <div className="navBarIcon"></div>}
|
||||
<div className="navBarLinkText">{text}</div>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Link { ...otherProps } to={href} className={classes.join(' ')}>
|
||||
{linkClass && <div className="navBarIcon"></div>}
|
||||
|
|
|
@ -16,8 +16,7 @@ class UpperLeftUI extends Component {
|
|||
const { map, currentUser, userRequested, requestAnswered, requestApproved, onRequestClick } = this.props
|
||||
return <div className="upperLeftUI">
|
||||
<div className="homeButton">
|
||||
{currentUser && <Link to="/">METAMAPS</Link>}
|
||||
{!currentUser && <a href="/">METAMAPS</a>}
|
||||
<Link to="/">METAMAPS</Link>
|
||||
</div>
|
||||
<div className="sidebarSearch">
|
||||
<input type="text" className="sidebarSearchField" placeholder="Search for topics, maps, and mappers..." />
|
||||
|
|
|
@ -6,10 +6,10 @@ class Admin extends Component {
|
|||
render = () => {
|
||||
return (
|
||||
<NavBar>
|
||||
<NavBarLink show hardReload href="/metacode_sets" text="Metacode Sets" />
|
||||
<NavBarLink show hardReload href="/metacode_sets/new" text="New Set" />
|
||||
<NavBarLink show hardReload href="/metacodes" text="Metacodes" />
|
||||
<NavBarLink show hardReload href="/metacodes/new" text="New Metacode" />
|
||||
<NavBarLink show href="/metacode_sets" text="Metacode Sets" />
|
||||
<NavBarLink show href="/metacode_sets/new" text="New Set" />
|
||||
<NavBarLink show href="/metacodes" text="Metacodes" />
|
||||
<NavBarLink show href="/metacodes/new" text="New Metacode" />
|
||||
</NavBar>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ class Apps extends Component {
|
|||
|
||||
return (
|
||||
<NavBar>
|
||||
{currentUser && currentUser.get('admin') && <NavBarLink show hardReload
|
||||
{currentUser && currentUser.get('admin') && <NavBarLink show
|
||||
matchChildRoutes href="/oauth/applications" linkClass="activeMaps"
|
||||
text="Registered Apps" />}
|
||||
<NavBarLink show hardReload matchChildRoutes
|
||||
<NavBarLink show matchChildRoutes
|
||||
href="/oauth/authorized_applications"
|
||||
linkClass="authedApps" text="Authorized Apps" />
|
||||
<NavBarLink show href="/" linkClass="myMaps" text="Maps" />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { Component } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
|
||||
class LoggedOutHome extends Component {
|
||||
render = () => {
|
||||
|
@ -17,8 +18,8 @@ class LoggedOutHome extends Component {
|
|||
<h3>Who finds it useful?</h3>
|
||||
<p>Designers, inventors, artists, educators, strategists, consultants, facilitators, entrepreneurs, systems thinkers, changemakers, analysts, students, researchers... maybe you!</p>
|
||||
<button type="button" className="button learnMoreCTA" onClick={() => {Metamaps.GlobalUI.openLightbox('about')}}>LEARN MORE</button>
|
||||
<a href="/explore/featured" data-router="true" className="exploreFeaturedCTA">EXPLORE FEATURED MAPS</a>
|
||||
<a href="/request" className="requestInviteCTA">REQUEST INVITE</a>
|
||||
<Link to="/explore/featured" data-router="true" className="exploreFeaturedCTA">EXPLORE FEATURED MAPS</Link>
|
||||
<Link to="/request" className="requestInviteCTA">REQUEST INVITE</Link>
|
||||
</div>
|
||||
<div className="clearfloat"></div>
|
||||
</div>
|
||||
|
|
246
src/routes/UserSettings.js
Normal file
246
src/routes/UserSettings.js
Normal file
|
@ -0,0 +1,246 @@
|
|||
import React, { Component } from 'react'
|
||||
import Loading from '../components/Loading'
|
||||
|
||||
class UserSettings extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
userImageMenuOpen: false,
|
||||
changePasswordOpen: false,
|
||||
changeName: false,
|
||||
loading: false,
|
||||
imagePreview: null,
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
newPasswordConfirmation: '',
|
||||
name: '',
|
||||
email: '',
|
||||
emailsAllowed: false,
|
||||
followTopicOnCreated: false,
|
||||
followTopicOnContributed: false,
|
||||
followMapOnCreated: false,
|
||||
followMapOnContributed: false,
|
||||
removeImage: '0' // can be '0' or '1', 0 means keep, 1 means remove
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
this.setState({
|
||||
name: this.props.currentUser.get('name'),
|
||||
email: this.props.currentUser.get('email'),
|
||||
image: this.props.currentUser.get('image'),
|
||||
emailsAllowed: this.props.currentUser.get('emails_allowed'),
|
||||
followTopicOnCreated: this.props.currentUser.get('follow_topic_on_created'),
|
||||
followTopicOnContributed: this.props.currentUser.get('follow_topic_on_contributed'),
|
||||
followMapOnCreated: this.props.currentUser.get('follow_map_on_created'),
|
||||
followMapOnContributed: this.props.currentUser.get('follow_map_on_contributed')
|
||||
})
|
||||
}
|
||||
|
||||
init = (serverData) => {
|
||||
Account.userIconUrl = serverData['user.png']
|
||||
}
|
||||
|
||||
initListeners = () => {
|
||||
/*
|
||||
$('#user_image').change(self.showImagePreview)
|
||||
*/
|
||||
}
|
||||
|
||||
toggleChangePicture = () => {
|
||||
this.setState({
|
||||
userImageMenuOpen: !this.state.userImageMenuOpen
|
||||
})
|
||||
}
|
||||
|
||||
openChangePicture = () => {
|
||||
this.setState({
|
||||
userImageMenuOpen: true
|
||||
})
|
||||
}
|
||||
|
||||
closeChangePicture = () => {
|
||||
this.setState({
|
||||
userImageMenuOpen: false
|
||||
})
|
||||
}
|
||||
|
||||
showLoading = () => {
|
||||
this.setState({ loading: true })
|
||||
}
|
||||
|
||||
showImagePreview = () => {
|
||||
var file = $('#user_image')[0].files[0]
|
||||
|
||||
var reader = new window.FileReader()
|
||||
|
||||
reader.onload = function(e) {
|
||||
var $canvas = $('<canvas>').attr({
|
||||
width: 84,
|
||||
height: 84
|
||||
})
|
||||
var context = $canvas[0].getContext('2d')
|
||||
var imageObj = new window.Image()
|
||||
|
||||
imageObj.onload = function() {
|
||||
$('.userImageDiv canvas').remove()
|
||||
$('.userImageDiv img').hide()
|
||||
|
||||
var imgWidth = imageObj.width
|
||||
var imgHeight = imageObj.height
|
||||
|
||||
var dimensionToMatch = imgWidth > imgHeight ? imgHeight : imgWidth
|
||||
// draw cropped image
|
||||
var nonZero = Math.abs(imgHeight - imgWidth) / 2
|
||||
var sourceX = dimensionToMatch === imgWidth ? 0 : nonZero
|
||||
var sourceY = dimensionToMatch === imgHeight ? 0 : nonZero
|
||||
var sourceWidth = dimensionToMatch
|
||||
var sourceHeight = dimensionToMatch
|
||||
var destX = 0
|
||||
var destY = 0
|
||||
var destWidth = 84
|
||||
var destHeight = 84
|
||||
|
||||
context.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight)
|
||||
$('.userImageDiv').prepend($canvas)
|
||||
}
|
||||
imageObj.src = reader.result
|
||||
}
|
||||
|
||||
if (file) {
|
||||
reader.readAsDataURL(file)
|
||||
$('.userImageMenu').hide()
|
||||
$('#remove_image').val('0')
|
||||
}
|
||||
}
|
||||
|
||||
removePicture = () => {
|
||||
/*
|
||||
$('.userImageDiv canvas').remove()
|
||||
$('.userImageDiv img').attr('src', '/images/user.png').show()
|
||||
$('.userImageMenu').hide()
|
||||
|
||||
var input = $('#user_image')
|
||||
input.replaceWith(input.val('').clone(true))
|
||||
$('#remove_image').val('1')
|
||||
*/
|
||||
this.setState({ removeImage: '1' })
|
||||
}
|
||||
|
||||
changeName = () => {
|
||||
this.setState({ changeName: true })
|
||||
}
|
||||
|
||||
showPass = () => {
|
||||
this.setState({ changePasswordOpen: true })
|
||||
}
|
||||
|
||||
hidePass = () => {
|
||||
this.setState({
|
||||
changePasswordOpen: false,
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
newPasswordConfirmation: ''
|
||||
})
|
||||
}
|
||||
|
||||
updateForKey = (key) => {
|
||||
return (event) => {
|
||||
const newState = {}
|
||||
if (event.target.type === 'checkbox') {
|
||||
newState[key] = !this.state[key]
|
||||
} else {
|
||||
newState[key] = event.target.value
|
||||
}
|
||||
this.setState(newState)
|
||||
}
|
||||
}
|
||||
|
||||
render = () => {
|
||||
const id = this.props.currentUser.get('id')
|
||||
const name = this.props.currentUser.get('name')
|
||||
return (
|
||||
<div id="yield">
|
||||
<form className="edit_user centerGreyForm" id={`edit_user_${id}`} encType="multipart/form-data" action={`/users/${id}`} acceptCharset="UTF-8" method="patch">
|
||||
<input name="utf8" type="hidden" value="✓" />
|
||||
<h3>Edit Settings</h3>
|
||||
<div className="userImage">
|
||||
<div className="userImageDiv" onClick={this.toggleChangePicture}>
|
||||
<img src={this.state.image} alt="11835c3" width="84" height="84" />
|
||||
<div className="editPhoto"></div>
|
||||
</div>
|
||||
{this.state.userImageMenuOpen && <div className="userImageMenu">
|
||||
<div className="userMenuArrow"></div>
|
||||
<ul>
|
||||
<li className="upload">
|
||||
Upload Photo
|
||||
<input type="hidden" name="remove_image" id="remove_image" value={this.state.removeImage} />
|
||||
<input type="file" name="user[image]" id="user_image" />
|
||||
<label htmlFor="user_image">Image</label>
|
||||
</li>
|
||||
<li className="remove" onClick={this.removePicture}>Remove</li>
|
||||
<li className="cancel" onClick={this.closeChangePicture}>Cancel</li>
|
||||
</ul>
|
||||
</div>}
|
||||
</div>
|
||||
{!this.state.changeName && <div className="accountName" onClick={this.changeName}>
|
||||
<div className="nameEdit">{ name }</div>
|
||||
</div>}
|
||||
{this.state.changeName && <div className="changeName">
|
||||
<label className="firstFieldText" htmlFor="user_name">Name:</label>
|
||||
<input type="text" name="user[name]" id="user_name" value={this.state.name} onChange={this.updateForKey('name')} />
|
||||
</div>}
|
||||
<div>
|
||||
<label className="firstFieldText" htmlFor="user_email">Email:</label>
|
||||
<input type="email" value={this.state.email} onChange={this.updateForKey('email')} name="user[email]" id="user_email" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="firstFieldText" htmlFor="user_emails_allowed">
|
||||
<input className="inline" type="checkbox" value={this.state.emailsAllowed ? '1' : '0'} checked={this.state.emailsAllowed} onChange={this.updateForKey('emailsAllowed')} name="user[emails_allowed]" id="user_emails_allowed" />
|
||||
Send Metamaps notifications to my email.
|
||||
</label>
|
||||
<label className="firstFieldText" htmlFor="settings_follow_topic_on_created">
|
||||
<input className="inline" type="checkbox" value={this.state.followTopicOnCreated ? '1' : '0'} checked={this.state.followTopicOnCreated} onChange={this.updateForKey('followTopicOnCreated')} name="settings[follow_topic_on_created]" id="settings_follow_topic_on_created" />
|
||||
Auto-follow topics you create.
|
||||
</label>
|
||||
<label className="firstFieldText" htmlFor="settings_follow_topic_on_contributed">
|
||||
<input className="inline" type="checkbox" value={this.state.followTopicOnContributed ? '1' : '0'} checked={this.state.followTopicOnContributed} onChange={this.updateForKey('followTopicOnContributed')} name="settings[follow_topic_on_contributed]" id="settings_follow_topic_on_contributed" />
|
||||
Auto-follow topics you edit.
|
||||
</label>
|
||||
<label className="firstFieldText" htmlFor="settings_follow_map_on_created">
|
||||
<input className="inline" type="checkbox" value={this.state.followMapOnCreated ? '1' : '0'} checked={this.state.followMapOnCreated} onChange={this.updateForKey('followMapOnCreated')} name="settings[follow_map_on_created]" id="settings_follow_map_on_created" />
|
||||
Auto-follow maps you create.
|
||||
</label>
|
||||
<label className="firstFieldText" htmlFor="settings_follow_map_on_contributed">
|
||||
<input className="inline" type="checkbox" value={this.state.followMapOnContributed ? '1' : '0'} checked={this.state.followMapOnContributed} onChange={this.updateForKey('followMapOnContributed')} name="settings[follow_map_on_contributed]" id="settings_follow_map_on_contributed" />
|
||||
Auto-follow maps you edit.
|
||||
</label>
|
||||
</div>
|
||||
{!this.state.changePasswordOpen && <div className="changePass" onClick={this.showPass}>Change Password</div>}
|
||||
{this.state.changePasswordOpen && <div>
|
||||
<div>
|
||||
<label className="firstFieldText" htmlFor="user_current_password">Current Password:</label>
|
||||
<input type="password" name="current_password" id="current_password" value={this.state.currentPassword} onChange={this.updateForKey('currentPassword')} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="firstFieldText" htmlFor="user_password">New Password:</label>
|
||||
<input autoComplete="off" type="password" name="user[password]" id="user_password" value={this.state.newPassword} onChange={this.updateForKey('newPassword')} />
|
||||
</div>
|
||||
<div>
|
||||
<label className="firstFieldText" htmlFor="user_password_confirmation">Confirm New Password:</label>
|
||||
<input autoComplete="off" type="password" name="user[password_confirmation]" id="user_password_confirmation" value={this.state.newPasswordConfirmation} onChange={this.updateForKey('newPasswordConfirmation')} />
|
||||
</div>
|
||||
<div className="noChangePass" onClick={this.hidePass}>Oops, don't change password</div>
|
||||
</div>}
|
||||
<div id="accountPageLoading">
|
||||
{this.state.loading && <Loading />}
|
||||
</div>
|
||||
<input type="submit" name="commit" value="Update" className="update" onClick={this.showLoading} data-disable-with="Update" />
|
||||
<div className="clearfloat"></div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default UserSettings
|
|
@ -12,6 +12,7 @@ import RequestAccess from './RequestAccess'
|
|||
import RequestInvite from './RequestInvite'
|
||||
import Login from './Login'
|
||||
import Join from './Join'
|
||||
import UserSettings from './UserSettings'
|
||||
|
||||
function nullComponent(props) {
|
||||
return null
|
||||
|
@ -42,7 +43,7 @@ export default function makeRoutes (currentUser) {
|
|||
<Route path=":id" component={Notifications} />
|
||||
</Route>
|
||||
<Route path="users">
|
||||
<Route path=":id/edit" component={nullComponent} />
|
||||
<Route path=":id/edit" component={UserSettings} />
|
||||
<Route path="password" component={nullComponent} />
|
||||
<Route path="password/new" component={nullComponent} />
|
||||
<Route path="password/edit" component={nullComponent} />
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
import React, { Component } from react
|
||||
|
||||
class MyComponent extends Component {
|
||||
render = () => {
|
||||
return (
|
||||
<div id="yield">
|
||||
{ form_for @user, url: user_url, :html =>{ :multipart => true, :className => "edit_user centerGreyForm"} do |form| }
|
||||
<h3>Edit Settings</h3>
|
||||
<div className="userImage">
|
||||
<div className="userImageDiv" onclick="Metamaps.Account.toggleChangePicture()">
|
||||
{ image_tag @user.image.url(:ninetysix), :size => "84x84" }
|
||||
<div className="editPhoto"></div>
|
||||
</div>
|
||||
<div className="userImageMenu">
|
||||
<div className="userMenuArrow"></div>
|
||||
<ul>
|
||||
<li className="upload">
|
||||
Upload Photo
|
||||
{ hidden_field_tag "remove_image", "0" }
|
||||
{ form.file_field :image }
|
||||
{ form.label :image }
|
||||
</li>
|
||||
<li className="remove" onclick="Metamaps.Account.removePicture()">Remove</li>
|
||||
<li className="cancel" onclick="Metamaps.Account.closeChangePicture()">Cancel</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="accountName" onclick="Metamaps.Account.changeName()">
|
||||
<div className="nameEdit">{ @user.name }</div>
|
||||
</div>
|
||||
<div className="changeName">
|
||||
{ form.label :name, "Name:", className: 'firstFieldText' }
|
||||
{ form.text_field :name }
|
||||
</div>
|
||||
<div>
|
||||
{ form.label :email, "Email:", className: 'firstFieldText' }
|
||||
{ form.email_field :email }
|
||||
</div>
|
||||
<div>
|
||||
{ form.label :emails_allowed, className: 'firstFieldText' do }
|
||||
{ form.check_box :emails_allowed, className: 'inline' }
|
||||
Send Metamaps notifications to my email.
|
||||
{ end }
|
||||
{ fields_for :settings, @user.settings do |settings| }
|
||||
{ settings.label :follow_topic_on_created, className: 'firstFieldText' do }
|
||||
{ settings.check_box :follow_topic_on_created, className: 'inline' }
|
||||
Auto-follow topics you create.
|
||||
{ end }
|
||||
{ settings.label :follow_topic_on_contributed, className: 'firstFieldText' do }
|
||||
{ settings.check_box :follow_topic_on_contributed, className: 'inline' }
|
||||
Auto-follow topics you edit.
|
||||
{ end }
|
||||
{ settings.label :follow_map_on_created, className: 'firstFieldText' do }
|
||||
{ settings.check_box :follow_map_on_created, className: 'inline' }
|
||||
Auto-follow maps you create.
|
||||
{ end }
|
||||
{ settings.label :follow_map_on_contributed, className: 'firstFieldText' do }
|
||||
{ settings.check_box :follow_map_on_contributed, className: 'inline' }
|
||||
Auto-follow maps you edit.
|
||||
{ end }
|
||||
{ end }
|
||||
</div>
|
||||
<div className="changePass" onclick="Metamaps.Account.showPass()">Change Password</div>
|
||||
<div className="toHide">
|
||||
<div>
|
||||
{ form.label :current_password, "Current Password:", :className => "firstFieldText" }
|
||||
{ password_field_tag :current_password, params[:current_password] }
|
||||
</div>
|
||||
<div>
|
||||
{ form.label :password, "New Password:", :className => "firstFieldText" }
|
||||
{ form.password_field :password, :autocomplete => :off}
|
||||
</div>
|
||||
<div>
|
||||
{ form.label :password_confirmation, "Confirm New Password:", :className => "firstFieldText" }
|
||||
{ form.password_field :password_confirmation, :autocomplete => :off}
|
||||
</div>
|
||||
<div className="noChangePass" onclick="Metamaps.Account.hidePass()">Oops, don't change password</div>
|
||||
</div>
|
||||
<div id="accountPageLoading"></div>
|
||||
{ form.submit "Update", className: "update", onclick: "Metamaps.Account.showLoading()" }
|
||||
<div className="clearfloat"></div>
|
||||
{ end }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default MyComponent
|
Loading…
Reference in a new issue