From 335cfab32f2b61e69563b1318d781b51c6b4632f Mon Sep 17 00:00:00 2001 From: Claire Date: Sun, 3 Dec 2023 11:22:38 +0100 Subject: [PATCH] Change account note design to match upstream's (#2495) --- .../flavours/glitch/actions/account_notes.js | 36 +--- .../account/components/account_note.jsx | 202 ++++++++++++------ .../features/account/components/header.jsx | 8 - .../containers/account_note_container.js | 29 +-- .../containers/header_container.jsx | 5 - .../flavours/glitch/locales/en.json | 16 +- .../flavours/glitch/reducers/account_notes.js | 44 ---- .../flavours/glitch/reducers/index.ts | 2 - .../glitch/styles/components/accounts.scss | 46 +--- .../glitch/styles/components/misc.scss | 9 + 10 files changed, 158 insertions(+), 239 deletions(-) delete mode 100644 app/javascript/flavours/glitch/reducers/account_notes.js diff --git a/app/javascript/flavours/glitch/actions/account_notes.js b/app/javascript/flavours/glitch/actions/account_notes.js index 62a6b4cbb8..72b943300d 100644 --- a/app/javascript/flavours/glitch/actions/account_notes.js +++ b/app/javascript/flavours/glitch/actions/account_notes.js @@ -4,19 +4,12 @@ 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() { +export function submitAccountNote(id, value) { 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']), + comment: value, }).then(response => { dispatch(submitAccountNoteSuccess(response.data)); }).catch(error => dispatch(submitAccountNoteFail(error))); @@ -42,28 +35,3 @@ export function submitAccountNoteFail(error) { 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, - }; -} diff --git a/app/javascript/flavours/glitch/features/account/components/account_note.jsx b/app/javascript/flavours/glitch/features/account/components/account_note.jsx index 041f8de983..bab523acf6 100644 --- a/app/javascript/flavours/glitch/features/account/components/account_note.jsx +++ b/app/javascript/flavours/glitch/features/account/components/account_note.jsx @@ -1,108 +1,174 @@ import PropTypes from 'prop-types'; +import { PureComponent } from 'react'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import { is } from 'immutable'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; import Textarea from 'react-textarea-autosize'; -import { Icon } from 'flavours/glitch/components/icon'; - const messages = defineMessages({ - placeholder: { id: 'account_note.glitch_placeholder', defaultMessage: 'No comment provided' }, + placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' }, }); -class Header extends ImmutablePureComponent { +class InlineAlert extends PureComponent { 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, + show: PropTypes.bool, }; - handleChangeAccountNote = (e) => { - this.props.onChangeAccountNote(e.target.value); + state = { + mountMessage: false, }; - componentWillUnmount () { - if (this.props.isEditing) { - this.props.onCancelAccountNote(); + static TRANSITION_DELAY = 200; + + UNSAFE_componentWillReceiveProps (nextProps) { + if (!this.props.show && nextProps.show) { + this.setState({ mountMessage: true }); + } else if (this.props.show && !nextProps.show) { + setTimeout(() => this.setState({ mountMessage: false }), InlineAlert.TRANSITION_DELAY); } } + render () { + const { show } = this.props; + const { mountMessage } = this.state; + + return ( + + {mountMessage && } + + ); + } + +} + +class AccountNote extends ImmutablePureComponent { + + static propTypes = { + account: ImmutablePropTypes.map.isRequired, + value: PropTypes.string, + onSave: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + state = { + value: null, + saving: false, + saved: false, + }; + + UNSAFE_componentWillMount () { + this._reset(); + } + + UNSAFE_componentWillReceiveProps (nextProps) { + const accountWillChange = !is(this.props.account, nextProps.account); + const newState = {}; + + if (accountWillChange && this._isDirty()) { + this._save(false); + } + + if (accountWillChange || nextProps.value === this.state.value) { + newState.saving = false; + } + + if (this.props.value !== nextProps.value) { + newState.value = nextProps.value; + } + + this.setState(newState); + } + + componentWillUnmount () { + if (this._isDirty()) { + this._save(false); + } + } + + setTextareaRef = c => { + this.textarea = c; + }; + + handleChange = e => { + this.setState({ value: e.target.value, saving: false }); + }; + handleKeyDown = e => { if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { - this.props.onSaveAccountNote(); + e.preventDefault(); + + this._save(); + + if (this.textarea) { + this.textarea.blur(); + } } else if (e.keyCode === 27) { - this.props.onCancelAccountNote(); + e.preventDefault(); + + this._reset(() => { + if (this.textarea) { + this.textarea.blur(); + } + }); } }; + handleBlur = () => { + if (this._isDirty()) { + this._save(); + } + }; + + _save (showMessage = true) { + this.setState({ saving: true }, () => this.props.onSave(this.state.value)); + + if (showMessage) { + this.setState({ saved: true }, () => setTimeout(() => this.setState({ saved: false }), 2000)); + } + } + + _reset (callback) { + this.setState({ value: this.props.value }, callback); + } + + _isDirty () { + return !this.state.saving && this.props.value !== null && this.state.value !== null && this.state.value !== this.props.value; + } + render () { - const { account, accountNote, isEditing, isSubmitting, intl } = this.props; + const { account, intl } = this.props; + const { value, saved } = this.state; - if (!account || (!accountNote && !isEditing)) { + if (!account) { return null; } - let action_buttons = null; - if (isEditing) { - action_buttons = ( -
- -
- -
- ); - } else { - action_buttons = ( -
- -
- ); - } - - let note_container = null; - if (isEditing) { - note_container = ( -