Add reaction deck
This commit is contained in:
parent
db5c358f4f
commit
f1625fe101
24 changed files with 404 additions and 13 deletions
45
app/controllers/api/v1/reaction_deck_controller.rb
Normal file
45
app/controllers/api/v1/reaction_deck_controller.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::ReactionDeckController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:lists' }, only: [:index]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:lists' }, only: [:create]
|
||||
|
||||
before_action :require_user!
|
||||
before_action :set_deck, only: [:index, :create]
|
||||
|
||||
rescue_from ArgumentError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
end
|
||||
|
||||
def index
|
||||
render json: @deck
|
||||
end
|
||||
|
||||
def create
|
||||
(deck_params['emojis'] || []).each do |data|
|
||||
raise ArgumentError if data['id'].to_i >= 16 || data['id'].to_i.negative?
|
||||
|
||||
exists = @deck.find { |dd| dd['id'] == data['id'] }
|
||||
if exists
|
||||
exists['emoji'] = data['emoji'].delete(':')
|
||||
else
|
||||
@deck << { id: data['id'], emoji: data['emoji'].delete(':') }
|
||||
end
|
||||
end
|
||||
@deck = @deck.sort_by { |a| a['id'].to_i }
|
||||
current_user.settings['reaction_deck'] = @deck.to_json
|
||||
current_user.save!
|
||||
|
||||
render json: @deck
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_deck
|
||||
@deck = current_user.setting_reaction_deck ? JSON.parse(current_user.setting_reaction_deck) : []
|
||||
end
|
||||
|
||||
def deck_params
|
||||
params
|
||||
end
|
||||
end
|
79
app/javascript/mastodon/actions/reaction_deck.js
Normal file
79
app/javascript/mastodon/actions/reaction_deck.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import api from '../api';
|
||||
|
||||
export const REACTION_DECK_FETCH_REQUEST = 'REACTION_DECK_FETCH_REQUEST';
|
||||
export const REACTION_DECK_FETCH_SUCCESS = 'REACTION_DECK_FETCH_SUCCESS';
|
||||
export const REACTION_DECK_FETCH_FAIL = 'REACTION_DECK_FETCH_FAIL';
|
||||
|
||||
export const REACTION_DECK_UPDATE_REQUEST = 'REACTION_DECK_UPDATE_REQUEST';
|
||||
export const REACTION_DECK_UPDATE_SUCCESS = 'REACTION_DECK_UPDATE_SUCCESS';
|
||||
export const REACTION_DECK_UPDATE_FAIL = 'REACTION_DECK_UPDATE_FAIL';
|
||||
|
||||
export function fetchReactionDeck() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchReactionDeckRequest());
|
||||
|
||||
api(getState).get('/api/v1/reaction_deck').then(response => {
|
||||
dispatch(fetchReactionDeckSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchReactionDeckFail(error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchReactionDeckRequest() {
|
||||
return {
|
||||
type: REACTION_DECK_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchReactionDeckSuccess(emojis) {
|
||||
return {
|
||||
type: REACTION_DECK_FETCH_SUCCESS,
|
||||
emojis,
|
||||
skipLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchReactionDeckFail(error) {
|
||||
return {
|
||||
type: REACTION_DECK_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateReactionDeck(id, emoji) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(updateReactionDeckRequest());
|
||||
|
||||
api(getState).post('/api/v1/reaction_deck', { emojis: [{ id, emoji: emoji.native || emoji.id }] }).then(response => {
|
||||
dispatch(updateReactionDeckSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(updateReactionDeckFail(error));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function updateReactionDeckRequest() {
|
||||
return {
|
||||
type: REACTION_DECK_UPDATE_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateReactionDeckSuccess(emojis) {
|
||||
return {
|
||||
type: REACTION_DECK_UPDATE_SUCCESS,
|
||||
emojis,
|
||||
skipLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateReactionDeckFail(error) {
|
||||
return {
|
||||
type: REACTION_DECK_UPDATE_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
}
|
|
@ -11,6 +11,7 @@ import { Provider as ReduxProvider } from 'react-redux';
|
|||
import { ScrollContext } from 'react-router-scroll-4';
|
||||
|
||||
import { fetchCustomEmojis } from 'mastodon/actions/custom_emojis';
|
||||
import { fetchReactionDeck } from 'mastodon/actions/reaction_deck';
|
||||
import { hydrateStore } from 'mastodon/actions/store';
|
||||
import { connectUserStream } from 'mastodon/actions/streaming';
|
||||
import ErrorBoundary from 'mastodon/components/error_boundary';
|
||||
|
@ -29,6 +30,7 @@ const hydrateAction = hydrateStore(initialState);
|
|||
store.dispatch(hydrateAction);
|
||||
if (initialState.meta.me) {
|
||||
store.dispatch(fetchCustomEmojis());
|
||||
store.dispatch(fetchReactionDeck());
|
||||
}
|
||||
|
||||
const createIdentityContext = state => ({
|
||||
|
|
|
@ -11,6 +11,7 @@ const messages = defineMessages({
|
|||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||
pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
|
||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||
reaction_deck: { id: 'navigation_bar.reaction_deck', defaultMessage: 'Reaction deck' },
|
||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||
emoji_reactions: { id: 'navigation_bar.emoji_reactions', defaultMessage: 'Stamps' },
|
||||
|
@ -44,6 +45,7 @@ class ActionBar extends PureComponent {
|
|||
|
||||
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.reaction_deck), to: '/reaction_deck' });
|
||||
menu.push({ text: intl.formatMessage(messages.pins), to: '/pinned' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
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 EmojiPickerDropdown from '../components/emoji_picker_dropdown';
|
||||
|
||||
|
||||
|
||||
const perLine = 8;
|
||||
const lines = 2;
|
||||
|
||||
|
@ -28,21 +33,43 @@ const DEFAULTS = [
|
|||
'ok_hand',
|
||||
];
|
||||
|
||||
const getFrequentlyUsedEmojis = createSelector([
|
||||
state => state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()),
|
||||
], emojiCounters => {
|
||||
let emojis = emojiCounters
|
||||
.keySeq()
|
||||
.sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b))
|
||||
.reverse()
|
||||
.slice(0, perLine * lines)
|
||||
.toArray();
|
||||
const RECENT_SIZE = DEFAULTS.length;
|
||||
const DECK_SIZE = 16;
|
||||
|
||||
if (emojis.length < DEFAULTS.length) {
|
||||
let uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji));
|
||||
emojis = emojis.concat(uniqueDefaults.slice(0, DEFAULTS.length - emojis.length));
|
||||
const getFrequentlyUsedEmojis = createSelector([
|
||||
state => { return {
|
||||
emojiCounters: state.getIn(['settings', 'frequentlyUsedEmojis'], ImmutableMap()),
|
||||
reactionDeck: state.get('reaction_deck', ImmutableList()),
|
||||
}; },
|
||||
], data => {
|
||||
const { emojiCounters, reactionDeck } = data;
|
||||
let deckEmojis = reactionDeck
|
||||
.toArray()
|
||||
.map((e) => e.get('emoji'))
|
||||
.filter((e) => e)
|
||||
.map((e) => shortCodes[e] || e);
|
||||
deckEmojis = [...new Set(deckEmojis)];
|
||||
|
||||
let emojis;
|
||||
if (!hideRecentEmojis) {
|
||||
emojis = emojiCounters
|
||||
.keySeq()
|
||||
.filter((ee) => deckEmojis.indexOf(ee) < 0)
|
||||
.sort((a, b) => emojiCounters.get(a) - emojiCounters.get(b))
|
||||
.reverse()
|
||||
.slice(0, perLine * lines)
|
||||
.toArray();
|
||||
|
||||
if (emojis.length < RECENT_SIZE) {
|
||||
let uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji));
|
||||
emojis = emojis.concat(uniqueDefaults.slice(0, RECENT_SIZE - emojis.length));
|
||||
}
|
||||
} else {
|
||||
emojis = [];
|
||||
}
|
||||
|
||||
emojis = deckEmojis.slice(0, DECK_SIZE).concat(emojis);
|
||||
|
||||
return emojis;
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { unicodeToUnifiedName } from './unicode_to_unified_name';
|
|||
const [ shortCodesToEmojiData, skins, categories, short_names ] = emojiCompressed;
|
||||
|
||||
const emojis = {};
|
||||
const shortCodes = {};
|
||||
|
||||
// decompress
|
||||
Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
|
||||
|
@ -33,10 +34,12 @@ Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
|
|||
short_names,
|
||||
unified,
|
||||
};
|
||||
shortCodes[native] = shortCode;
|
||||
});
|
||||
|
||||
export {
|
||||
emojis,
|
||||
shortCodes,
|
||||
skins,
|
||||
categories,
|
||||
short_names,
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { 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 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)),
|
||||
});
|
||||
|
||||
class ReactionEmoji extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
emoji: PropTypes.string,
|
||||
emojiMap: ImmutablePropTypes.map.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
emoji: '',
|
||||
};
|
||||
|
||||
render () {
|
||||
const { emojiMap, emoji, onChange } = this.props;
|
||||
|
||||
let content = null;
|
||||
|
||||
if (emojiMap.get(emoji)) {
|
||||
const filename = autoPlayGif ? emojiMap.getIn([emoji, 'url']) : emojiMap.getIn([emoji, 'static_url']);
|
||||
const shortCode = `:${emoji}:`;
|
||||
|
||||
content = (
|
||||
<img
|
||||
draggable='false'
|
||||
className='emojione custom-emoji'
|
||||
alt={shortCode}
|
||||
title={shortCode}
|
||||
src={filename}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
const html = { __html: emojify(emoji) };
|
||||
content = (
|
||||
<span dangerouslySetInnerHTML={html} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='reaction_deck__emoji'>
|
||||
<div className='reaction_deck__emoji__wrapper'>
|
||||
<EmojiPickerDropdownContainer onPickEmoji={onChange} />
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(MapStateToProps, mapDispatchToProps)(injectIntl(ReactionEmoji));
|
86
app/javascript/mastodon/features/reaction_deck/index.jsx
Normal file
86
app/javascript/mastodon/features/reaction_deck/index.jsx
Normal file
|
@ -0,0 +1,86 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
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;
|
||||
|
||||
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' },
|
||||
heading: { id: 'column.reaction_deck', defaultMessage: 'Reaction deck' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
|
||||
deck: state.get('reaction_deck'),
|
||||
emojiMap: customEmojiMap(state),
|
||||
});
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, deck, emojiMap, multiColumn } = this.props;
|
||||
|
||||
if (!deck) {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Column bindToDocument={!multiColumn}>
|
||||
<ColumnHeader
|
||||
icon='smile-o'
|
||||
title={intl.formatMessage(messages.heading)}
|
||||
multiColumn={multiColumn}
|
||||
showBackButton
|
||||
/>
|
||||
|
||||
<ScrollableList
|
||||
scrollKey='reaction_deck'
|
||||
bindToDocument={!multiColumn}
|
||||
>
|
||||
{[...Array(DECK_SIZE).keys()].map(emojiId =>
|
||||
<ReactionEmoji key={emojiId} emojiMap={emojiMap} emojiId={emojiId} />
|
||||
)}
|
||||
</ScrollableList>
|
||||
|
||||
<Helmet>
|
||||
<meta name='robots' content='noindex' />
|
||||
</Helmet>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(ReactionDeck));
|
|
@ -62,6 +62,7 @@ import {
|
|||
Lists,
|
||||
Directory,
|
||||
Explore,
|
||||
ReactionDeck,
|
||||
Onboarding,
|
||||
About,
|
||||
PrivacyPolicy,
|
||||
|
@ -203,6 +204,8 @@ class SwitchingColumnsArea extends PureComponent {
|
|||
<WrappedRoute path='/bookmarks' component={BookmarkedStatuses} content={children} />
|
||||
<WrappedRoute path='/pinned' component={PinnedStatuses} content={children} />
|
||||
|
||||
<WrappedRoute path='/reaction_deck' component={ReactionDeck} content={children} />
|
||||
|
||||
<WrappedRoute path='/start' exact component={Onboarding} content={children} />
|
||||
<WrappedRoute path='/directory' component={Directory} content={children} />
|
||||
<WrappedRoute path={['/explore', '/search']} component={Explore} content={children} />
|
||||
|
|
|
@ -166,6 +166,10 @@ export function Onboarding () {
|
|||
return import(/* webpackChunkName: "features/onboarding" */'../../onboarding');
|
||||
}
|
||||
|
||||
export function ReactionDeck () {
|
||||
return import(/* webpackChunkName: "features/reaction_deck" */'../../reaction_deck');
|
||||
}
|
||||
|
||||
export function CompareHistoryModal () {
|
||||
return import(/*webpackChunkName: "modals/compare_history_modal" */'../components/compare_history_modal');
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
* @property {string} domain
|
||||
* @property {boolean} enable_login_privacy
|
||||
* @property {boolean=} expand_spoilers
|
||||
* @property {boolean} hide_recent_emojis
|
||||
* @property {boolean} limited_federation_mode
|
||||
* @property {string} locale
|
||||
* @property {string | null} mascot
|
||||
|
@ -115,6 +116,7 @@ export const domain = getMeta('domain');
|
|||
export const enableLoginPrivacy = getMeta('enable_login_privacy');
|
||||
export const expandSpoilers = getMeta('expand_spoilers');
|
||||
export const forceSingleColumn = !getMeta('advanced_layout');
|
||||
export const hideRecentEmojis = getMeta('hide_recent_emojis');
|
||||
export const limitedFederationMode = getMeta('limited_federation_mode');
|
||||
export const mascot = getMeta('mascot');
|
||||
export const me = getMeta('me');
|
||||
|
|
|
@ -34,6 +34,7 @@ import notifications from './notifications';
|
|||
import picture_in_picture from './picture_in_picture';
|
||||
import polls from './polls';
|
||||
import push_notifications from './push_notifications';
|
||||
import reaction_deck from './reaction_deck';
|
||||
import relationships from './relationships';
|
||||
import search from './search';
|
||||
import server from './server';
|
||||
|
@ -92,6 +93,7 @@ const reducers = {
|
|||
history,
|
||||
tags,
|
||||
followed_tags,
|
||||
reaction_deck,
|
||||
};
|
||||
|
||||
const rootReducer = combineReducers(reducers);
|
||||
|
|
13
app/javascript/mastodon/reducers/reaction_deck.js
Normal file
13
app/javascript/mastodon/reducers/reaction_deck.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { List as ImmutableList, fromJS as ConvertToImmutable } from 'immutable';
|
||||
|
||||
import { REACTION_DECK_FETCH_SUCCESS, REACTION_DECK_UPDATE_SUCCESS } from '../actions/reaction_deck';
|
||||
|
||||
const initialState = ImmutableList([]);
|
||||
|
||||
export default function reaction_deck(state = initialState, action) {
|
||||
if(action.type === REACTION_DECK_FETCH_SUCCESS || action.type === REACTION_DECK_UPDATE_SUCCESS) {
|
||||
state = ConvertToImmutable(action.emojis);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
|
@ -7544,6 +7544,26 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
.reaction_deck__emoji {
|
||||
&__wrapper {
|
||||
display: flex;
|
||||
|
||||
margin: 8px 4px;
|
||||
height: 32px;
|
||||
|
||||
.emojione {
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
margin-left: 20px;
|
||||
margin-right: 24px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.focal-point {
|
||||
position: relative;
|
||||
cursor: move;
|
||||
|
|
|
@ -23,10 +23,18 @@ module HasUserSettings
|
|||
settings['web.auto_play']
|
||||
end
|
||||
|
||||
def setting_reaction_deck
|
||||
settings['reaction_deck']
|
||||
end
|
||||
|
||||
def setting_enable_login_privacy
|
||||
settings['web.enable_login_privacy']
|
||||
end
|
||||
|
||||
def setting_hide_recent_emojis
|
||||
settings['web.hide_recent_emojis']
|
||||
end
|
||||
|
||||
def setting_default_sensitive
|
||||
settings['default_sensitive']
|
||||
end
|
||||
|
|
|
@ -25,6 +25,7 @@ class UserSettings
|
|||
setting :reject_public_unlisted_subscription, default: false
|
||||
setting :reject_unlisted_subscription, default: false
|
||||
setting :send_without_domain_blocks, default: false
|
||||
setting :reaction_deck, default: nil
|
||||
setting :stop_emoji_reaction_streaming, default: false
|
||||
setting :emoji_reaction_streaming_notify_impl2, default: false
|
||||
|
||||
|
@ -38,6 +39,7 @@ class UserSettings
|
|||
setting :disable_swiping, default: false
|
||||
setting :delete_modal, default: true
|
||||
setting :enable_login_privacy, default: false
|
||||
setting :hide_recent_emojis, default: false
|
||||
setting :reblog_modal, default: false
|
||||
setting :unfollow_modal, default: true
|
||||
setting :reduce_motion, default: false
|
||||
|
|
|
@ -44,6 +44,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
store[:display_media_expand] = object.current_account.user.setting_display_media_expand
|
||||
store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
|
||||
store[:enable_login_privacy] = object.current_account.user.setting_enable_login_privacy
|
||||
store[:hide_recent_emojis] = object.current_account.user.setting_hide_recent_emojis
|
||||
store[:reduce_motion] = object.current_account.user.setting_reduce_motion
|
||||
store[:disable_swiping] = object.current_account.user.setting_disable_swiping
|
||||
store[:advanced_layout] = object.current_account.user.setting_advanced_layout
|
||||
|
|
|
@ -85,6 +85,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
|||
max_reactions_per_account: EmojiReaction::EMOJI_REACTION_PER_ACCOUNT_LIMIT,
|
||||
},
|
||||
|
||||
reaction_deck: {
|
||||
max_items: 16,
|
||||
},
|
||||
|
||||
reactions: {
|
||||
max_reactions: EmojiReaction::EMOJI_REACTION_PER_ACCOUNT_LIMIT,
|
||||
},
|
||||
|
@ -110,6 +114,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
|||
:kmyblue_searchability,
|
||||
:searchability,
|
||||
:kmyblue_markdown,
|
||||
:kmyblue_reaction_deck,
|
||||
]
|
||||
|
||||
capabilities << :profile_search unless Chewy.enabled?
|
||||
|
|
|
@ -91,6 +91,10 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer
|
|||
max_reactions_per_account: EmojiReaction::EMOJI_REACTION_PER_ACCOUNT_LIMIT,
|
||||
},
|
||||
|
||||
reaction_deck: {
|
||||
max_items: 16,
|
||||
},
|
||||
|
||||
reactions: {
|
||||
max_reactions: EmojiReaction::EMOJI_REACTION_PER_ACCOUNT_LIMIT,
|
||||
},
|
||||
|
@ -119,6 +123,7 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer
|
|||
:kmyblue_searchability,
|
||||
:searchability,
|
||||
:kmyblue_markdown,
|
||||
:kmyblue_reaction_deck,
|
||||
]
|
||||
|
||||
capabilities << :profile_search unless Chewy.enabled?
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
.fields-group
|
||||
= ff.input :'web.crop_images', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_crop_images')
|
||||
|
||||
.fields-group
|
||||
= ff.input :'web.hide_recent_emojis', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_hide_recent_emojis'), hint: false
|
||||
|
||||
.fields-group
|
||||
= ff.input :emoji_reaction_streaming_notify_impl2, as: :boolean, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_emoji_reaction_streaming_notify_impl2'), hint: I18n.t('simple_form.hints.defaults.setting_emoji_reaction_streaming_notify_impl2')
|
||||
|
||||
|
|
|
@ -225,6 +225,7 @@ en:
|
|||
setting_hide_followers_count: Hide followers count
|
||||
setting_hide_following_count: Hide following count
|
||||
setting_hide_network: Hide your social graph
|
||||
setting_hide_recent_emojis: Hide recent emojis
|
||||
setting_hide_statuses_count: Hide statuses count
|
||||
setting_noai: Set noai meta tags
|
||||
setting_noindex: Opt-out of search engine indexing
|
||||
|
|
|
@ -233,6 +233,7 @@ ja:
|
|||
setting_hide_followers_count: フォロワー数を隠す
|
||||
setting_hide_following_count: フォロー数を隠す
|
||||
setting_hide_network: 繋がりを隠す
|
||||
setting_hide_recent_emojis: 絵文字ピッカーで最近使用した絵文字を隠す(リアクションデッキのみを表示する)
|
||||
setting_hide_statuses_count: 投稿数を隠す
|
||||
setting_noai: 自分のコンテンツのAI学習利用に対して不快感を表明する
|
||||
setting_noindex: 検索エンジンによるインデックスを拒否する
|
||||
|
|
|
@ -19,6 +19,7 @@ Rails.application.routes.draw do
|
|||
/emoji_reactions
|
||||
/bookmarks
|
||||
/pinned
|
||||
/reaction_deck
|
||||
/start
|
||||
/directory
|
||||
/explore/(*any)
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace :api, format: false do
|
|||
get '/streaming/(*any)', to: 'streaming#index'
|
||||
|
||||
resources :custom_emojis, only: [:index]
|
||||
resources :reaction_deck, only: [:index, :create]
|
||||
resources :suggestions, only: [:index, :destroy]
|
||||
resources :scheduled_statuses, only: [:index, :show, :update, :destroy]
|
||||
resources :preferences, only: [:index]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue