Port 65506bac3f
to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
local
parent
50f5254568
commit
511edccf61
10 changed files with 339 additions and 1 deletions
@ -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; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue