forked from fedi/mastodon
Change design of account notes in web UI (#14208)
* Change design of account notes in web UI * Fix `for` -> `htmlFor`
This commit is contained in:
parent
83fd046107
commit
c3187411c2
app
javascript
mastodon
actions
features
account
account_timeline/containers
locales
reducers
styles/mastodon
serializers/rest
|
@ -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_SUCCESS = 'ACCOUNT_NOTE_SUBMIT_SUCCESS';
|
||||||
export const ACCOUNT_NOTE_SUBMIT_FAIL = 'ACCOUNT_NOTE_SUBMIT_FAIL';
|
export const ACCOUNT_NOTE_SUBMIT_FAIL = 'ACCOUNT_NOTE_SUBMIT_FAIL';
|
||||||
|
|
||||||
export const ACCOUNT_NOTE_INIT_EDIT = 'ACCOUNT_NOTE_INIT_EDIT';
|
export function submitAccountNote(id, value) {
|
||||||
export const ACCOUNT_NOTE_CANCEL = 'ACCOUNT_NOTE_CANCEL';
|
|
||||||
|
|
||||||
export const ACCOUNT_NOTE_CHANGE_COMMENT = 'ACCOUNT_NOTE_CHANGE_COMMENT';
|
|
||||||
|
|
||||||
export function submitAccountNote() {
|
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
dispatch(submitAccountNoteRequest());
|
dispatch(submitAccountNoteRequest());
|
||||||
|
|
||||||
const id = getState().getIn(['account_notes', 'edit', 'account_id']);
|
|
||||||
|
|
||||||
api(getState).post(`/api/v1/accounts/${id}/note`, {
|
api(getState).post(`/api/v1/accounts/${id}/note`, {
|
||||||
comment: getState().getIn(['account_notes', 'edit', 'comment']),
|
comment: value,
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
dispatch(submitAccountNoteSuccess(response.data));
|
dispatch(submitAccountNoteSuccess(response.data));
|
||||||
}).catch(error => dispatch(submitAccountNoteFail(error)));
|
}).catch(error => dispatch(submitAccountNoteFail(error)));
|
||||||
|
@ -42,28 +35,3 @@ export function submitAccountNoteFail(error) {
|
||||||
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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -3,99 +3,166 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Icon from 'mastodon/components/icon';
|
|
||||||
import Textarea from 'react-textarea-autosize';
|
import Textarea from 'react-textarea-autosize';
|
||||||
|
import { is } from 'immutable';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
placeholder: { id: 'account_note.placeholder', defaultMessage: 'No comment provided' },
|
placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @injectIntl
|
class InlineAlert extends React.PureComponent {
|
||||||
class Header extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
account: ImmutablePropTypes.map.isRequired,
|
show: PropTypes.bool,
|
||||||
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) => {
|
state = {
|
||||||
this.props.onChangeAccountNote(e.target.value);
|
mountMessage: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount () {
|
static TRANSITION_DELAY = 200;
|
||||||
if (this.props.isEditing) {
|
|
||||||
this.props.onCancelAccountNote();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeyDown = e => {
|
componentWillReceiveProps (nextProps) {
|
||||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
if (!this.props.show && nextProps.show) {
|
||||||
this.props.onSaveAccountNote();
|
this.setState({ mountMessage: true });
|
||||||
} else if (e.keyCode === 27) {
|
} else if (this.props.show && !nextProps.show) {
|
||||||
this.props.onCancelAccountNote();
|
setTimeout(() => this.setState({ mountMessage: false }), InlineAlert.TRANSITION_DELAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, accountNote, isEditing, isSubmitting, intl } = this.props;
|
const { show } = this.props;
|
||||||
|
const { mountMessage } = this.state;
|
||||||
|
|
||||||
if (!account || (!accountNote && !isEditing)) {
|
return (
|
||||||
|
<span aria-live='polite' role='status' className='inline-alert' style={{ opacity: show ? 1 : 0 }}>
|
||||||
|
{mountMessage && <FormattedMessage id='generic.saved' defaultMessage='Saved' />}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
this._reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
this._save();
|
||||||
|
|
||||||
|
if (this.textarea) {
|
||||||
|
this.textarea.blur();
|
||||||
|
}
|
||||||
|
} else if (e.keyCode === 27) {
|
||||||
|
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, intl } = this.props;
|
||||||
|
const { value, saved } = this.state;
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
return null;
|
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 (
|
return (
|
||||||
<div className='account__header__account-note'>
|
<div className='account__header__account-note'>
|
||||||
<div className='account__header__account-note__header'>
|
<label htmlFor={`account-note-${account.get('id')}`}>
|
||||||
<strong><FormattedMessage id='account.account_note_header' defaultMessage='Your note for @{name}' values={{ name: account.get('username') }} /></strong>
|
<FormattedMessage id='account.account_note_header' defaultMessage='Note' /> <InlineAlert show={saved} />
|
||||||
{!isEditing && (
|
</label>
|
||||||
<div>
|
|
||||||
<button className='text-btn' tabIndex='0' onClick={this.props.onEditAccountNote} disabled={isSubmitting}>
|
<Textarea
|
||||||
<Icon id='pencil' size={15} /> <FormattedMessage id='account_note.edit' defaultMessage='Edit' />
|
id={`account-note-${account.get('id')}`}
|
||||||
</button>
|
className='account__header__account-note__content'
|
||||||
</div>
|
disabled={this.props.value === null || value === null}
|
||||||
)}
|
placeholder={intl.formatMessage(messages.placeholder)}
|
||||||
</div>
|
value={value || ''}
|
||||||
{note_container}
|
onChange={this.handleChange}
|
||||||
{action_buttons}
|
onKeyDown={this.handleKeyDown}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
ref={this.setTextareaRef}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,6 @@ class Header extends ImmutablePureComponent {
|
||||||
identity_props: ImmutablePropTypes.list,
|
identity_props: ImmutablePropTypes.list,
|
||||||
onFollow: PropTypes.func.isRequired,
|
onFollow: PropTypes.func.isRequired,
|
||||||
onBlock: PropTypes.func.isRequired,
|
onBlock: PropTypes.func.isRequired,
|
||||||
onEditAccountNote: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
domain: PropTypes.string.isRequired,
|
domain: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -132,8 +131,6 @@ class Header extends ImmutablePureComponent {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountNote = account.getIn(['relationship', 'note']);
|
|
||||||
|
|
||||||
let info = [];
|
let info = [];
|
||||||
let actionBtn = '';
|
let actionBtn = '';
|
||||||
let lockedIcon = '';
|
let lockedIcon = '';
|
||||||
|
@ -184,10 +181,6 @@ class Header extends ImmutablePureComponent {
|
||||||
menu.push(null);
|
menu.push(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accountNote === null) {
|
|
||||||
menu.push({ text: intl.formatMessage(messages.add_account_note, { name: account.get('username') }), action: this.props.onEditAccountNote });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.get('id') === me) {
|
if (account.get('id') === me) {
|
||||||
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
|
menu.push({ text: intl.formatMessage(messages.edit_profile), href: '/settings/profile' });
|
||||||
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
|
menu.push({ text: intl.formatMessage(messages.preferences), href: '/settings/preferences' });
|
||||||
|
@ -294,8 +287,6 @@ class Header extends ImmutablePureComponent {
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AccountNoteContainer account={account} />
|
|
||||||
|
|
||||||
<div className='account__header__extra'>
|
<div className='account__header__extra'>
|
||||||
<div className='account__header__bio'>
|
<div className='account__header__bio'>
|
||||||
{ (fields.size > 0 || identity_proofs.size > 0) && (
|
{ (fields.size > 0 || identity_proofs.size > 0) && (
|
||||||
|
@ -324,6 +315,8 @@ class Header extends ImmutablePureComponent {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{account.get('id') !== me && <AccountNoteContainer account={account} />}
|
||||||
|
|
||||||
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content' dangerouslySetInnerHTML={content} />}
|
{account.get('note').length > 0 && account.get('note') !== '<p></p>' && <div className='account__header__content' dangerouslySetInnerHTML={content} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,34 +1,17 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { changeAccountNoteComment, submitAccountNote, initEditAccountNote, cancelAccountNote } from 'mastodon/actions/account_notes';
|
import { submitAccountNote } from 'mastodon/actions/account_notes';
|
||||||
import AccountNote from '../components/account_note';
|
import AccountNote from '../components/account_note';
|
||||||
|
|
||||||
const mapStateToProps = (state, { account }) => {
|
const mapStateToProps = (state, { account }) => ({
|
||||||
const isEditing = state.getIn(['account_notes', 'edit', 'account_id']) === account.get('id');
|
value: account.getIn(['relationship', 'note']),
|
||||||
|
});
|
||||||
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 }) => ({
|
const mapDispatchToProps = (dispatch, { account }) => ({
|
||||||
|
|
||||||
onEditAccountNote() {
|
onSave (value) {
|
||||||
dispatch(initEditAccountNote(account));
|
dispatch(submitAccountNote(account.get('id'), value));
|
||||||
},
|
},
|
||||||
|
|
||||||
onSaveAccountNote() {
|
|
||||||
dispatch(submitAccountNote());
|
|
||||||
},
|
|
||||||
|
|
||||||
onCancelAccountNote() {
|
|
||||||
dispatch(cancelAccountNote());
|
|
||||||
},
|
|
||||||
|
|
||||||
onChangeAccountNote(comment) {
|
|
||||||
dispatch(changeAccountNoteComment(comment));
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(AccountNote);
|
export default connect(mapStateToProps, mapDispatchToProps)(AccountNote);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { initBlockModal } from '../../../actions/blocks';
|
||||||
import { initReport } from '../../../actions/reports';
|
import { initReport } from '../../../actions/reports';
|
||||||
import { openModal } from '../../../actions/modal';
|
import { openModal } from '../../../actions/modal';
|
||||||
import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
|
import { blockDomain, unblockDomain } from '../../../actions/domain_blocks';
|
||||||
import { initEditAccountNote } from 'mastodon/actions/account_notes';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import { unfollowModal } from '../../../initial_state';
|
import { unfollowModal } from '../../../initial_state';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
@ -103,10 +102,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onEditAccountNote (account) {
|
|
||||||
dispatch(initEditAccountNote(account));
|
|
||||||
},
|
|
||||||
|
|
||||||
onBlockDomain (domain) {
|
onBlockDomain (domain) {
|
||||||
dispatch(openModal('CONFIRM', {
|
dispatch(openModal('CONFIRM', {
|
||||||
message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
|
message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
|
||||||
|
|
|
@ -666,24 +666,16 @@
|
||||||
{
|
{
|
||||||
"descriptors": [
|
"descriptors": [
|
||||||
{
|
{
|
||||||
"defaultMessage": "No comment provided",
|
"defaultMessage": "Click to add a note",
|
||||||
"id": "account_note.placeholder"
|
"id": "account_note.placeholder"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"defaultMessage": "Cancel",
|
"defaultMessage": "Saved",
|
||||||
"id": "account_note.cancel"
|
"id": "generic.saved"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"defaultMessage": "Save",
|
"defaultMessage": "Note",
|
||||||
"id": "account_note.save"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"defaultMessage": "Your note for @{name}",
|
|
||||||
"id": "account.account_note_header"
|
"id": "account.account_note_header"
|
||||||
},
|
|
||||||
{
|
|
||||||
"defaultMessage": "Edit",
|
|
||||||
"id": "account_note.edit"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"path": "app/javascript/mastodon/features/account/components/account_note.json"
|
"path": "app/javascript/mastodon/features/account/components/account_note.json"
|
||||||
|
@ -818,10 +810,6 @@
|
||||||
"defaultMessage": "Open moderation interface for @{name}",
|
"defaultMessage": "Open moderation interface for @{name}",
|
||||||
"id": "status.admin_account"
|
"id": "status.admin_account"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"defaultMessage": "Add note for @{name}",
|
|
||||||
"id": "account.add_account_note"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"defaultMessage": "Follows you",
|
"defaultMessage": "Follows you",
|
||||||
"id": "account.follows_you"
|
"id": "account.follows_you"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"account.account_note_header": "Your note for @{name}",
|
"account.account_note_header": "Note",
|
||||||
"account.add_account_note": "Add note for @{name}",
|
|
||||||
"account.add_or_remove_from_list": "Add or Remove from lists",
|
"account.add_or_remove_from_list": "Add or Remove from lists",
|
||||||
"account.badges.bot": "Bot",
|
"account.badges.bot": "Bot",
|
||||||
"account.badges.group": "Group",
|
"account.badges.group": "Group",
|
||||||
|
@ -42,10 +41,7 @@
|
||||||
"account.unfollow": "Unfollow",
|
"account.unfollow": "Unfollow",
|
||||||
"account.unmute": "Unmute @{name}",
|
"account.unmute": "Unmute @{name}",
|
||||||
"account.unmute_notifications": "Unmute notifications from @{name}",
|
"account.unmute_notifications": "Unmute notifications from @{name}",
|
||||||
"account_note.cancel": "Cancel",
|
"account_note.placeholder": "Click to add note",
|
||||||
"account_note.edit": "Edit",
|
|
||||||
"account_note.placeholder": "No comment provided",
|
|
||||||
"account_note.save": "Save",
|
|
||||||
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
|
"alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
|
||||||
"alert.rate_limited.title": "Rate limited",
|
"alert.rate_limited.title": "Rate limited",
|
||||||
"alert.unexpected.message": "An unexpected error occurred.",
|
"alert.unexpected.message": "An unexpected error occurred.",
|
||||||
|
@ -174,6 +170,7 @@
|
||||||
"follow_request.authorize": "Authorize",
|
"follow_request.authorize": "Authorize",
|
||||||
"follow_request.reject": "Reject",
|
"follow_request.reject": "Reject",
|
||||||
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
|
"follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
|
||||||
|
"generic.saved": "Saved",
|
||||||
"getting_started.developers": "Developers",
|
"getting_started.developers": "Developers",
|
||||||
"getting_started.directory": "Profile directory",
|
"getting_started.directory": "Profile directory",
|
||||||
"getting_started.documentation": "Documentation",
|
"getting_started.documentation": "Documentation",
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -36,7 +36,6 @@ import trends from './trends';
|
||||||
import missed_updates from './missed_updates';
|
import missed_updates from './missed_updates';
|
||||||
import announcements from './announcements';
|
import announcements from './announcements';
|
||||||
import markers from './markers';
|
import markers from './markers';
|
||||||
import account_notes from './account_notes';
|
|
||||||
|
|
||||||
const reducers = {
|
const reducers = {
|
||||||
announcements,
|
announcements,
|
||||||
|
@ -76,7 +75,6 @@ const reducers = {
|
||||||
trends,
|
trends,
|
||||||
missed_updates,
|
missed_updates,
|
||||||
markers,
|
markers,
|
||||||
account_notes,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default combineReducers(reducers);
|
export default combineReducers(reducers);
|
||||||
|
|
|
@ -11,6 +11,15 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline-alert {
|
||||||
|
color: $valid-value-color;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
.no-reduce-motion & {
|
||||||
|
transition: opacity 200ms ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.link-button {
|
.link-button {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
@ -6557,6 +6566,11 @@ noscript {
|
||||||
padding: 20px 15px;
|
padding: 20px 15px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
|
|
||||||
|
.columns-area--mobile & {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.account__header__fields {
|
.account__header__fields {
|
||||||
|
@ -6601,63 +6615,51 @@ noscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__account-note {
|
&__account-note {
|
||||||
margin: 5px;
|
padding: 15px;
|
||||||
padding: 10px;
|
padding-bottom: 10px;
|
||||||
background: $ui-highlight-color;
|
|
||||||
color: $primary-text-color;
|
color: $primary-text-color;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||||
|
|
||||||
&__header {
|
.columns-area--mobile & {
|
||||||
display: flex;
|
padding-left: 20px;
|
||||||
flex-direction: row;
|
padding-right: 20px;
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
label {
|
||||||
white-space: pre-wrap;
|
display: block;
|
||||||
margin-top: 5px;
|
font-size: 12px;
|
||||||
}
|
|
||||||
|
|
||||||
&__buttons {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-top: 5px;
|
|
||||||
|
|
||||||
.flex-spacer {
|
|
||||||
flex: 0 0 20px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
strong {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
color: $darker-text-color;
|
||||||
|
text-transform: uppercase;
|
||||||
button:hover span {
|
margin-bottom: 5px;
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
display: block;
|
display: block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: calc(100% + 20px);
|
||||||
margin: 0;
|
color: $secondary-text-color;
|
||||||
margin-top: 5px;
|
background: transparent;
|
||||||
color: $inverted-text-color;
|
|
||||||
background: $simple-background-color;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
margin: 0 -10px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
resize: none;
|
resize: none;
|
||||||
border: 0;
|
border: 0;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: $dark-text-color;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: $ui-base-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,6 @@ class REST::RelationshipSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def note
|
def note
|
||||||
(instance_options[:relationships].account_note[object.id] || {})[:comment]
|
(instance_options[:relationships].account_note[object.id] || {})[:comment] || ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue