Merge commit 'dee69e4f83' into kb_migration

This commit is contained in:
KMY 2023-05-27 12:51:27 +09:00
commit f010acbde2
18 changed files with 286 additions and 59 deletions

View file

@ -6,7 +6,7 @@ import { hideRecentEmojis } from 'mastodon/initial_state';
import { useEmoji } from '../../../actions/emojis';
import { changeSetting } from '../../../actions/settings';
import { shortCodes } from '../../emoji/emoji_mart_data_light';
import unicodeMapping from '../../emoji/emoji_unicode_mapping_light';
import EmojiPickerDropdown from '../components/emoji_picker_dropdown';
@ -34,7 +34,6 @@ const DEFAULTS = [
];
const RECENT_SIZE = DEFAULTS.length;
const DECK_SIZE = 16;
const getFrequentlyUsedEmojis = createSelector([
state => { return {
@ -43,11 +42,12 @@ const getFrequentlyUsedEmojis = createSelector([
}; },
], data => {
const { emojiCounters, reactionDeck } = data;
let deckEmojis = reactionDeck
.toArray()
.map((e) => e.get('emoji'))
.map((e) => e.get('name'))
.filter((e) => e)
.map((e) => shortCodes[e] || e);
.map((e) => unicodeMapping[e] ? unicodeMapping[e].shortCode : e);
deckEmojis = [...new Set(deckEmojis)];
let emojis;
@ -68,7 +68,9 @@ const getFrequentlyUsedEmojis = createSelector([
emojis = [];
}
emojis = deckEmojis.slice(0, DECK_SIZE).concat(emojis);
emojis = deckEmojis.concat(emojis);
if (emojis.length <= 0) emojis = ['+1'];
return emojis;
});

View file

@ -7,7 +7,6 @@ import { unicodeToUnifiedName } from './unicode_to_unified_name';
const [ shortCodesToEmojiData, skins, categories, short_names ] = emojiCompressed;
const emojis = {};
const shortCodes = {};
// decompress
Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
@ -34,12 +33,10 @@ Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
short_names,
unified,
};
shortCodes[native] = shortCode;
});
export {
emojis,
shortCodes,
skins,
categories,
short_names,

View file

@ -13,7 +13,7 @@ import { registrationsOpen } from 'mastodon/initial_state';
const mapStateToProps = (state, { accountId }) => ({
displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up'),
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up') || '/auth/sign_up',
});
const mapDispatchToProps = (dispatch) => ({

View file

@ -1,40 +1,44 @@
import PropTypes from 'prop-types';
import { injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from 'react-intl';
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { updateReactionDeck } from 'mastodon/actions/reaction_deck';
import Button from 'mastodon/components/button';
import EmojiPickerDropdownContainer from 'mastodon/features/compose/containers/emoji_picker_dropdown_container';
import emojify from 'mastodon/features/emoji/emoji';
import { autoPlayGif } from 'mastodon/initial_state';
const MapStateToProps = (state, { emojiId, emojiMap }) => ({
emoji: (state.get('reaction_deck', ImmutableList()).toArray().find(em => em.get('id') === emojiId) || ImmutableMap({ emoji: '' })).get('emoji'),
emojiMap,
});
const mapDispatchToProps = (dispatch, { emojiId }) => ({
onChange: (emoji) => dispatch(updateReactionDeck(emojiId, emoji)),
const messages = defineMessages({
remove: { id: 'reaction_deck.remove', defaultMessage: 'Remove' },
});
class ReactionEmoji extends ImmutablePureComponent {
static propTypes = {
index: PropTypes.number,
emoji: PropTypes.string,
emojiMap: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
};
static defaultProps = {
emoji: '',
};
handleChange = (emoji) => {
this.props.onChange(this.props.index, emoji);
};
handleRemove = () => {
this.props.onRemove(this.props.index);
};
render () {
const { emojiMap, emoji, onChange } = this.props;
const { intl, emojiMap, emoji } = this.props;
let content = null;
@ -61,9 +65,14 @@ class ReactionEmoji extends ImmutablePureComponent {
return (
<div className='reaction_deck__emoji'>
<div className='reaction_deck__emoji__wrapper'>
<EmojiPickerDropdownContainer onPickEmoji={onChange} />
<div>
{content}
<div className='reaction_deck__emoji__wrapper__content'>
<EmojiPickerDropdownContainer onPickEmoji={this.handleChange} />
<div>
{content}
</div>
</div>
<div className='reaction_deck__emoji__wrapper__options'>
<Button secondary text={intl.formatMessage(messages.remove)} onClick={this.handleRemove} />
</div>
</div>
</div>
@ -72,4 +81,4 @@ class ReactionEmoji extends ImmutablePureComponent {
}
export default connect(MapStateToProps, mapDispatchToProps)(injectIntl(ReactionEmoji));
export default connect()(injectIntl(ReactionEmoji));

View file

@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import { useEffect, useState } from "react";
import { defineMessages, injectIntl } from 'react-intl';
@ -10,20 +11,41 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { updateReactionDeck } from 'mastodon/actions/reaction_deck';
import Button from 'mastodon/components/button';
import ColumnHeader from 'mastodon/components/column_header';
import LoadingIndicator from 'mastodon/components/loading_indicator';
import ScrollableList from 'mastodon/components/scrollable_list';
import Column from 'mastodon/features/ui/components/column';
import ReactionEmoji from './components/reaction_emoji';
const DECK_SIZE = 16;
// https://medium.com/@wbern/getting-react-18s-strict-mode-to-work-with-react-beautiful-dnd-47bc909348e4
/* eslint react/prop-types: 0 */
const StrictModeDroppable = ({ children, ...props }) => {
const [enabled, setEnabled] = useState(false);
useEffect(() => {
const animation = requestAnimationFrame(() => setEnabled(true));
return () => {
cancelAnimationFrame(animation);
setEnabled(false);
};
}, []);
if (!enabled) {
return null;
}
return <Droppable {...props}>{children}</Droppable>;
};
/* eslint react/prop-types: 0 */
const customEmojiMap = createSelector([state => state.get('custom_emojis')], items => items.reduce((map, emoji) => map.set(emoji.get('shortcode'), emoji), ImmutableMap()));
const messages = defineMessages({
refresh: { id: 'refresh', defaultMessage: 'Refresh' },
reaction_deck_add: { id: 'reaction_deck.add', defaultMessage: 'Add' },
heading: { id: 'column.reaction_deck', defaultMessage: 'Reaction deck' },
});
@ -33,17 +55,52 @@ const mapStateToProps = (state, props) => ({
emojiMap: customEmojiMap(state),
});
const mapDispatchToProps = (dispatch) => ({
onChange: (emojis) => dispatch(updateReactionDeck(emojis)),
});
class ReactionDeck extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
deck: ImmutablePropTypes.list,
emojiMap: ImmutablePropTypes.map,
multiColumn: PropTypes.bool,
intl: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
};
deckToArray = () => {
const { deck } = this.props;
return deck.map((item) => item.get('name')).toArray();
};
handleReorder = (result) => {
const newDeck = this.deckToArray();
const deleted = newDeck.splice(result.source.index, 1);
newDeck.splice(result.destination.index, 0, deleted[0]);
this.props.onChange(newDeck);
};
handleChange = (index, emoji) => {
const newDeck = this.deckToArray();
newDeck[index] = emoji.native || emoji.id.replace(':', '');
this.props.onChange(newDeck);
};
handleRemove = (index) => {
const newDeck = this.deckToArray();
newDeck.splice(index, 1);
this.props.onChange(newDeck);
};
handleAdd = () => {
const newDeck = this.deckToArray();
newDeck.push('👍');
this.props.onChange(newDeck);
}
render () {
const { intl, deck, emojiMap, multiColumn } = this.props;
@ -69,9 +126,31 @@ class ReactionDeck extends ImmutablePureComponent {
scrollKey='reaction_deck'
bindToDocument={!multiColumn}
>
{[...Array(DECK_SIZE).keys()].map(emojiId =>
<ReactionEmoji key={emojiId} emojiMap={emojiMap} emojiId={emojiId} />
)}
<DragDropContext onDragEnd={this.handleReorder}>
<StrictModeDroppable droppableId='deckitems'>
{(provided) => (
<div className='deckitems' {...provided.droppableProps} ref={provided.innerRef}>
{deck.map((emoji, index) => (
<Draggable key={index} draggableId={'' + index} index={index}>
{(provided2) => (
<div ref={provided2.innerRef} {...provided2.draggableProps} {...provided2.dragHandleProps}>
<ReactionEmoji emojiMap={emojiMap}
emoji={emoji.get('name')}
index={index}
onChange={this.handleChange}
onRemove={this.handleRemove}
/>
</div>
)}
</Draggable>
))}
{provided.placeholder}
<Button text={intl.formatMessage(messages.reaction_deck_add)} onClick={this.handleAdd} />
</div>
)}
</StrictModeDroppable>
</DragDropContext>
</ScrollableList>
<Helmet>
@ -83,4 +162,4 @@ class ReactionDeck extends ImmutablePureComponent {
}
export default connect(mapStateToProps)(injectIntl(ReactionDeck));
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ReactionDeck));

View file

@ -61,7 +61,10 @@ class NavigationPanel extends Component {
</div>
{signedIn && (
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
<>
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
<ColumnLink transparent to='/home' icon='home' text={intl.formatMessage(messages.home)} />
</>
)}
{!signedIn && explorer}
@ -82,7 +85,6 @@ class NavigationPanel extends Component {
{signedIn && (
<>
<ColumnLink transparent to='/notifications' icon={<NotificationsCounterIcon className='column-link__icon' />} text={intl.formatMessage(messages.notifications)} />
<FollowRequestsColumnLink />
<ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
</>

View file

@ -17,7 +17,7 @@ const SignInBanner = () => {
let signupButton;
const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up'));
const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], '/auth/sign_up') || '/auth/sign_up');
if (registrationsOpen) {
signupButton = (