Port 65506bac3f
to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
closed-social-glitch-2
@ -0,0 +1,69 @@ | |||||
import api from 'flavours/glitch/util/api'; | |||||
export const ACCOUNT_NOTE_SUBMIT_REQUEST = 'ACCOUNT_NOTE_SUBMIT_REQUEST'; | |||||
export const ACCOUNT_NOTE_SUBMIT_SUCCESS = 'ACCOUNT_NOTE_SUBMIT_SUCCESS'; | |||||
export const ACCOUNT_NOTE_SUBMIT_FAIL = 'ACCOUNT_NOTE_SUBMIT_FAIL'; | |||||
export const ACCOUNT_NOTE_INIT_EDIT = 'ACCOUNT_NOTE_INIT_EDIT'; | |||||
export const ACCOUNT_NOTE_CANCEL = 'ACCOUNT_NOTE_CANCEL'; | |||||
export const ACCOUNT_NOTE_CHANGE_COMMENT = 'ACCOUNT_NOTE_CHANGE_COMMENT'; | |||||
export function submitAccountNote() { | |||||
return (dispatch, getState) => { | |||||
dispatch(submitAccountNoteRequest()); | |||||
const id = getState().getIn(['account_notes', 'edit', 'account_id']); | |||||
api(getState).post(`/api/v1/accounts/${id}/note`, { | |||||
comment: getState().getIn(['account_notes', 'edit', 'comment']), | |||||
}).then(response => { | |||||
dispatch(submitAccountNoteSuccess(response.data)); | |||||
}).catch(error => dispatch(submitAccountNoteFail(error))); | |||||
}; | |||||
}; | |||||
export function submitAccountNoteRequest() { | |||||
return { | |||||
type: ACCOUNT_NOTE_SUBMIT_REQUEST, | |||||
}; | |||||
}; | |||||
export function submitAccountNoteSuccess(relationship) { | |||||
return { | |||||
type: ACCOUNT_NOTE_SUBMIT_SUCCESS, | |||||
relationship, | |||||
}; | |||||
}; | |||||
export function submitAccountNoteFail(error) { | |||||
return { | |||||
type: ACCOUNT_NOTE_SUBMIT_FAIL, | |||||
error, | |||||
}; | |||||
}; | |||||
export function initEditAccountNote(account) { | |||||
return (dispatch, getState) => { | |||||
const comment = getState().getIn(['relationships', account.get('id'), 'note']); | |||||
dispatch({ | |||||
type: ACCOUNT_NOTE_INIT_EDIT, | |||||
account, | |||||
comment, | |||||
}); | |||||
}; | |||||
}; | |||||
export function cancelAccountNote() { | |||||
return { | |||||
type: ACCOUNT_NOTE_CANCEL, | |||||
}; | |||||
}; | |||||
export function changeAccountNoteComment(comment) { | |||||
return { | |||||
type: ACCOUNT_NOTE_CHANGE_COMMENT, | |||||
comment, | |||||
}; | |||||
}; |
@ -0,0 +1,103 @@ | |||||
import React from 'react'; | |||||
import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||
import PropTypes from 'prop-types'; | |||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | |||||
import ImmutablePureComponent from 'react-immutable-pure-component'; | |||||
import Icon from 'flavours/glitch/components/icon'; | |||||
import Textarea from 'react-textarea-autosize'; | |||||
const messages = defineMessages({ | |||||
placeholder: { id: 'account_note.placeholder', defaultMessage: 'No comment provided' }, | |||||
}); | |||||
export default @injectIntl | |||||
class Header extends ImmutablePureComponent { | |||||
static propTypes = { | |||||
account: ImmutablePropTypes.map.isRequired, | |||||
isEditing: PropTypes.bool, | |||||
isSubmitting: PropTypes.bool, | |||||
accountNote: PropTypes.string, | |||||
onEditAccountNote: PropTypes.func.isRequired, | |||||
onCancelAccountNote: PropTypes.func.isRequired, | |||||
onSaveAccountNote: PropTypes.func.isRequired, | |||||
onChangeAccountNote: PropTypes.func.isRequired, | |||||
intl: PropTypes.object.isRequired, | |||||
}; | |||||
handleChangeAccountNote = (e) => { | |||||
this.props.onChangeAccountNote(e.target.value); | |||||
}; | |||||
componentWillUnmount () { | |||||
if (this.props.isEditing) { | |||||
this.props.onCancelAccountNote(); | |||||
} | |||||
} | |||||
handleKeyDown = e => { | |||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { | |||||
this.props.onSaveAccountNote(); | |||||
} else if (e.keyCode === 27) { | |||||
this.props.onCancelAccountNote(); | |||||
} | |||||
} | |||||
render () { | |||||
const { account, accountNote, isEditing, isSubmitting, intl } = this.props; | |||||
if (!account || (!accountNote && !isEditing)) { | |||||
return null; | |||||
} | |||||
let action_buttons = null; | |||||
if (isEditing) { | |||||
action_buttons = ( | |||||
<div className='account__header__account-note__buttons'> | |||||
<button className='text-btn' tabIndex='0' onClick={this.props.onCancelAccountNote} disabled={isSubmitting}> | |||||
<Icon id='times' size={15} /> <FormattedMessage id='account_note.cancel' defaultMessage='Cancel' /> | |||||
</button> | |||||
<div className='flex-spacer' /> | |||||
<button className='text-btn' tabIndex='0' onClick={this.props.onSaveAccountNote} disabled={isSubmitting}> | |||||
<Icon id='check' size={15} /> <FormattedMessage id='account_note.save' defaultMessage='Save' /> | |||||
</button> | |||||
</div> | |||||
); | |||||
} | |||||
let note_container = null; | |||||
if (isEditing) { | |||||
note_container = ( | |||||
<Textarea | |||||
className='account__header__account-note__content' | |||||
disabled={isSubmitting} | |||||
placeholder={intl.formatMessage(messages.placeholder)} | |||||
value={accountNote} | |||||
onChange={this.handleChangeAccountNote} | |||||
onKeyDown={this.handleKeyDown} | |||||
autoFocus | |||||
/> | |||||
); | |||||
} else { | |||||
note_container = (<div className='account__header__account-note__content'>{accountNote}</div>); | |||||
} | |||||
return ( | |||||
<div className='account__header__account-note'> | |||||
<div className='account__header__account-note__header'> | |||||
<strong><FormattedMessage id='account.account_note_header' defaultMessage='Your note for @{name}' values={{ name: account.get('username') }} /></strong> | |||||
{!isEditing && ( | |||||
<div> | |||||
<button className='text-btn' tabIndex='0' onClick={this.props.onEditAccountNote} disabled={isSubmitting}> | |||||
<Icon id='pencil' size={15} /> <FormattedMessage id='account_note.edit' defaultMessage='Edit' /> | |||||
</button> | |||||
</div> | |||||
)} | |||||
</div> | |||||
{note_container} | |||||
{action_buttons} | |||||
</div> | |||||
); | |||||
} | |||||
} |
@ -0,0 +1,34 @@ | |||||
import { connect } from 'react-redux'; | |||||
import { changeAccountNoteComment, submitAccountNote, initEditAccountNote, cancelAccountNote } from 'flavours/glitch/actions/account_notes'; | |||||
import AccountNote from '../components/account_note'; | |||||
const mapStateToProps = (state, { account }) => { | |||||
const isEditing = state.getIn(['account_notes', 'edit', 'account_id']) === account.get('id'); | |||||
return { | |||||
isSubmitting: state.getIn(['account_notes', 'edit', 'isSubmitting']), | |||||
accountNote: isEditing ? state.getIn(['account_notes', 'edit', 'comment']) : account.getIn(['relationship', 'note']), | |||||
isEditing, | |||||
}; | |||||
}; | |||||
const mapDispatchToProps = (dispatch, { account }) => ({ | |||||
onEditAccountNote() { | |||||
dispatch(initEditAccountNote(account)); | |||||
}, | |||||
onSaveAccountNote() { | |||||
dispatch(submitAccountNote()); | |||||
}, | |||||
onCancelAccountNote() { | |||||
dispatch(cancelAccountNote()); | |||||
}, | |||||
onChangeAccountNote(comment) { | |||||
dispatch(changeAccountNoteComment(comment)); | |||||
}, | |||||
}); | |||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountNote); |
@ -0,0 +1,44 @@ | |||||
import { Map as ImmutableMap } from 'immutable'; | |||||
import { | |||||
ACCOUNT_NOTE_INIT_EDIT, | |||||
ACCOUNT_NOTE_CANCEL, | |||||
ACCOUNT_NOTE_CHANGE_COMMENT, | |||||
ACCOUNT_NOTE_SUBMIT_REQUEST, | |||||
ACCOUNT_NOTE_SUBMIT_FAIL, | |||||
ACCOUNT_NOTE_SUBMIT_SUCCESS, | |||||
} from '../actions/account_notes'; | |||||
const initialState = ImmutableMap({ | |||||
edit: ImmutableMap({ | |||||
isSubmitting: false, | |||||
account_id: null, | |||||
comment: null, | |||||
}), | |||||
}); | |||||
export default function account_notes(state = initialState, action) { | |||||
switch (action.type) { | |||||
case ACCOUNT_NOTE_INIT_EDIT: | |||||
return state.withMutations((state) => { | |||||
state.setIn(['edit', 'isSubmitting'], false); | |||||
state.setIn(['edit', 'account_id'], action.account.get('id')); | |||||
state.setIn(['edit', 'comment'], action.comment); | |||||
}); | |||||
case ACCOUNT_NOTE_CHANGE_COMMENT: | |||||
return state.setIn(['edit', 'comment'], action.comment); | |||||
case ACCOUNT_NOTE_SUBMIT_REQUEST: | |||||
return state.setIn(['edit', 'isSubmitting'], true); | |||||
case ACCOUNT_NOTE_SUBMIT_FAIL: | |||||
return state.setIn(['edit', 'isSubmitting'], false); | |||||
case ACCOUNT_NOTE_SUBMIT_SUCCESS: | |||||
case ACCOUNT_NOTE_CANCEL: | |||||
return state.withMutations((state) => { | |||||
state.setIn(['edit', 'isSubmitting'], false); | |||||
state.setIn(['edit', 'account_id'], null); | |||||
state.setIn(['edit', 'comment'], null); | |||||
}); | |||||
default: | |||||
return state; | |||||
} | |||||
} |