From 88ee94d4b6666c81f30b72d1e595c562ee64e038 Mon Sep 17 00:00:00 2001 From: Rokt33r Date: Sun, 18 Oct 2015 17:17:25 +0900 Subject: [PATCH] add Preferences modal(30%done) & move some modules (actions, reducer, socket, store -> lib/~) --- browser/main/HomePage.js | 5 +- browser/main/HomePage/ArticleDetail.js | 6 +- browser/main/HomePage/ArticleList.js | 2 +- browser/main/HomePage/ArticleNavigator.js | 18 +- browser/main/index.html | 8 +- browser/main/index.js | 4 +- browser/styles/main/HomeContainer/index.styl | 1 + .../main/HomeContainer/lib/Preferences.styl | 126 ++++++ browser/styles/vars.styl | 11 +- {browser/main => lib}/actions.js | 0 lib/api.js | 20 + lib/components/modal/CreateNewFolder.js | 16 +- lib/components/modal/Preferences.js | 427 ++++++++++++++++++ {browser/main => lib}/reducer.js | 0 {browser/main => lib}/socket.js | 4 +- {browser/main => lib}/store.js | 0 16 files changed, 618 insertions(+), 30 deletions(-) create mode 100644 browser/styles/main/HomeContainer/lib/Preferences.styl rename {browser/main => lib}/actions.js (100%) create mode 100644 lib/components/modal/Preferences.js rename {browser/main => lib}/reducer.js (100%) rename {browser/main => lib}/socket.js (93%) rename {browser/main => lib}/store.js (100%) diff --git a/browser/main/HomePage.js b/browser/main/HomePage.js index 0cca9d94..f9abf00d 100644 --- a/browser/main/HomePage.js +++ b/browser/main/HomePage.js @@ -1,6 +1,6 @@ import React, { PropTypes} from 'react' import { connect } from 'react-redux' -import { CREATE_MODE, IDLE_MODE, switchUser } from './actions' +import { CREATE_MODE, IDLE_MODE, switchUser, NEW, refreshArticles } from 'boost/actions' import UserNavigator from './HomePage/UserNavigator' import ArticleNavigator from './HomePage/ArticleNavigator' import ArticleTopBar from './HomePage/ArticleTopBar' @@ -8,10 +8,9 @@ import ArticleList from './HomePage/ArticleList' import ArticleDetail from './HomePage/ArticleDetail' import { findWhere, findIndex, pick } from 'lodash' import keygen from 'boost/keygen' -import { NEW, refreshArticles } from './actions' import api from 'boost/api' import auth from 'boost/auth' -import './socket' +import 'boost/socket' class HomePage extends React.Component { componentDidMount () { diff --git a/browser/main/HomePage/ArticleDetail.js b/browser/main/HomePage/ArticleDetail.js index 1ad8dc4f..2a8e509f 100644 --- a/browser/main/HomePage/ArticleDetail.js +++ b/browser/main/HomePage/ArticleDetail.js @@ -4,7 +4,7 @@ import { findWhere, uniq } from 'lodash' import ModeIcon from 'boost/components/ModeIcon' import MarkdownPreview from 'boost/components/MarkdownPreview' import CodeEditor from 'boost/components/CodeEditor' -import { UNSYNCED, IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, updateArticle, destroyArticle } from '../actions' +import { UNSYNCED, IDLE_MODE, CREATE_MODE, EDIT_MODE, switchMode, switchArticle, updateArticle, destroyArticle } from 'boost/actions' import aceModes from 'boost/ace-modes' import Select from 'react-select' import linkState from 'boost/linkState' @@ -20,7 +20,7 @@ var modeOptions = aceModes.map(function (mode) { function makeInstantArticle (article) { let instantArticle = Object.assign({}, article) - instantArticle.Tags = (typeof instantArticle.Tags === 'array') ? instantArticle.Tags.map(tag => tag.name) : [] + instantArticle.Tags = Array.isArray(instantArticle.Tags) ? instantArticle.Tags.map(tag => tag.name) : [] return instantArticle } @@ -107,7 +107,7 @@ export default class ArticleDetail extends React.Component { Sure diff --git a/browser/main/HomePage/ArticleList.js b/browser/main/HomePage/ArticleList.js index 2f6dfd37..a9657a03 100644 --- a/browser/main/HomePage/ArticleList.js +++ b/browser/main/HomePage/ArticleList.js @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react' import ProfileImage from 'boost/components/ProfileImage' import ModeIcon from 'boost/components/ModeIcon' import moment from 'moment' -import { switchArticle, NEW } from '../actions' +import { switchArticle, NEW } from 'boost/actions' import FolderMark from 'boost/components/FolderMark' export default class ArticleList extends React.Component { diff --git a/browser/main/HomePage/ArticleNavigator.js b/browser/main/HomePage/ArticleNavigator.js index 0be5c620..3150449b 100644 --- a/browser/main/HomePage/ArticleNavigator.js +++ b/browser/main/HomePage/ArticleNavigator.js @@ -1,12 +1,21 @@ import React, { PropTypes } from 'react' import ProfileImage from 'boost/components/ProfileImage' import { findWhere } from 'lodash' -import { switchMode, CREATE_MODE } from '../actions' +import { switchMode, CREATE_MODE } from 'boost/actions' import { openModal } from 'boost/modal' -import CreateNewFolder from 'boost/components/modal/CreateNewFolder' import FolderMark from 'boost/components/FolderMark' +import Preferences from 'boost/components/modal/Preferences' +import CreateNewFolder from 'boost/components/modal/CreateNewFolder' export default class ArticleNavigator extends React.Component { + componentDidMount () { + this.handlePreferencesButtonClick() + } + + handlePreferencesButtonClick (e) { + openModal(Preferences) + } + handleNewPostButtonClick (e) { let { dispatch } = this.props @@ -27,7 +36,7 @@ export default class ArticleNavigator extends React.Component { let folders = activeUser.Folders.map((folder, index) => { return ( + {folder.name} {folder.public ? : null} ) }) @@ -47,7 +56,7 @@ export default class ArticleNavigator extends React.Component {
{activeUser.profileName}
{activeUser.name}
- +
@@ -89,3 +98,4 @@ ArticleNavigator.propTypes = { }), dispatch: PropTypes.func } + diff --git a/browser/main/index.html b/browser/main/index.html index 619e5ed4..81f0282b 100644 --- a/browser/main/index.html +++ b/browser/main/index.html @@ -53,12 +53,16 @@
- diff --git a/browser/main/index.js b/browser/main/index.js index 162bce74..84659bb2 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -1,6 +1,6 @@ import React from 'react' import { Provider } from 'react-redux' -import { updateUser } from './actions' +import { updateUser } from 'boost/actions' import { fetchCurrentUser } from 'boost/api' import { Router, Route, IndexRoute } from 'react-router' import MainPage from './MainPage' @@ -8,7 +8,7 @@ import LoginPage from './LoginPage' import SignupPage from './SignupPage' import HomePage from './HomePage' import auth from 'boost/auth' -import store, { devToolElement } from './store' +import store, { devToolElement } from 'boost/store' require('../styles/main/index.styl') function onlyUser (state, replaceState) { diff --git a/browser/styles/main/HomeContainer/index.styl b/browser/styles/main/HomeContainer/index.styl index 4009c6ed..d0551d0c 100644 --- a/browser/styles/main/HomeContainer/index.styl +++ b/browser/styles/main/HomeContainer/index.styl @@ -7,3 +7,4 @@ @require './lib/modal' @require './lib/CreateNewTeam' @require './lib/CreateNewFolder' +@require './lib/Preferences' diff --git a/browser/styles/main/HomeContainer/lib/Preferences.styl b/browser/styles/main/HomeContainer/lib/Preferences.styl new file mode 100644 index 00000000..95678456 --- /dev/null +++ b/browser/styles/main/HomeContainer/lib/Preferences.styl @@ -0,0 +1,126 @@ +menuColor = #808080 +menuBgColor = #E6E6E6 +closeBtnBgColor = #1790C6 +iptFocusBorderColor = #369DCD + +.Preferences.modal + padding 0 + border-radius 5px + overflow hidden + width 720px + height 450px + &>.header + absolute top left right + height 50px + border-bottom 1px solid borderColor + background-color menuBgColor + &>.title + font-size 22px + font-weight bold + float left + padding-left 30px + line-height 50px + &>.closeBtn + float right + font-size 14px + background-color closeBtnBgColor + color white + padding 0 15px + height 33px + margin-top 9px + margin-right 15px + border none + border-radius 5px + &:hover + background-color lighten(closeBtnBgColor, 10%) + &>.nav + absolute left bottom + top 50px + width 180px + background-color menuBgColor + border-right 1px solid borderColor + &>button + width 100% + height 44px + font-size 18px + color menuColor + border none + background-color transparent + transition 0.1s + text-align left + padding-left 15px + &:hover + background-color darken(menuBgColor, 10%) + &.active, &:active + background-color brandColor + color white + &>.content + absolute right bottom + top 50px + left 180px + &>.section + padding 10px + border-bottom 1px solid borderColor + overflow-y auto + &:nth-last-child(1) + border-bottom none + &>.sectionTitle + font-size 18px + margin 10px 0 5px + color brandColor + &>.sectionInput + height 33px + margin-bottom 5px + clearfix() + label + width 180px + padding-left 15px + float left + line-height 33px + input + width 300px + float left + height 33px + border-radius 5px + border 1px solid borderColor + padding 0 10px + font-size 14px + outline none + &:focus + border-color iptFocusBorderColor + &>.sectionConfirm + clearfix() + padding 5px 15px + button + float right + background-color brandColor + color white + border none + border-radius 5px + height 33px + padding 0 15px + font-size 14px + &:hover + background-color lighten(brandColor, 10%) + .alert + float right + height 33px + padding 0 15px + border-radius 5px + margin-right 5px + line-height 33px + font-size 14px + &.info + background-color infoBackgroundColor + color infoTextColor + &.error + background-color errorBackgroundColor + color errorTextColor + &.success + background-color successBackgroundColor + color successTextColor + + .description + marked() + + diff --git a/browser/styles/vars.styl b/browser/styles/vars.styl index 0e6967ff..c24dca04 100644 --- a/browser/styles/vars.styl +++ b/browser/styles/vars.styl @@ -1,4 +1,4 @@ -borderColor = #D0D0D0 +borderColor = #D0D0D0 // using highlightenBorderColor = darken(borderColor, 20%) invBorderColor = #404849 brandBorderColor = #3FB399 @@ -7,12 +7,12 @@ buttonBorderColor = #4C4C4C lightButtonColor = #898989 -hoverBackgroundColor= transparentify(#444, 4%) +hoverBackgroundColor= transparentify(#444, 4%) // using -inactiveTextColor = #888 -textColor = #4D4D4D +inactiveTextColor = #888 // using +textColor = #4D4D4D // using backgroundColor= white -fontSize= 14px +fontSize= 14px // using shadowColor= #C5C5C5 @@ -34,6 +34,7 @@ tableEvenBgColor = white facebookColor= #3b5998 githubBtn= #201F1F +// using successBackgroundColor= #E0F0D9 successTextColor= #3E753F errorBackgroundColor= #F2DEDE diff --git a/browser/main/actions.js b/lib/actions.js similarity index 100% rename from browser/main/actions.js rename to lib/actions.js diff --git a/lib/api.js b/lib/api.js index 6ca98e60..ae3a4e02 100644 --- a/lib/api.js +++ b/lib/api.js @@ -17,6 +17,24 @@ export function signup (input) { .send(input) } +export function updateUserInfo (input) { + return request + .put(API_URL + 'auth/user') + .set({ + Authorization: 'Bearer ' + auth.token() + }) + .send(input) +} + +export function updatePassword (input) { + return request + .post(API_URL + 'auth/password') + .set({ + Authorization: 'Bearer ' + auth.token() + }) + .send(input) +} + export function fetchCurrentUser () { return request .get(API_URL + 'auth/user') @@ -113,6 +131,8 @@ export function sendEmail (input) { export default { login, signup, + updateUserInfo, + updatePassword, fetchCurrentUser, fetchArticles, createArticle, diff --git a/lib/components/modal/CreateNewFolder.js b/lib/components/modal/CreateNewFolder.js index cfdb98e1..64df2464 100644 --- a/lib/components/modal/CreateNewFolder.js +++ b/lib/components/modal/CreateNewFolder.js @@ -32,22 +32,22 @@ export default class CreateNewFolder extends React.Component { }) .catch(err => { console.log(err) - if (err.code === '') { - let alert = { + var alert + if (err.code === 'ECONNREFUSED') { + alert = { type: 'error', message: 'Can\'t connect to API server.' } - } - else if (err.status != null) { - let alert = { + } else if (err.status != null) { + alert = { type: 'error', message: err.response.body.message } - this.setState({alert: alert}) - } - else { + } else { throw err } + + this.setState({alert: alert}) }) } diff --git a/lib/components/modal/Preferences.js b/lib/components/modal/Preferences.js new file mode 100644 index 00000000..245436e2 --- /dev/null +++ b/lib/components/modal/Preferences.js @@ -0,0 +1,427 @@ +import React, { PropTypes, findDOMNode } from 'react' +import { connect, Provider } from 'react-redux' +import auth from 'boost/auth' +import linkState from 'boost/linkState' +import Select from 'react-select' +import api from 'boost/api' +import ProfileImage from 'boost/components/ProfileImage' +import store from 'boost/store' + +const PROFILE = 'PROFILE' +const PREFERENCES = 'PREFERENCES' +const HELP = 'HELP' +const TEAM = 'TEAM' +const MEMBER = 'MEMBER' +const FOLDER = 'FOLDER' + +function getUsers (input, cb) { + api.searchUser(input) + .then(function (res) { + let users = res.body + + cb(null, { + options: users.map(user => { + return { value: user.name, label: user.name } + }), + complete: false + }) + }) + .catch(function (err) { + console.error(err) + }) +} + +class Preferences extends React.Component { + constructor (props) { + super(props) + + this.state = { + currentTab: PROFILE, + profile: { + userInfo: { + profileName: props.currentUser.profileName, + email: props.currentUser.email, + alert: null + }, + password: { + currentPassword: '', + newPassword: '', + confirmation: '', + error: null + } + } + } + } + + handleNavButtonClick (tab) { + return e => { + this.setState({currentTab: tab}) + } + } + + render () { + let content = this.renderContent() + + let tabs = [ + {target: PROFILE, label: 'Profile'}, + {target: PREFERENCES, label: 'Preferences'}, + {target: HELP, label: 'Help & Feedback'}, + {target: TEAM, label: 'Team setting'}, + {target: MEMBER, label: 'Manage member'}, + {target: FOLDER, label: 'Manage folder'} + ] + + let navButtons = tabs.map(tab => ( + + )) + + return ( +
+
+
Setting
+ +
+ +
+ {navButtons} +
+ + {content} +
+ ) + } + + renderContent () { + switch (this.state.currentTab) { + case PREFERENCES: + return this.renderPreferences() + case HELP: + return this.renderHelp() + case TEAM: + return this.renderTeamSetting() + case MEMBER: + return this.renderMemberSetting() + case FOLDER: + return this.renderFolderSetting() + case PROFILE: + default: + return this.renderProfile() + } + } + + handleProfileSaveButtonClick (e) { + let profileState = this.state.profile + profileState.userInfo.alert = { + type: 'info', + message: 'Sending...' + } + this.setState({profile: profileState}, () => { + let input = { + profileName: profileState.userInfo.profileName, + email: profileState.userInfo.email + } + api.updateUserInfo(input) + .then(res => { + let profileState = this.state.profile + profileState.userInfo.alert = { + type: 'success', + message: 'Successfully done!' + } + this.setState({profile: profileState}) + }) + .catch(err => { + var message + if (err.status != null) { + message = err.response.body.message + } else if (err.code === 'ECONNREFUSED') { + message = 'Can\'t connect to API server.' + } else throw err + + let profileState = this.state.profile + profileState.userInfo.alert = { + type: 'error', + message: message + } + + this.setState({profile: profileState}) + }) + }) + } + + handlePasswordSaveButton (e) { + let profileState = this.state.profile + + if (profileState.password.newPassword !== profileState.password.confirmation) { + profileState.password.alert = { + type: 'error', + message: 'Confirmation doesn\'t match' + } + this.setState({profile: profileState}) + return + } + + profileState.password.alert = { + type: 'info', + message: 'Sending...' + } + + this.setState({profile: profileState}, () => { + let input = { + password: profileState.password.currentPassword, + newPassword: profileState.password.newPassword + } + api.updatePassword(input) + .then(res => { + let profileState = this.state.profile + profileState.password.alert = { + type: 'success', + message: 'Successfully done!' + } + profileState.password.currentPassword = '' + profileState.password.newPassword = '' + profileState.password.confirmation = '' + + this.setState({profile: profileState}) + }) + .catch(err => { + var message + if (err.status != null) { + message = err.response.body.message + } else if (err.code === 'ECONNREFUSED') { + message = 'Can\'t connect to API server.' + } else throw err + + let profileState = this.state.profile + profileState.password.alert = { + type: 'error', + message: message + } + profileState.password.currentPassword = '' + profileState.password.newPassword = '' + profileState.password.confirmation = '' + + this.setState({profile: profileState}, () => { + if (this.refs.currentPassword != null) findDOMNode(this.refs.currentPassword).focus() + }) + }) + }) + } + + renderProfile () { + let profileState = this.state.profile + return ( +
+
+
User Info
+
+ + +
+
+ + +
+
+ + + {this.state.profile.userInfo.alert != null + ? ( +
{profileState.userInfo.alert.message}
+ ) + : null} +
+
+ +
+
Password
+
+ + +
+
+ + +
+
+ + +
+
+ + + {this.state.profile.password.alert != null + ? ( +
{profileState.password.alert.message}
+ ) + : null} +
+
+
+ ) + } + + renderPreferences () { + return ( +
+
+
Hotkey
+
+ + +
+
+ +
+
+
    +
  • 0 to 9
  • +
  • A to Z
  • +
  • F1 to F24
  • +
  • Punctuations like ~, !, @, #, $, etc.
  • +
  • Plus
  • +
  • Space
  • +
  • Backspace
  • +
  • Delete
  • +
  • Insert
  • +
  • Return (or Enter as alias)
  • +
  • Up, Down, Left and Right
  • +
  • Home and End
  • +
  • PageUp and PageDown
  • +
  • Escape (or Esc for short)
  • +
  • VolumeUp, VolumeDown and VolumeMute
  • +
  • MediaNextTrack, MediaPreviousTrack, MediaStop and MediaPlayPause
  • +
+
+
+
+ ) + } + + renderHelp () { + return ( +
+ Comming soon +
+ ) + } + + renderTeamSetting () { + return ( +
+
+ +
's Team Setting
+
+
+
Team profile
+
+
Team Name
+ +
+
+ +
+
+ + {false + ? ( +
+
Delete this team
+ +
+ ) + : ( +
+
Are you sure to delete this team?
+ + +
+ )} +
+ ) + } + + renderMemberSetting () { + let membersEl = [].map(member => { + let isCurrentUser = this.state.currentUser.id === member.id + + return ( +
  • + +
    +
    {`${member.profileName} (${member.name})`}
    +
    {member.email}
    +
    + +
    + + +
    +
  • + ) + }) + + return ( +
    +
    + +
    's Team Setting
    +
    + +
    +