Add title editor and delete button

This commit is contained in:
KMY 2023-08-26 14:13:45 +09:00
parent 87490a3220
commit 09b4b025df
6 changed files with 138 additions and 8 deletions

View file

@ -113,7 +113,7 @@ export const submitBookmarkCategoryEditor = shouldReset => (dispatch, getState)
export const setupBookmarkCategoryEditor = bookmarkCategoryId => (dispatch, getState) => { export const setupBookmarkCategoryEditor = bookmarkCategoryId => (dispatch, getState) => {
dispatch({ dispatch({
type: BOOKMARK_CATEGORY_EDITOR_SETUP, type: BOOKMARK_CATEGORY_EDITOR_SETUP,
bookmarkCategory: getState().getIn(['bookmarkCategories', bookmarkCategoryId]), bookmarkCategory: getState().getIn(['bookmark_categories', bookmarkCategoryId]),
}); });
dispatch(fetchBookmarkCategoryStatuses(bookmarkCategoryId)); dispatch(fetchBookmarkCategoryStatuses(bookmarkCategoryId));

View file

@ -0,0 +1,73 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { changeBookmarkCategoryEditorTitle, submitBookmarkCategoryEditor } from '../../../actions/bookmark_categories';
import { IconButton } from '../../../components/icon_button';
const messages = defineMessages({
title: { id: 'bookmark_categories.edit.submit', defaultMessage: 'Change title' },
});
const mapStateToProps = state => ({
value: state.getIn(['bookmarkCategoryEditor', 'title']),
disabled: !state.getIn(['bookmarkCategoryEditor', 'isChanged']) || !state.getIn(['bookmarkCategoryEditor', 'title']),
});
const mapDispatchToProps = dispatch => ({
onChange: value => dispatch(changeBookmarkCategoryEditorTitle(value)),
onSubmit: () => dispatch(submitBookmarkCategoryEditor(false)),
});
class EditBookmarkCategoryForm extends PureComponent {
static propTypes = {
value: PropTypes.string.isRequired,
disabled: PropTypes.bool,
intl: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};
handleChange = e => {
this.props.onChange(e.target.value);
};
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit();
};
handleClick = () => {
this.props.onSubmit();
};
render () {
const { value, disabled, intl } = this.props;
const title = intl.formatMessage(messages.title);
return (
<form className='column-inline-form' onSubmit={this.handleSubmit}>
<input
className='setting-text'
value={value}
onChange={this.handleChange}
/>
<IconButton
disabled={disabled}
icon='check'
title={title}
onClick={this.handleClick}
/>
</form>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(EditBookmarkCategoryForm));

View file

@ -10,16 +10,23 @@ import { connect } from 'react-redux';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { expandBookmarkCategoryStatuses, fetchBookmarkCategory, fetchBookmarkCategoryStatuses } from 'mastodon/actions/bookmark_categories'; import { deleteBookmarkCategory, expandBookmarkCategoryStatuses, fetchBookmarkCategory, fetchBookmarkCategoryStatuses , setupBookmarkCategoryEditor } from 'mastodon/actions/bookmark_categories';
import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns';
import { openModal } from 'mastodon/actions/modal';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import StatusList from 'mastodon/components/status_list'; import StatusList from 'mastodon/components/status_list';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error'; import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import Column from 'mastodon/features/ui/components/column'; import Column from 'mastodon/features/ui/components/column';
import { getBookmarkCategoryStatusList } from 'mastodon/selectors'; import { getBookmarkCategoryStatusList } from 'mastodon/selectors';
import EditBookmarkCategoryForm from './components/edit_bookmark_category_form';
const messages = defineMessages({ const messages = defineMessages({
deleteMessage: { id: 'confirmations.delete_bookmary_category.message', defaultMessage: 'Are you sure you want to permanently delete this category?' },
deleteConfirm: { id: 'confirmations.delete_bookmark_category.confirm', defaultMessage: 'Delete' },
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' }, heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
}); });
@ -27,11 +34,16 @@ const mapStateToProps = (state, { params }) => ({
bookmarkCategory: state.getIn(['bookmark_categories', params.id]), bookmarkCategory: state.getIn(['bookmark_categories', params.id]),
statusIds: getBookmarkCategoryStatusList(state, params.id), statusIds: getBookmarkCategoryStatusList(state, params.id),
isLoading: state.getIn(['bookmark_categories', params.id, 'isLoading'], true), isLoading: state.getIn(['bookmark_categories', params.id, 'isLoading'], true),
isEditing: state.getIn(['bookmarkCategoryEditor', 'bookmarkCategoryId']) === params.id,
hasMore: !!state.getIn(['bookmark_categories', params.id, 'next']), hasMore: !!state.getIn(['bookmark_categories', params.id, 'next']),
}); });
class BookmarkCategoryStatuses extends ImmutablePureComponent { class BookmarkCategoryStatuses extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
};
static propTypes = { static propTypes = {
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -42,6 +54,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
isEditing: PropTypes.bool,
}; };
UNSAFE_componentWillMount () { UNSAFE_componentWillMount () {
@ -68,6 +81,32 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
this.column.scrollTop(); this.column.scrollTop();
}; };
handleEditClick = () => {
this.props.dispatch(setupBookmarkCategoryEditor(this.props.params.id));
};
handleDeleteClick = () => {
const { dispatch, columnId, intl } = this.props;
const { id } = this.props.params;
dispatch(openModal({
modalType: 'CONFIRM',
modalProps: {
message: intl.formatMessage(messages.deleteMessage),
confirm: intl.formatMessage(messages.deleteConfirm),
onConfirm: () => {
dispatch(deleteBookmarkCategory(id));
if (columnId) {
dispatch(removeColumn(columnId));
} else {
this.context.router.history.push('/bookmark_categories');
}
},
},
}));
};
setRef = c => { setRef = c => {
this.column = c; this.column = c;
}; };
@ -77,7 +116,7 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
}, 300, { leading: true }); }, 300, { leading: true });
render () { render () {
const { intl, bookmarkCategory, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props; const { intl, bookmarkCategory, statusIds, columnId, multiColumn, hasMore, isLoading, isEditing } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
if (typeof bookmarkCategory === 'undefined') { if (typeof bookmarkCategory === 'undefined') {
@ -96,6 +135,10 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked posts yet. When you bookmark one, it will show up here." />; const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked posts yet. When you bookmark one, it will show up here." />;
const editor = isEditing && (
<EditBookmarkCategoryForm />
);
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
<ColumnHeader <ColumnHeader
@ -106,7 +149,19 @@ class BookmarkCategoryStatuses extends ImmutablePureComponent {
onClick={this.handleHeaderClick} onClick={this.handleHeaderClick}
pinned={pinned} pinned={pinned}
multiColumn={multiColumn} multiColumn={multiColumn}
/> >
<div className='column-settings__row column-header__links'>
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
<Icon id='pencil' /> <FormattedMessage id='bookmark_categories.edit' defaultMessage='Edit category' />
</button>
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
<Icon id='trash' /> <FormattedMessage id='bookmark_categories.delete' defaultMessage='Delete category' />
</button>
{editor}
</div>
</ColumnHeader>
<StatusList <StatusList
trackScroll={!pinned} trackScroll={!pinned}

View file

@ -147,7 +147,7 @@ class ListTimeline extends PureComponent {
handleEditAntennaClick = (e) => { handleEditAntennaClick = (e) => {
const id = e.currentTarget.getAttribute('data-id'); const id = e.currentTarget.getAttribute('data-id');
this.context.router.history.push(`/antennasw/${id}/edit`); this.context.router.history.push(`/antennasw/${id}/edit`);
} };
handleRepliesPolicyChange = ({ target }) => { handleRepliesPolicyChange = ({ target }) => {
const { dispatch } = this.props; const { dispatch } = this.props;

View file

@ -49,7 +49,9 @@ const appendToBookmarkCategoryStatuses = (state, bookmarkCategoryId, statuses, n
const removeStatusFromAllBookmarkCategories = (state, status) => { const removeStatusFromAllBookmarkCategories = (state, status) => {
state.toList().forEach((bookmarkCategory) => { state.toList().forEach((bookmarkCategory) => {
if (state.getIn([bookmarkCategory.get('id'), 'items'])) {
state = state.updateIn([bookmarkCategory.get('id'), 'items'], items => items.delete(status.get('id'))); state = state.updateIn([bookmarkCategory.get('id'), 'items'], items => items.delete(status.get('id')));
}
}); });
return state; return state;
}; };
@ -58,8 +60,9 @@ export default function bookmarkCategories(state = initialState, action) {
switch(action.type) { switch(action.type) {
case BOOKMARK_CATEGORY_FETCH_SUCCESS: case BOOKMARK_CATEGORY_FETCH_SUCCESS:
case BOOKMARK_CATEGORY_CREATE_SUCCESS: case BOOKMARK_CATEGORY_CREATE_SUCCESS:
case BOOKMARK_CATEGORY_UPDATE_SUCCESS:
return normalizeBookmarkCategory(state, action.bookmarkCategory); return normalizeBookmarkCategory(state, action.bookmarkCategory);
case BOOKMARK_CATEGORY_UPDATE_SUCCESS:
return state.setIn([action.bookmarkCategory.id, 'title'], action.bookmarkCategory.title);
case BOOKMARK_CATEGORIES_FETCH_SUCCESS: case BOOKMARK_CATEGORIES_FETCH_SUCCESS:
return normalizeBookmarkCategories(state, action.bookmarkCategories); return normalizeBookmarkCategories(state, action.bookmarkCategories);
case BOOKMARK_CATEGORY_DELETE_SUCCESS: case BOOKMARK_CATEGORY_DELETE_SUCCESS:

View file

@ -39,7 +39,6 @@ export default function bookmarkCategoryEditorReducer(state = initialState, acti
return state.withMutations(map => { return state.withMutations(map => {
map.set('bookmarkCategoryId', action.bookmarkCategory.get('id')); map.set('bookmarkCategoryId', action.bookmarkCategory.get('id'));
map.set('title', action.bookmarkCategory.get('title')); map.set('title', action.bookmarkCategory.get('title'));
map.set('isExclusive', action.bookmarkCategory.get('is_exclusive'));
map.set('isSubmitting', false); map.set('isSubmitting', false);
}); });
case BOOKMARK_CATEGORY_EDITOR_TITLE_CHANGE: case BOOKMARK_CATEGORY_EDITOR_TITLE_CHANGE: