diff options
author | akshitkrnagpal <akshitkrnagpal@gmail.com> | 2018-06-09 21:40:00 +0530 |
---|---|---|
committer | Saúl Ibarra Corretgé <s@saghul.net> | 2018-06-21 07:55:32 +0200 |
commit | 8f79e886fc56760694b3e1f470a8e61993b1ffba (patch) | |
tree | cb966209f7664e17cc92b56206eb2be42f412abf /app/features/settings | |
parent | 93d8268a68db286cfec44f4020117c7087863402 (diff) |
Added settings drawer with persistent settings
Diffstat (limited to 'app/features/settings')
-rw-r--r-- | app/features/settings/actionTypes.js | 30 | ||||
-rw-r--r-- | app/features/settings/actions.js | 51 | ||||
-rw-r--r-- | app/features/settings/components/SettingsAction.js | 60 | ||||
-rw-r--r-- | app/features/settings/components/SettingsDrawer.js | 193 | ||||
-rw-r--r-- | app/features/settings/components/index.js | 2 | ||||
-rw-r--r-- | app/features/settings/index.js | 7 | ||||
-rw-r--r-- | app/features/settings/middleware.js | 24 | ||||
-rw-r--r-- | app/features/settings/reducer.js | 53 | ||||
-rw-r--r-- | app/features/settings/styled/AvatarContainer.js | 9 | ||||
-rw-r--r-- | app/features/settings/styled/ProfileContainer.js | 8 | ||||
-rw-r--r-- | app/features/settings/styled/index.js | 2 |
11 files changed, 439 insertions, 0 deletions
diff --git a/app/features/settings/actionTypes.js b/app/features/settings/actionTypes.js new file mode 100644 index 0000000..977ef4d --- /dev/null +++ b/app/features/settings/actionTypes.js @@ -0,0 +1,30 @@ +/** + * The type of (redux) action that sets the Avatar URL. + * + * { + * type: SET_AVATAR_URL, + * avatarURL: string + * } + */ +export const SET_AVATAR_URL = Symbol('SET_AVATAR_URL'); + +/** + * The type of (redux) action that sets the email of the user. + * + * { + * type: SET_EMAIL, + * email: string + * } + */ +export const SET_EMAIL = Symbol('SET_EMAIL'); + +/** + * The type of (redux) action that sets the name of the user. + * + * { + * type: SET_NAME, + * name: string + * } + */ +export const SET_NAME = Symbol('SET_NAME'); + diff --git a/app/features/settings/actions.js b/app/features/settings/actions.js new file mode 100644 index 0000000..dd5a383 --- /dev/null +++ b/app/features/settings/actions.js @@ -0,0 +1,51 @@ +// @flow + +import { SET_AVATAR_URL, SET_EMAIL, SET_NAME } from './actionTypes'; + +/** + * Set Avatar URL. + * + * @param {string} avatarURL - Avatar URL. + * @returns {{ + * type: SET_AVATAR_URL, + * avatarURL: string + * }} + */ +export function setAvatarURL(avatarURL: string) { + return { + type: SET_AVATAR_URL, + avatarURL + }; +} + +/** + * Set the email of the user. + * + * @param {string} email - Email of the user. + * @returns {{ + * type: SET_EMAIL, + * email: string + * }} + */ +export function setEmail(email: string) { + return { + type: SET_EMAIL, + email + }; +} + +/** + * Set the name of the user. + * + * @param {string} name - Name of the user. + * @returns {{ + * type: SET_NAME, + * name: string + * }} + */ +export function setName(name: string) { + return { + type: SET_NAME, + name + }; +} diff --git a/app/features/settings/components/SettingsAction.js b/app/features/settings/components/SettingsAction.js new file mode 100644 index 0000000..080e3c6 --- /dev/null +++ b/app/features/settings/components/SettingsAction.js @@ -0,0 +1,60 @@ +// @flow + +import SettingsIcon from '@atlaskit/icon/glyph/settings'; + +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import type { Dispatch } from 'redux'; + +import { openDrawer } from '../../navbar'; + +import SettingsDrawer from './SettingsDrawer'; + +type Props = { + + /** + * Redux dispatch. + */ + dispatch: Dispatch<*>; +}; + +/** + * Setttings Action for Navigation Bar. + */ +class SettingsAction extends Component<Props, *> { + /** + * Initializes a new {@code SettingsAction} instance. + * + * @inheritdoc + */ + constructor() { + super(); + + this._onIconClick = this._onIconClick.bind(this); + } + + /** + * Render function of component. + * + * @returns {ReactElement} + */ + render() { + return ( + <SettingsIcon + onClick = { this._onIconClick } /> + ); + } + + _onIconClick: (*) => void; + + /** + * Open Settings drawer when SettingsAction is clicked. + * + * @returns {void} + */ + _onIconClick() { + this.props.dispatch(openDrawer(SettingsDrawer)); + } +} + +export default connect()(SettingsAction); diff --git a/app/features/settings/components/SettingsDrawer.js b/app/features/settings/components/SettingsDrawer.js new file mode 100644 index 0000000..7869cd2 --- /dev/null +++ b/app/features/settings/components/SettingsDrawer.js @@ -0,0 +1,193 @@ +// @flow + +import Avatar from '@atlaskit/avatar'; +import FieldText from '@atlaskit/field-text'; +import ArrowLeft from '@atlaskit/icon/glyph/arrow-left'; +import { AkCustomDrawer } from '@atlaskit/navigation'; + +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import type { Dispatch } from 'redux'; + +import { closeDrawer, DrawerContainer, Logo } from '../../navbar'; +import { AvatarContainer, ProfileContainer } from '../styled'; +import { setEmail, setName } from '../actions'; + +type Props = { + + /** + * Redux dispatch. + */ + dispatch: Dispatch<*>; + + /** + * Is the drawer open or not. + */ + isOpen: boolean; + + /** + * Avatar URL. + */ + _avatarURL: string; + + /** + * Email of the user. + */ + _email: string; + + /** + * Name of the user. + */ + _name: string; +}; + +/** + * Drawer that open when SettingsAction is clicked. + */ +class SettingsDrawer extends Component<Props, *> { + /** + * Initializes a new {@code SettingsDrawer} instance. + * + * @inheritdoc + */ + constructor(props) { + super(props); + + this._onBackButton = this._onBackButton.bind(this); + this._onEmailBlur = this._onEmailBlur.bind(this); + this._onEmailFormSubmit = this._onEmailFormSubmit.bind(this); + this._onNameBlur = this._onNameBlur.bind(this); + this._onNameFormSubmit = this._onNameFormSubmit.bind(this); + } + + /** + * Render function of component. + * + * @returns {ReactElement} + */ + render() { + return ( + <AkCustomDrawer + backIcon = { <ArrowLeft label = 'Back' /> } + isOpen = { this.props.isOpen } + onBackButton = { this._onBackButton } + primaryIcon = { <Logo /> } > + <DrawerContainer> + <ProfileContainer> + <AvatarContainer> + <Avatar + size = 'xlarge' + src = { this.props._avatarURL } /> + </AvatarContainer> + <form onSubmit = { this._onNameFormSubmit }> + <FieldText + label = 'Name' + onBlur = { this._onNameBlur } + shouldFitContainer = { true } + type = 'text' + value = { this.props._name } /> + </form> + <form onSubmit = { this._onEmailFormSubmit }> + <FieldText + label = 'Email' + onBlur = { this._onEmailBlur } + shouldFitContainer = { true } + type = 'text' + value = { this.props._email } /> + </form> + </ProfileContainer> + </DrawerContainer> + </AkCustomDrawer> + ); + } + + + _onBackButton: (*) => void; + + /** + * Closes the drawer when back button is clicked. + * + * @returns {void} + */ + _onBackButton() { + this.props.dispatch(closeDrawer()); + } + + _onEmailBlur: (*) => void; + + /** + * Updates Avatar URL in (redux) state when email is updated. + * + * @param {SyntheticInputEvent<HTMLInputElement>} event - Event by which + * this function is called. + * @returns {void} + */ + _onEmailBlur(event: SyntheticInputEvent<HTMLInputElement>) { + this.props.dispatch(setEmail(event.currentTarget.value)); + } + + _onEmailFormSubmit: (*) => void; + + /** + * Prevents submission of the form and updates email. + * + * @param {SyntheticEvent<HTMLFormElement>} event - Event by which + * this function is called. + * @returns {void} + */ + _onEmailFormSubmit(event: SyntheticEvent<HTMLFormElement>) { + event.preventDefault(); + + // $FlowFixMe + this.props.dispatch(setEmail(event.currentTarget.elements[0].value)); + } + + _onNameBlur: (*) => void; + + /** + * Updates Avatar URL in (redux) state when name is updated. + * + * @param {SyntheticInputEvent<HTMLInputElement>} event - Event by which + * this function is called. + * @returns {void} + */ + _onNameBlur(event: SyntheticInputEvent<HTMLInputElement>) { + this.props.dispatch(setName(event.currentTarget.value)); + } + + _onNameFormSubmit: (*) => void; + + /** + * Prevents submission of the form and updates name. + * + * @param {SyntheticEvent<HTMLFormElement>} event - Event by which + * this function is called. + * @returns {void} + */ + _onNameFormSubmit(event: SyntheticEvent<HTMLFormElement>) { + event.preventDefault(); + + // $FlowFixMe + this.props.dispatch(setName(event.currentTarget.elements[0].value)); + } +} + +/** + * Maps (parts of) the redux state to the React props. + * + * @param {Object} state - The redux state. + * @returns {{ + * _avatarURL: string, + * _email: string, + * _name: string + * }} + */ +function _mapStateToProps(state: Object) { + return { + _avatarURL: state.settings.avatarURL, + _email: state.settings.email, + _name: state.settings.name + }; +} + +export default connect(_mapStateToProps)(SettingsDrawer); diff --git a/app/features/settings/components/index.js b/app/features/settings/components/index.js new file mode 100644 index 0000000..63dd49d --- /dev/null +++ b/app/features/settings/components/index.js @@ -0,0 +1,2 @@ +export { default as SettingsAction } from './SettingsAction'; +export { default as SettingsDrawer } from './SettingsDrawer'; diff --git a/app/features/settings/index.js b/app/features/settings/index.js new file mode 100644 index 0000000..17b8002 --- /dev/null +++ b/app/features/settings/index.js @@ -0,0 +1,7 @@ +export * from './actions'; +export * from './actionTypes'; +export * from './components'; +export * from './styled'; + +export { default as middleware } from './middleware'; +export { default as reducer } from './reducer'; diff --git a/app/features/settings/middleware.js b/app/features/settings/middleware.js new file mode 100644 index 0000000..0d6e489 --- /dev/null +++ b/app/features/settings/middleware.js @@ -0,0 +1,24 @@ +// @flow + +import { getAvatarURL } from '../utils'; +import { SET_EMAIL, SET_NAME } from './actionTypes'; +import { setAvatarURL } from './actions'; + +export default (store: Object) => (next: Function) => (action: Object) => { + const result = next(action); + const state = store.getState(); + + switch (action.type) { + case SET_EMAIL: + case SET_NAME: { + const avatarURL = getAvatarURL({ + email: state.settings.email, + id: state.settings.name + }); + + store.dispatch(setAvatarURL(avatarURL)); + } + } + + return result; +}; diff --git a/app/features/settings/reducer.js b/app/features/settings/reducer.js new file mode 100644 index 0000000..02d42d5 --- /dev/null +++ b/app/features/settings/reducer.js @@ -0,0 +1,53 @@ +// @flow + +import os from 'os'; + +import { getAvatarURL } from '../utils'; + +import { SET_AVATAR_URL, SET_EMAIL, SET_NAME } from './actionTypes'; + +type State = { + avatarURL: string, + email: string, + name: string +}; + +const username = os.userInfo().username; + +const DEFAULT_STATE = { + avatarURL: getAvatarURL({ id: username }), + email: '', + name: username +}; + +/** + * Reduces redux actions for features/settings. + * + * @param {State} state - Current reduced redux state. + * @param {Object} action - Action which was dispatched. + * @returns {State} - Updated reduced redux state. + */ +export default (state: State = DEFAULT_STATE, action: Object) => { + switch (action.type) { + case SET_AVATAR_URL: + return { + ...state, + avatarURL: action.avatarURL + }; + + case SET_EMAIL: + return { + ...state, + email: action.email + }; + + case SET_NAME: + return { + ...state, + name: action.name + }; + + default: + return state; + } +}; diff --git a/app/features/settings/styled/AvatarContainer.js b/app/features/settings/styled/AvatarContainer.js new file mode 100644 index 0000000..65e617e --- /dev/null +++ b/app/features/settings/styled/AvatarContainer.js @@ -0,0 +1,9 @@ +// @flow + +import styled from 'styled-components'; + +export default styled.div` + align-items: center; + display: flex; + flex-direction: column; +`; diff --git a/app/features/settings/styled/ProfileContainer.js b/app/features/settings/styled/ProfileContainer.js new file mode 100644 index 0000000..8daae3a --- /dev/null +++ b/app/features/settings/styled/ProfileContainer.js @@ -0,0 +1,8 @@ +// @flow + +import styled from 'styled-components'; + +export default styled.div` + margin: 0 auto; + width: 70%; +`; diff --git a/app/features/settings/styled/index.js b/app/features/settings/styled/index.js new file mode 100644 index 0000000..ed00704 --- /dev/null +++ b/app/features/settings/styled/index.js @@ -0,0 +1,2 @@ +export { default as AvatarContainer } from './AvatarContainer'; +export { default as ProfileContainer } from './ProfileContainer'; |