Merge upstream!! #64 <3 <3
This commit is contained in:
25
app/javascript/mastodon/actions/bundles.js
Normal file
25
app/javascript/mastodon/actions/bundles.js
Normal file
@ -0,0 +1,25 @@
|
||||
export const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST';
|
||||
export const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS';
|
||||
export const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL';
|
||||
|
||||
export function fetchBundleRequest(skipLoading) {
|
||||
return {
|
||||
type: BUNDLE_FETCH_REQUEST,
|
||||
skipLoading,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBundleSuccess(skipLoading) {
|
||||
return {
|
||||
type: BUNDLE_FETCH_SUCCESS,
|
||||
skipLoading,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBundleFail(error, skipLoading) {
|
||||
return {
|
||||
type: BUNDLE_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading,
|
||||
};
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import Immutable from 'immutable';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { defineMessages } from 'react-intl';
|
||||
@ -124,7 +124,7 @@ export function refreshNotificationsFail(error, skipLoading) {
|
||||
|
||||
export function expandNotifications() {
|
||||
return (dispatch, getState) => {
|
||||
const items = getState().getIn(['notifications', 'items'], Immutable.List());
|
||||
const items = getState().getIn(['notifications', 'items'], ImmutableList());
|
||||
|
||||
if (getState().getIn(['notifications', 'isLoading']) || items.size === 0) {
|
||||
return;
|
||||
|
@ -1,10 +1,11 @@
|
||||
import Immutable from 'immutable';
|
||||
import { Iterable, fromJS } from 'immutable';
|
||||
|
||||
export const STORE_HYDRATE = 'STORE_HYDRATE';
|
||||
export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
|
||||
|
||||
const convertState = rawState =>
|
||||
Immutable.fromJS(rawState, (k, v) =>
|
||||
Immutable.Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x =>
|
||||
fromJS(rawState, (k, v) =>
|
||||
Iterable.isIndexed(v) ? v.toList() : v.toMap().mapKeys(x =>
|
||||
Number.isNaN(x * 1) ? x : x * 1));
|
||||
|
||||
export function hydrateStore(rawState) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
@ -66,13 +66,13 @@ export function refreshTimelineRequest(timeline, skipLoading) {
|
||||
|
||||
export function refreshTimeline(timelineId, path, params = {}) {
|
||||
return function (dispatch, getState) {
|
||||
const timeline = getState().getIn(['timelines', timelineId], Immutable.Map());
|
||||
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
|
||||
|
||||
if (timeline.get('isLoading') || timeline.get('online')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ids = timeline.get('items', Immutable.List());
|
||||
const ids = timeline.get('items', ImmutableList());
|
||||
const newestId = ids.size > 0 ? ids.first() : null;
|
||||
|
||||
let skipLoading = timeline.get('loaded');
|
||||
@ -111,8 +111,8 @@ export function refreshTimelineFail(timeline, error, skipLoading) {
|
||||
|
||||
export function expandTimeline(timelineId, path, params = {}) {
|
||||
return (dispatch, getState) => {
|
||||
const timeline = getState().getIn(['timelines', timelineId], Immutable.Map());
|
||||
const ids = timeline.get('items', Immutable.List());
|
||||
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
|
||||
const ids = timeline.get('items', ImmutableList());
|
||||
|
||||
if (timeline.get('isLoading') || ids.size === 0) {
|
||||
return;
|
||||
|
@ -10,7 +10,7 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
title: PropTypes.node.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
active: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
|
@ -14,6 +14,7 @@ export default class DropdownMenu extends React.PureComponent {
|
||||
size: PropTypes.number.isRequired,
|
||||
direction: PropTypes.string,
|
||||
ariaLabel: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@ -68,9 +69,19 @@ export default class DropdownMenu extends React.PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { icon, items, size, direction, ariaLabel } = this.props;
|
||||
const { expanded } = this.state;
|
||||
const { icon, items, size, direction, ariaLabel, disabled } = this.props;
|
||||
const { expanded } = this.state;
|
||||
const directionClass = (direction === 'left') ? 'dropdown__left' : 'dropdown__right';
|
||||
const iconStyle = { fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` };
|
||||
const iconClassname = `fa fa-fw fa-${icon} dropdown__icon`;
|
||||
|
||||
if (disabled) {
|
||||
return (
|
||||
<div className='icon-button disabled' style={iconStyle} aria-label={ariaLabel}>
|
||||
<i className={iconClassname} aria-hidden />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const dropdownItems = expanded && (
|
||||
<ul className='dropdown__content-list'>
|
||||
@ -80,8 +91,8 @@ export default class DropdownMenu extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<Dropdown ref={this.setRef} onShow={this.handleShow} onHide={this.handleHide}>
|
||||
<DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }} aria-label={ariaLabel}>
|
||||
<i className={`fa fa-fw fa-${icon} dropdown__icon`} aria-hidden />
|
||||
<DropdownTrigger className='icon-button' style={iconStyle} aria-label={ariaLabel}>
|
||||
<i className={iconClassname} aria-hidden />
|
||||
</DropdownTrigger>
|
||||
|
||||
<DropdownContent className={directionClass}>
|
||||
|
@ -15,7 +15,7 @@ export default class Permalink extends React.PureComponent {
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(this.props.to);
|
||||
}
|
||||
@ -25,7 +25,7 @@ export default class Permalink extends React.PureComponent {
|
||||
const { href, children, className, ...other } = this.props;
|
||||
|
||||
return (
|
||||
<a href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
|
@ -22,14 +22,15 @@ import { getLocale } from '../locales';
|
||||
const { localeData, messages } = getLocale();
|
||||
addLocaleData(localeData);
|
||||
|
||||
const store = configureStore();
|
||||
export const store = configureStore();
|
||||
const initialState = JSON.parse(document.getElementById('initial-state').textContent);
|
||||
try {
|
||||
initialState.local_settings = JSON.parse(localStorage.getItem('mastodon-settings'));
|
||||
} catch (e) {
|
||||
initialState.local_settings = {};
|
||||
}
|
||||
store.dispatch(hydrateStore(initialState));
|
||||
const hydrateAction = hydrateStore(initialState);
|
||||
store.dispatch(hydrateAction);
|
||||
|
||||
export default class Mastodon extends React.PureComponent {
|
||||
|
||||
|
39
app/javascript/mastodon/containers/timeline_container.js
Normal file
39
app/javascript/mastodon/containers/timeline_container.js
Normal file
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import configureStore from '../store/configureStore';
|
||||
import { hydrateStore } from '../actions/store';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import { getLocale } from '../locales';
|
||||
import PublicTimeline from '../features/standalone/public_timeline';
|
||||
|
||||
const { localeData, messages } = getLocale();
|
||||
addLocaleData(localeData);
|
||||
|
||||
const store = configureStore();
|
||||
const initialStateContainer = document.getElementById('initial-state');
|
||||
|
||||
if (initialStateContainer !== null) {
|
||||
const initialState = JSON.parse(initialStateContainer.textContent);
|
||||
store.dispatch(hydrateStore(initialState));
|
||||
}
|
||||
|
||||
export default class TimelineContainer extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
locale: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { locale } = this.props;
|
||||
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
<Provider store={store}>
|
||||
<PublicTimeline />
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,35 +1,55 @@
|
||||
import emojione from 'emojione';
|
||||
import Trie from 'substring-trie';
|
||||
|
||||
const toImage = str => shortnameToImage(unicodeToImage(str));
|
||||
const mappedUnicode = emojione.mapUnicodeToShort();
|
||||
const trie = new Trie(Object.keys(emojione.jsEscapeMap));
|
||||
|
||||
const unicodeToImage = str => {
|
||||
const mappedUnicode = emojione.mapUnicodeToShort();
|
||||
|
||||
return str.replace(emojione.regUnicode, unicodeChar => {
|
||||
if (typeof unicodeChar === 'undefined' || unicodeChar === '' || !(unicodeChar in emojione.jsEscapeMap)) {
|
||||
return unicodeChar;
|
||||
function emojify(str) {
|
||||
// This walks through the string from start to end, ignoring any tags (<p>, <br>, etc.)
|
||||
// and replacing valid shortnames like :smile: and :wink: as well as unicode strings
|
||||
// that _aren't_ within tags with an <img> version.
|
||||
// The goal is to be the same as an emojione.regShortNames/regUnicode replacement, but faster.
|
||||
let i = -1;
|
||||
let insideTag = false;
|
||||
let insideShortname = false;
|
||||
let shortnameStartIndex = -1;
|
||||
let match;
|
||||
while (++i < str.length) {
|
||||
const char = str.charAt(i);
|
||||
if (insideShortname && char === ':') {
|
||||
const shortname = str.substring(shortnameStartIndex, i + 1);
|
||||
if (shortname in emojione.emojioneList) {
|
||||
const unicode = emojione.emojioneList[shortname].unicode[emojione.emojioneList[shortname].unicode.length - 1];
|
||||
const alt = emojione.convert(unicode.toUpperCase());
|
||||
const replacement = `<img draggable="false" class="emojione" alt="${alt}" title="${shortname}" src="/emoji/${unicode}.svg" />`;
|
||||
str = str.substring(0, shortnameStartIndex) + replacement + str.substring(i + 1);
|
||||
i += (replacement.length - shortname.length - 1); // jump ahead the length we've added to the string
|
||||
} else {
|
||||
i--; // stray colon, try again
|
||||
}
|
||||
insideShortname = false;
|
||||
} else if (insideTag && char === '>') {
|
||||
insideTag = false;
|
||||
} else if (char === '<') {
|
||||
insideTag = true;
|
||||
insideShortname = false;
|
||||
} else if (!insideTag && char === ':') {
|
||||
insideShortname = true;
|
||||
shortnameStartIndex = i;
|
||||
} else if (!insideTag && (match = trie.search(str.substring(i)))) {
|
||||
const unicodeStr = match;
|
||||
if (unicodeStr in emojione.jsEscapeMap) {
|
||||
const unicode = emojione.jsEscapeMap[unicodeStr];
|
||||
const short = mappedUnicode[unicode];
|
||||
const filename = emojione.emojioneList[short].fname;
|
||||
const alt = emojione.convert(unicode.toUpperCase());
|
||||
const replacement = `<img draggable="false" class="emojione" alt="${alt}" title="${short}" src="/emoji/${filename}.svg" />`;
|
||||
str = str.substring(0, i) + replacement + str.substring(i + unicodeStr.length);
|
||||
i += (replacement.length - unicodeStr.length); // jump ahead the length we've added to the string
|
||||
}
|
||||
}
|
||||
|
||||
const unicode = emojione.jsEscapeMap[unicodeChar];
|
||||
const short = mappedUnicode[unicode];
|
||||
const filename = emojione.emojioneList[short].fname;
|
||||
const alt = emojione.convert(unicode.toUpperCase());
|
||||
|
||||
return `<img draggable="false" class="emojione" alt="${alt}" title="${short}" src="/emoji/${filename}.svg" />`;
|
||||
});
|
||||
};
|
||||
|
||||
const shortnameToImage = str => str.replace(emojione.regShortNames, shortname => {
|
||||
if (typeof shortname === 'undefined' || shortname === '' || !(shortname in emojione.emojioneList)) {
|
||||
return shortname;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const unicode = emojione.emojioneList[shortname].unicode[emojione.emojioneList[shortname].unicode.length - 1];
|
||||
const alt = emojione.convert(unicode.toUpperCase());
|
||||
|
||||
return `<img draggable="false" class="emojione" alt="${alt}" title="${shortname}" src="/emoji/${unicode}.svg" />`;
|
||||
});
|
||||
|
||||
export default function emojify(text) {
|
||||
return toImage(text);
|
||||
};
|
||||
export default emojify;
|
||||
|
@ -9,11 +9,11 @@ import LoadingIndicator from '../../components/loading_indicator';
|
||||
import Column from '../ui/components/column';
|
||||
import HeaderContainer from './containers/header_container';
|
||||
import ColumnBackButton from '../../components/column_back_button';
|
||||
import Immutable from 'immutable';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
statusIds: state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'items'], Immutable.List()),
|
||||
statusIds: state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'items'], ImmutableList()),
|
||||
isLoading: state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'isLoading']),
|
||||
hasMore: !!state.getIn(['timelines', `account:${Number(props.params.accountId)}`, 'next']),
|
||||
me: state.getIn(['meta', 'me']),
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
||||
|
||||
const messages = defineMessages({
|
||||
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
|
||||
@ -50,7 +51,7 @@ export default class EmojiPickerDropdown extends React.PureComponent {
|
||||
this.setState({ active: true });
|
||||
if (!EmojiPicker) {
|
||||
this.setState({ loading: true });
|
||||
import(/* webpackChunkName: "emojione_picker" */ 'emojione-picker').then(TheEmojiPicker => {
|
||||
EmojiPickerAsync().then(TheEmojiPicker => {
|
||||
EmojiPicker = TheEmojiPicker.default;
|
||||
this.setState({ loading: false });
|
||||
}).catch(() => {
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import ComposeFormContainer from './containers/compose_form_container';
|
||||
import NavigationContainer from './containers/navigation_container';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
import { mountCompose, unmountCompose } from '../../actions/compose';
|
||||
import { openModal } from '../../actions/modal';
|
||||
@ -15,6 +16,8 @@ import SearchResultsContainer from './containers/search_results_container';
|
||||
|
||||
const messages = defineMessages({
|
||||
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||
public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
||||
community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
||||
settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
|
||||
@ -22,6 +25,7 @@ const messages = defineMessages({
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
columns: state.getIn(['settings', 'columns']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
});
|
||||
|
||||
@ -31,6 +35,7 @@ export default class Compose extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
columns: ImmutablePropTypes.list.isRequired,
|
||||
multiColumn: PropTypes.bool,
|
||||
showSearch: PropTypes.bool,
|
||||
intl: PropTypes.object.isRequired,
|
||||
@ -60,11 +65,22 @@ export default class Compose extends React.PureComponent {
|
||||
let header = '';
|
||||
|
||||
if (multiColumn) {
|
||||
const { columns } = this.props;
|
||||
header = (
|
||||
<div className='drawer__header'>
|
||||
<Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)}><i role='img' aria-label={intl.formatMessage(messages.start)} className='fa fa-fw fa-asterisk' /></Link>
|
||||
<Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)}><i role='img' aria-label={intl.formatMessage(messages.community)} className='fa fa-fw fa-users' /></Link>
|
||||
<Link to='/timelines/public' className='drawer__tab' title={intl.formatMessage(messages.public)}><i role='img' aria-label={intl.formatMessage(messages.public)} className='fa fa-fw fa-globe' /></Link>
|
||||
{!columns.some(column => column.get('id') === 'HOME') && (
|
||||
<Link to='/timelines/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)}><i role='img' className='fa fa-fw fa-home' aria-label={intl.formatMessage(messages.home_timeline)} /></Link>
|
||||
)}
|
||||
{!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
|
||||
<Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)}><i role='img' className='fa fa-fw fa-bell' aria-label={intl.formatMessage(messages.notifications)} /></Link>
|
||||
)}
|
||||
{!columns.some(column => column.get('id') === 'COMMUNITY') && (
|
||||
<Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)}><i role='img' aria-label={intl.formatMessage(messages.community)} className='fa fa-fw fa-users' /></Link>
|
||||
)}
|
||||
{!columns.some(column => column.get('id') === 'PUBLIC') && (
|
||||
<Link to='/timelines/public' className='drawer__tab' title={intl.formatMessage(messages.public)}><i role='img' aria-label={intl.formatMessage(messages.public)} className='fa fa-fw fa-globe' /></Link>
|
||||
)}
|
||||
<a onClick={this.openSettings} role='button' tabIndex='0' className='drawer__tab' title={intl.formatMessage(messages.settings)}><i role='img' aria-label={intl.formatMessage(messages.settings)} className='fa fa-fw fa-cogs' /></a>
|
||||
<a href='/auth/sign_out' className='drawer__tab' data-method='delete' title={intl.formatMessage(messages.logout)}><i role='img' aria-label={intl.formatMessage(messages.logout)} className='fa fa-fw fa-sign-out' /></a>
|
||||
</div>
|
||||
|
@ -11,7 +11,7 @@ import { ScrollContainer } from 'react-router-scroll';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||
import { createSelector } from 'reselect';
|
||||
import Immutable from 'immutable';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import LoadMore from '../../components/load_more';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
@ -20,7 +20,7 @@ const messages = defineMessages({
|
||||
});
|
||||
|
||||
const getNotifications = createSelector([
|
||||
state => Immutable.List(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
|
||||
state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
|
||||
state => state.getIn(['notifications', 'items']),
|
||||
], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type'))));
|
||||
|
||||
@ -122,7 +122,7 @@ export default class Notifications extends React.PureComponent {
|
||||
let unread = '';
|
||||
let scrollContainer = '';
|
||||
|
||||
if (!isLoading && notifications.size > 0 && hasMore) {
|
||||
if (!isLoading && hasMore) {
|
||||
loadMore = <LoadMore onClick={this.handleLoadMore} />;
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ export default class Notifications extends React.PureComponent {
|
||||
|
||||
if (isLoading && this.scrollableArea) {
|
||||
scrollableArea = this.scrollableArea;
|
||||
} else if (notifications.size > 0) {
|
||||
} else if (notifications.size > 0 || hasMore) {
|
||||
scrollableArea = (
|
||||
<div className='scrollable' onScroll={this.handleScroll} ref={this.setRef}>
|
||||
{unread}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { connect } from 'react-redux';
|
||||
import StatusCheckBox from '../components/status_check_box';
|
||||
import { toggleStatusReport } from '../../../actions/reports';
|
||||
import Immutable from 'immutable';
|
||||
import { Set as ImmutableSet } from 'immutable';
|
||||
|
||||
const mapStateToProps = (state, { id }) => ({
|
||||
status: state.getIn(['statuses', id]),
|
||||
checked: state.getIn(['reports', 'new', 'status_ids'], Immutable.Set()).includes(id),
|
||||
checked: state.getIn(['reports', 'new', 'status_ids'], ImmutableSet()).includes(id),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { id }) => ({
|
||||
|
@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import StatusListContainer from '../../ui/containers/status_list_container';
|
||||
import {
|
||||
refreshPublicTimeline,
|
||||
expandPublicTimeline,
|
||||
} from '../../../actions/timelines';
|
||||
import Column from '../../../components/column';
|
||||
import ColumnHeader from '../../../components/column_header';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'standalone.public_title', defaultMessage: 'A look inside...' },
|
||||
});
|
||||
|
||||
@connect()
|
||||
@injectIntl
|
||||
export default class PublicTimeline extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(refreshPublicTimeline());
|
||||
|
||||
this.polling = setInterval(() => {
|
||||
dispatch(refreshPublicTimeline());
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (typeof this.polling !== 'undefined') {
|
||||
clearInterval(this.polling);
|
||||
this.polling = null;
|
||||
}
|
||||
}
|
||||
|
||||
handleLoadMore = () => {
|
||||
this.props.dispatch(expandPublicTimeline());
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<Column ref={this.setRef}>
|
||||
<ColumnHeader
|
||||
icon='globe'
|
||||
title={intl.formatMessage(messages.title)}
|
||||
onClick={this.handleHeaderClick}
|
||||
/>
|
||||
|
||||
<StatusListContainer
|
||||
timelineId='public'
|
||||
loadMore={this.handleLoadMore}
|
||||
scrollKey='standalone_public_timeline'
|
||||
trackScroll={false}
|
||||
/>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
101
app/javascript/mastodon/features/ui/components/bundle.js
Normal file
101
app/javascript/mastodon/features/ui/components/bundle.js
Normal file
@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const emptyComponent = () => null;
|
||||
const noop = () => { };
|
||||
|
||||
class Bundle extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
fetchComponent: PropTypes.func.isRequired,
|
||||
loading: PropTypes.func,
|
||||
error: PropTypes.func,
|
||||
children: PropTypes.func.isRequired,
|
||||
renderDelay: PropTypes.number,
|
||||
onFetch: PropTypes.func,
|
||||
onFetchSuccess: PropTypes.func,
|
||||
onFetchFail: PropTypes.func,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
loading: emptyComponent,
|
||||
error: emptyComponent,
|
||||
renderDelay: 0,
|
||||
onFetch: noop,
|
||||
onFetchSuccess: noop,
|
||||
onFetchFail: noop,
|
||||
}
|
||||
|
||||
static cache = {}
|
||||
|
||||
state = {
|
||||
mod: undefined,
|
||||
forceRender: false,
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.load(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.fetchComponent !== this.props.fetchComponent) {
|
||||
this.load(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
load = (props) => {
|
||||
const { fetchComponent, onFetch, onFetchSuccess, onFetchFail, renderDelay } = props || this.props;
|
||||
|
||||
this.setState({ mod: undefined });
|
||||
onFetch();
|
||||
|
||||
if (renderDelay !== 0) {
|
||||
this.timestamp = new Date();
|
||||
this.timeout = setTimeout(() => this.setState({ forceRender: true }), renderDelay);
|
||||
}
|
||||
|
||||
if (Bundle.cache[fetchComponent.name]) {
|
||||
const mod = Bundle.cache[fetchComponent.name];
|
||||
|
||||
this.setState({ mod: mod.default });
|
||||
onFetchSuccess();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return fetchComponent()
|
||||
.then((mod) => {
|
||||
Bundle.cache[fetchComponent.name] = mod;
|
||||
this.setState({ mod: mod.default });
|
||||
onFetchSuccess();
|
||||
})
|
||||
.catch((error) => {
|
||||
this.setState({ mod: null });
|
||||
onFetchFail(error);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading: Loading, error: Error, children, renderDelay } = this.props;
|
||||
const { mod, forceRender } = this.state;
|
||||
const elapsed = this.timestamp ? (new Date() - this.timestamp) : renderDelay;
|
||||
|
||||
if (mod === undefined) {
|
||||
return (elapsed >= renderDelay || forceRender) ? <Loading /> : null;
|
||||
}
|
||||
|
||||
if (mod === null) {
|
||||
return <Error onRetry={this.load} />;
|
||||
}
|
||||
|
||||
return children(mod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Bundle;
|
@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import Column from './column';
|
||||
import ColumnHeader from './column_header';
|
||||
import ColumnBackButtonSlim from '../../../components/column_back_button_slim';
|
||||
import IconButton from '../../../components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'bundle_column_error.title', defaultMessage: 'Network error' },
|
||||
body: { id: 'bundle_column_error.body', defaultMessage: 'Something went wrong while loading this component.' },
|
||||
retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' },
|
||||
});
|
||||
|
||||
class BundleColumnError extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
onRetry: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
this.props.onRetry();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl: { formatMessage } } = this.props;
|
||||
|
||||
return (
|
||||
<Column>
|
||||
<ColumnHeader icon='exclamation-circle' type={formatMessage(messages.title)} />
|
||||
<ColumnBackButtonSlim />
|
||||
<div className='error-column'>
|
||||
<IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
|
||||
{formatMessage(messages.body)}
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(BundleColumnError);
|
@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import IconButton from '../../../components/icon_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
|
||||
retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' },
|
||||
close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
|
||||
});
|
||||
|
||||
class BundleModalError extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
onRetry: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
this.props.onRetry();
|
||||
}
|
||||
|
||||
render () {
|
||||
const { onClose, intl: { formatMessage } } = this.props;
|
||||
|
||||
// Keep the markup in sync with <ModalLoading />
|
||||
// (make sure they have the same dimensions)
|
||||
return (
|
||||
<div className='modal-root__modal error-modal'>
|
||||
<div className='error-modal__body'>
|
||||
<IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} />
|
||||
{formatMessage(messages.error)}
|
||||
</div>
|
||||
|
||||
<div className='error-modal__footer'>
|
||||
<div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className='error-modal__nav onboarding-modal__skip'
|
||||
>
|
||||
{formatMessage(messages.close)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default injectIntl(BundleModalError);
|
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Column from '../../../components/column';
|
||||
import ColumnHeader from '../../../components/column_header';
|
||||
|
||||
const ColumnLoading = ({ title = '', icon = ' ' }) => (
|
||||
<Column>
|
||||
<ColumnHeader icon={icon} title={title} multiColumn={false} />
|
||||
<div className='scrollable' />
|
||||
</Column>
|
||||
);
|
||||
|
||||
ColumnLoading.propTypes = {
|
||||
title: PropTypes.node,
|
||||
icon: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ColumnLoading;
|
@ -2,14 +2,14 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ReactSwipeable from 'react-swipeable';
|
||||
import HomeTimeline from '../../home_timeline';
|
||||
import Notifications from '../../notifications';
|
||||
import PublicTimeline from '../../public_timeline';
|
||||
import CommunityTimeline from '../../community_timeline';
|
||||
import HashtagTimeline from '../../hashtag_timeline';
|
||||
import Compose from '../../compose';
|
||||
import { getPreviousLink, getNextLink } from './tabs_bar';
|
||||
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
import { links, getIndex, getLink } from './tabs_bar';
|
||||
|
||||
import BundleContainer from '../containers/bundle_container';
|
||||
import ColumnLoading from './column_loading';
|
||||
import BundleColumnError from './bundle_column_error';
|
||||
import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline } from '../../ui/util/async-components';
|
||||
|
||||
const componentMap = {
|
||||
'COMPOSE': Compose,
|
||||
@ -32,39 +32,61 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
handleRightSwipe = () => {
|
||||
const previousLink = getPreviousLink(this.context.router.history.location.pathname);
|
||||
|
||||
if (previousLink) {
|
||||
this.context.router.history.push(previousLink);
|
||||
}
|
||||
handleSwipe = (index) => {
|
||||
window.requestAnimationFrame(() => {
|
||||
window.requestAnimationFrame(() => {
|
||||
this.context.router.history.push(getLink(index));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleLeftSwipe = () => {
|
||||
const previousLink = getNextLink(this.context.router.history.location.pathname);
|
||||
renderView = (link, index) => {
|
||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||
const title = link.props.children[1] && React.cloneElement(link.props.children[1]);
|
||||
const icon = (link.props.children[0] || link.props.children).props.className.split(' ')[2].split('-')[1];
|
||||
|
||||
if (previousLink) {
|
||||
this.context.router.history.push(previousLink);
|
||||
}
|
||||
};
|
||||
const view = (index === columnIndex) ?
|
||||
React.cloneElement(this.props.children) :
|
||||
<ColumnLoading title={title} icon={icon} />;
|
||||
|
||||
return (
|
||||
<div className='columns-area' key={index}>
|
||||
{view}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderLoading = () => {
|
||||
return <ColumnLoading />;
|
||||
}
|
||||
|
||||
renderError = (props) => {
|
||||
return <BundleColumnError {...props} />;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { columns, children, singleColumn } = this.props;
|
||||
|
||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||
|
||||
if (singleColumn) {
|
||||
return (
|
||||
<ReactSwipeable onSwipedLeft={this.handleLeftSwipe} onSwipedRight={this.handleRightSwipe} className='columns-area'>
|
||||
{children}
|
||||
</ReactSwipeable>
|
||||
);
|
||||
return columnIndex !== -1 ? (
|
||||
<ReactSwipeableViews index={columnIndex} onChangeIndex={this.handleSwipe} animateTransitions={false} style={{ height: '100%' }}>
|
||||
{links.map(this.renderView)}
|
||||
</ReactSwipeableViews>
|
||||
) : <div className='columns-area'>{children}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='columns-area'>
|
||||
{columns.map(column => {
|
||||
const SpecificComponent = componentMap[column.get('id')];
|
||||
const params = column.get('params', null) === null ? null : column.get('params').toJS();
|
||||
return <SpecificComponent key={column.get('uuid')} columnId={column.get('uuid')} params={params} multiColumn />;
|
||||
|
||||
return (
|
||||
<BundleContainer key={column.get('uuid')} fetchComponent={componentMap[column.get('id')]} loading={this.renderLoading} error={this.renderError}>
|
||||
{SpecificComponent => <SpecificComponent columnId={column.get('uuid')} params={params} multiColumn />}
|
||||
</BundleContainer>
|
||||
);
|
||||
})}
|
||||
|
||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
|
||||
|
@ -8,12 +8,14 @@ export default class ImageLoader extends React.PureComponent {
|
||||
alt: PropTypes.string,
|
||||
src: PropTypes.string.isRequired,
|
||||
previewSrc: PropTypes.string.isRequired,
|
||||
width: PropTypes.number.isRequired,
|
||||
height: PropTypes.number.isRequired,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
alt: '',
|
||||
width: null,
|
||||
height: null,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -46,8 +48,8 @@ export default class ImageLoader extends React.PureComponent {
|
||||
this.setState({ loading: true, error: false });
|
||||
Promise.all([
|
||||
this.loadPreviewCanvas(props),
|
||||
this.loadOriginalImage(props),
|
||||
])
|
||||
this.hasSize() && this.loadOriginalImage(props),
|
||||
].filter(Boolean))
|
||||
.then(() => {
|
||||
this.setState({ loading: false, error: false });
|
||||
this.clearPreviewCanvas();
|
||||
@ -106,6 +108,11 @@ export default class ImageLoader extends React.PureComponent {
|
||||
this.removers = [];
|
||||
}
|
||||
|
||||
hasSize () {
|
||||
const { width, height } = this.props;
|
||||
return typeof width === 'number' && typeof height === 'number';
|
||||
}
|
||||
|
||||
setCanvasRef = c => {
|
||||
this.canvas = c;
|
||||
}
|
||||
@ -116,6 +123,7 @@ export default class ImageLoader extends React.PureComponent {
|
||||
|
||||
const className = classNames('image-loader', {
|
||||
'image-loader--loading': loading,
|
||||
'image-loader--amorphous': !this.hasSize(),
|
||||
});
|
||||
|
||||
return (
|
||||
@ -125,6 +133,7 @@ export default class ImageLoader extends React.PureComponent {
|
||||
width={width}
|
||||
height={height}
|
||||
ref={this.setCanvasRef}
|
||||
style={{ opacity: loading ? 1 : 0 }}
|
||||
/>
|
||||
|
||||
{!loading && (
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import ReactSwipeable from 'react-swipeable';
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import ExtendedVideoPlayer from '../../../components/extended_video_player';
|
||||
@ -26,12 +26,16 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
index: null,
|
||||
};
|
||||
|
||||
handleSwipe = (index) => {
|
||||
this.setState({ index: (index) % this.props.media.size });
|
||||
}
|
||||
|
||||
handleNextClick = () => {
|
||||
this.setState({ index: (this.getIndex() + 1) % this.props.media.size });
|
||||
}
|
||||
|
||||
handlePrevClick = () => {
|
||||
this.setState({ index: (this.getIndex() - 1) % this.props.media.size });
|
||||
this.setState({ index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size });
|
||||
}
|
||||
|
||||
handleKeyUp = (e) => {
|
||||
@ -74,7 +78,12 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
if (attachment.get('type') === 'image') {
|
||||
content = <ImageLoader previewSrc={attachment.get('preview_url')} src={url} width={attachment.getIn(['meta', 'original', 'width'])} height={attachment.getIn(['meta', 'original', 'height'])} />;
|
||||
content = media.map((image) => {
|
||||
const width = image.getIn(['meta', 'original', 'width']) || null;
|
||||
const height = image.getIn(['meta', 'original', 'height']) || null;
|
||||
|
||||
return <ImageLoader previewSrc={image.get('preview_url')} src={image.get('url')} width={width} height={height} key={image.get('preview_url')} />;
|
||||
}).toArray();
|
||||
} else if (attachment.get('type') === 'gifv') {
|
||||
content = <ExtendedVideoPlayer src={url} muted controls={false} />;
|
||||
}
|
||||
@ -85,9 +94,9 @@ export default class MediaModal extends ImmutablePureComponent {
|
||||
|
||||
<div className='media-modal__content'>
|
||||
<IconButton className='media-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={16} />
|
||||
<ReactSwipeable onSwipedRight={this.handlePrevClick} onSwipedLeft={this.handleNextClick}>
|
||||
<ReactSwipeableViews onChangeIndex={this.handleSwipe} index={index} animateHeight>
|
||||
{content}
|
||||
</ReactSwipeable>
|
||||
</ReactSwipeableViews>
|
||||
</div>
|
||||
|
||||
{rightNav}
|
||||
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import LoadingIndicator from '../../../components/loading_indicator';
|
||||
|
||||
// Keep the markup in sync with <BundleModalError />
|
||||
// (make sure they have the same dimensions)
|
||||
const ModalLoading = () => (
|
||||
<div className='modal-root__modal error-modal'>
|
||||
<div className='error-modal__body'>
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
<div className='error-modal__footer'>
|
||||
<div>
|
||||
<button className='error-modal__nav onboarding-modal__skip' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ModalLoading;
|
@ -1,14 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import MediaModal from './media_modal';
|
||||
import OnboardingModal from './onboarding_modal';
|
||||
import VideoModal from './video_modal';
|
||||
import BoostModal from './boost_modal';
|
||||
import ConfirmationModal from './confirmation_modal';
|
||||
import ReportModal from './report_modal';
|
||||
import SettingsContainer from '../../../../glitch/containers/settings';
|
||||
import TransitionMotion from 'react-motion/lib/TransitionMotion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import BundleContainer from '../containers/bundle_container';
|
||||
import BundleModalError from './bundle_modal_error';
|
||||
import ModalLoading from './modal_loading';
|
||||
import {
|
||||
MediaModal,
|
||||
OnboardingModal,
|
||||
VideoModal,
|
||||
BoostModal,
|
||||
ConfirmationModal,
|
||||
ReportModal,
|
||||
SettingsModal,
|
||||
} from '../../../features/ui/util/async-components';
|
||||
|
||||
const MODAL_COMPONENTS = {
|
||||
'MEDIA': MediaModal,
|
||||
@ -17,7 +22,7 @@ const MODAL_COMPONENTS = {
|
||||
'BOOST': BoostModal,
|
||||
'CONFIRM': ConfirmationModal,
|
||||
'REPORT': ReportModal,
|
||||
'SETTINGS': SettingsContainer,
|
||||
'SETTINGS': SettingsModal,
|
||||
};
|
||||
|
||||
export default class ModalRoot extends React.PureComponent {
|
||||
@ -51,6 +56,22 @@ export default class ModalRoot extends React.PureComponent {
|
||||
return { opacity: spring(0), scale: spring(0.98) };
|
||||
}
|
||||
|
||||
renderModal = (SpecificComponent) => {
|
||||
const { props, onClose } = this.props;
|
||||
|
||||
return <SpecificComponent {...props} onClose={onClose} />;
|
||||
}
|
||||
|
||||
renderLoading = () => {
|
||||
return <ModalLoading />;
|
||||
}
|
||||
|
||||
renderError = (props) => {
|
||||
const { onClose } = this.props;
|
||||
|
||||
return <BundleModalError {...props} onClose={onClose} />;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { type, props, onClose } = this.props;
|
||||
const visible = !!type;
|
||||
@ -72,18 +93,14 @@ export default class ModalRoot extends React.PureComponent {
|
||||
>
|
||||
{interpolatedStyles =>
|
||||
<div className='modal-root'>
|
||||
{interpolatedStyles.map(({ key, data: { type, props }, style }) => {
|
||||
const SpecificComponent = MODAL_COMPONENTS[type];
|
||||
|
||||
return (
|
||||
<div key={key} style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div role='presentation' className='modal-root__overlay' style={{ opacity: style.opacity }} onClick={onClose} />
|
||||
<div className='modal-root__container' style={{ opacity: style.opacity, transform: `translateZ(0px) scale(${style.scale})` }}>
|
||||
<SpecificComponent {...props} onClose={onClose} />
|
||||
</div>
|
||||
{interpolatedStyles.map(({ key, data: { type }, style }) => (
|
||||
<div key={key} style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div role='presentation' className='modal-root__overlay' style={{ opacity: style.opacity }} onClick={onClose} />
|
||||
<div className='modal-root__container' style={{ opacity: style.opacity, transform: `translateZ(0px) scale(${style.scale})` }}>
|
||||
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading} error={this.renderError} renderDelay={200}>{this.renderModal}</BundleContainer>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
</TransitionMotion>
|
||||
|
@ -3,16 +3,14 @@ import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ReactSwipeable from 'react-swipeable';
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
import classNames from 'classnames';
|
||||
import Permalink from '../../../components/permalink';
|
||||
import TransitionMotion from 'react-motion/lib/TransitionMotion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import ComposeForm from '../../compose/components/compose_form';
|
||||
import Search from '../../compose/components/search';
|
||||
import NavigationBar from '../../compose/components/navigation_bar';
|
||||
import ColumnHeader from './column_header';
|
||||
import Immutable from 'immutable';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
|
||||
const noop = () => { };
|
||||
|
||||
@ -50,7 +48,7 @@ const PageTwo = ({ me }) => (
|
||||
</div>
|
||||
<ComposeForm
|
||||
text='Awoo! #introductions'
|
||||
suggestions={Immutable.List()}
|
||||
suggestions={ImmutableList()}
|
||||
mentionedDomains={[]}
|
||||
spoiler={false}
|
||||
onChange={noop}
|
||||
@ -227,6 +225,10 @@ export default class OnboardingModal extends React.PureComponent {
|
||||
}));
|
||||
}
|
||||
|
||||
handleSwipe = (index) => {
|
||||
this.setState({ currentIndex: index });
|
||||
}
|
||||
|
||||
handleKeyUp = ({ key }) => {
|
||||
switch (key) {
|
||||
case 'ArrowLeft':
|
||||
@ -263,30 +265,18 @@ export default class OnboardingModal extends React.PureComponent {
|
||||
</button>
|
||||
);
|
||||
|
||||
const styles = pages.map((data, i) => ({
|
||||
key: `page-${i}`,
|
||||
data,
|
||||
style: {
|
||||
opacity: spring(i === currentIndex ? 1 : 0),
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal onboarding-modal'>
|
||||
<TransitionMotion styles={styles}>
|
||||
{interpolatedStyles => (
|
||||
<ReactSwipeable onSwipedRight={this.handlePrev} onSwipedLeft={this.handleNext} className='onboarding-modal__pager'>
|
||||
{interpolatedStyles.map(({ key, data, style }, i) => {
|
||||
const className = classNames('onboarding-modal__page__wrapper', {
|
||||
'onboarding-modal__page__wrapper--active': i === currentIndex,
|
||||
});
|
||||
return (
|
||||
<div key={key} style={style} className={className}>{data}</div>
|
||||
);
|
||||
})}
|
||||
</ReactSwipeable>
|
||||
)}
|
||||
</TransitionMotion>
|
||||
<ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'>
|
||||
{pages.map((page, i) => {
|
||||
const className = classNames('onboarding-modal__page__wrapper', {
|
||||
'onboarding-modal__page__wrapper--active': i === currentIndex,
|
||||
});
|
||||
return (
|
||||
<div key={i} className={className}>{page}</div>
|
||||
);
|
||||
})}
|
||||
</ReactSwipeableViews>
|
||||
|
||||
<div className='onboarding-modal__paginator'>
|
||||
<div>
|
||||
|
@ -7,7 +7,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { makeGetAccount } from '../../../selectors';
|
||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
||||
import StatusCheckBox from '../../report/containers/status_check_box_container';
|
||||
import Immutable from 'immutable';
|
||||
import { OrderedSet } from 'immutable';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Button from '../../../components/button';
|
||||
|
||||
@ -26,7 +26,7 @@ const makeMapStateToProps = () => {
|
||||
isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
|
||||
account: getAccount(state, accountId),
|
||||
comment: state.getIn(['reports', 'new', 'comment']),
|
||||
statusIds: Immutable.OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
|
||||
statusIds: OrderedSet(state.getIn(['timelines', `account:${accountId}`, 'items'])).union(state.getIn(['reports', 'new', 'status_ids'])),
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
import NavLink from 'react-router-dom/NavLink';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const links = [
|
||||
export const links = [
|
||||
<NavLink className='tabs-bar__link primary' activeClassName='active' to='/statuses/new'><i className='fa fa-fw fa-pencil' /><FormattedMessage id='tabs_bar.compose' defaultMessage='Compose' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link primary' activeClassName='active' to='/timelines/home'><i className='fa fa-fw fa-home' /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
|
||||
<NavLink className='tabs-bar__link primary' activeClassName='active' to='/notifications'><i className='fa fa-fw fa-bell' /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
|
||||
@ -13,25 +13,13 @@ const links = [
|
||||
<NavLink className='tabs-bar__link primary' activeClassName='active' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started'><i className='fa fa-fw fa-asterisk' /></NavLink>,
|
||||
];
|
||||
|
||||
export function getPreviousLink (path) {
|
||||
const index = links.findIndex(link => link.props.to === path);
|
||||
export function getIndex (path) {
|
||||
return links.findIndex(link => link.props.to === path);
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
return links[index - 1].props.to;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export function getNextLink (path) {
|
||||
const index = links.findIndex(link => link.props.to === path);
|
||||
|
||||
if (index !== -1 && index < links.length - 1) {
|
||||
return links[index + 1].props.to;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
export function getLink (index) {
|
||||
return links[index].props.to;
|
||||
}
|
||||
|
||||
export default class TabsBar extends React.Component {
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import Bundle from '../components/bundle';
|
||||
|
||||
import { fetchBundleRequest, fetchBundleSuccess, fetchBundleFail } from '../../../actions/bundles';
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onFetch () {
|
||||
dispatch(fetchBundleRequest());
|
||||
},
|
||||
onFetchSuccess () {
|
||||
dispatch(fetchBundleSuccess());
|
||||
},
|
||||
onFetchFail (error) {
|
||||
dispatch(fetchBundleFail(error));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(Bundle);
|
@ -1,13 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
import StatusList from '../../../components/status_list';
|
||||
import { scrollTopTimeline } from '../../../actions/timelines';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import { createSelector } from 'reselect';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const makeGetStatusIds = () => createSelector([
|
||||
(state, { type }) => state.getIn(['settings', type], Immutable.Map()),
|
||||
(state, { type }) => state.getIn(['timelines', type, 'items'], Immutable.List()),
|
||||
(state, { type }) => state.getIn(['settings', type], ImmutableMap()),
|
||||
(state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
|
||||
(state) => state.get('statuses'),
|
||||
(state) => state.getIn(['meta', 'me']),
|
||||
], (columnSettings, statusIds, statuses, me) => statusIds.filter(id => {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import Switch from 'react-router-dom/Switch';
|
||||
import Route from 'react-router-dom/Route';
|
||||
import classNames from 'classnames';
|
||||
import Redirect from 'react-router-dom/Redirect';
|
||||
import NotificationsContainer from './containers/notifications_container';
|
||||
import PropTypes from 'prop-types';
|
||||
@ -13,66 +12,37 @@ import { debounce } from 'lodash';
|
||||
import { uploadCompose } from '../../actions/compose';
|
||||
import { refreshHomeTimeline } from '../../actions/timelines';
|
||||
import { refreshNotifications } from '../../actions/notifications';
|
||||
import { WrappedSwitch, WrappedRoute } from './util/react_router_helpers';
|
||||
import UploadArea from './components/upload_area';
|
||||
import ColumnsAreaContainer from './containers/columns_area_container';
|
||||
import Status from '../../features/status';
|
||||
import GettingStarted from '../../features/getting_started';
|
||||
import PublicTimeline from '../../features/public_timeline';
|
||||
import CommunityTimeline from '../../features/community_timeline';
|
||||
import AccountTimeline from '../../features/account_timeline';
|
||||
import AccountGallery from '../../features/account_gallery';
|
||||
import HomeTimeline from '../../features/home_timeline';
|
||||
import Compose from '../../features/compose';
|
||||
import Followers from '../../features/followers';
|
||||
import Following from '../../features/following';
|
||||
import Reblogs from '../../features/reblogs';
|
||||
import Favourites from '../../features/favourites';
|
||||
import HashtagTimeline from '../../features/hashtag_timeline';
|
||||
import Notifications from '../../features/notifications';
|
||||
import FollowRequests from '../../features/follow_requests';
|
||||
import GenericNotFound from '../../features/generic_not_found';
|
||||
import FavouritedStatuses from '../../features/favourited_statuses';
|
||||
import Blocks from '../../features/blocks';
|
||||
import Mutes from '../../features/mutes';
|
||||
import {
|
||||
Compose,
|
||||
Status,
|
||||
GettingStarted,
|
||||
PublicTimeline,
|
||||
CommunityTimeline,
|
||||
AccountTimeline,
|
||||
AccountGallery,
|
||||
HomeTimeline,
|
||||
Followers,
|
||||
Following,
|
||||
Reblogs,
|
||||
Favourites,
|
||||
HashtagTimeline,
|
||||
Notifications,
|
||||
FollowRequests,
|
||||
GenericNotFound,
|
||||
FavouritedStatuses,
|
||||
Blocks,
|
||||
Mutes,
|
||||
} from './util/async-components';
|
||||
|
||||
// Small wrapper to pass multiColumn to the route components
|
||||
const WrappedSwitch = ({ multiColumn, children }) => (
|
||||
<Switch>
|
||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
|
||||
</Switch>
|
||||
);
|
||||
|
||||
WrappedSwitch.propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
// Small Wraper to extract the params from the route and pass
|
||||
// them to the rendered component, together with the content to
|
||||
// be rendered inside (the children)
|
||||
class WrappedRoute extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
component: PropTypes.func.isRequired,
|
||||
content: PropTypes.node,
|
||||
multiColumn: PropTypes.bool,
|
||||
}
|
||||
|
||||
renderComponent = ({ match: { params } }) => {
|
||||
const { component: Component, content, multiColumn } = this.props;
|
||||
|
||||
return <Component params={params} multiColumn={multiColumn}>{content}</Component>;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { component: Component, content, ...rest } = this.props;
|
||||
|
||||
return <Route {...rest} render={this.renderComponent} />;
|
||||
}
|
||||
|
||||
}
|
||||
// Dummy import, to make sure that <Status /> ends up in the application bundle.
|
||||
// Without this it ends up in ~8 very commonly used bundles.
|
||||
import '../../../glitch/components/status';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
systemFontUi: state.getIn(['meta', 'system_font_ui']),
|
||||
layout: state.getIn(['local_settings', 'layout']),
|
||||
isWide: state.getIn(['local_settings', 'stretch']),
|
||||
});
|
||||
@ -85,6 +55,7 @@ export default class UI extends React.PureComponent {
|
||||
children: PropTypes.node,
|
||||
layout: PropTypes.string,
|
||||
isWide: PropTypes.bool,
|
||||
systemFontUi: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
@ -194,8 +165,13 @@ export default class UI extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
const className = classNames('ui', columnsClass(layout), {
|
||||
'wide': isWide,
|
||||
'system-font': this.props.systemFontUi,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={'ui ' + columnsClass(layout) + (isWide ? ' wide' : '')} ref={this.setRef}>
|
||||
<div className={className} ref={this.setRef}>
|
||||
<TabsBar />
|
||||
<ColumnsAreaContainer singleColumn={isMobile(width, layout)}>
|
||||
<WrappedSwitch>
|
||||
|
@ -0,0 +1,21 @@
|
||||
|
||||
// Get the bounding client rect from an IntersectionObserver entry.
|
||||
// This is to work around a bug in Chrome: https://crbug.com/737228
|
||||
|
||||
let hasBoundingRectBug;
|
||||
|
||||
function getRectFromEntry(entry) {
|
||||
if (typeof hasBoundingRectBug !== 'boolean') {
|
||||
const boundingRect = entry.target.getBoundingClientRect();
|
||||
const observerRect = entry.boundingClientRect;
|
||||
hasBoundingRectBug = boundingRect.height !== observerRect.height ||
|
||||
boundingRect.top !== observerRect.top ||
|
||||
boundingRect.width !== observerRect.width ||
|
||||
boundingRect.bottom !== observerRect.bottom ||
|
||||
boundingRect.left !== observerRect.left ||
|
||||
boundingRect.right !== observerRect.right;
|
||||
}
|
||||
return hasBoundingRectBug ? entry.target.getBoundingClientRect() : entry.boundingClientRect;
|
||||
}
|
||||
|
||||
export default getRectFromEntry;
|
@ -37,9 +37,18 @@ class IntersectionObserverWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
unobserve (id, node) {
|
||||
if (this.observer) {
|
||||
delete this.callbacks[id];
|
||||
this.observer.unobserve(node);
|
||||
}
|
||||
}
|
||||
|
||||
disconnect () {
|
||||
if (this.observer) {
|
||||
this.callbacks = {};
|
||||
this.observer.disconnect();
|
||||
this.observer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Switch from 'react-router-dom/Switch';
|
||||
import Route from 'react-router-dom/Route';
|
||||
|
||||
import ColumnLoading from '../components/column_loading';
|
||||
import BundleColumnError from '../components/bundle_column_error';
|
||||
import BundleContainer from '../containers/bundle_container';
|
||||
|
||||
// Small wrapper to pass multiColumn to the route components
|
||||
export const WrappedSwitch = ({ multiColumn, children }) => (
|
||||
<Switch>
|
||||
{React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
|
||||
</Switch>
|
||||
);
|
||||
|
||||
WrappedSwitch.propTypes = {
|
||||
multiColumn: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
// Small Wraper to extract the params from the route and pass
|
||||
// them to the rendered component, together with the content to
|
||||
// be rendered inside (the children)
|
||||
export class WrappedRoute extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
component: PropTypes.func.isRequired,
|
||||
content: PropTypes.node,
|
||||
multiColumn: PropTypes.bool,
|
||||
}
|
||||
|
||||
renderComponent = ({ match }) => {
|
||||
const { component, content, multiColumn } = this.props;
|
||||
|
||||
return (
|
||||
<BundleContainer fetchComponent={component} loading={this.renderLoading} error={this.renderError}>
|
||||
{Component => <Component params={match.params} multiColumn={multiColumn}>{content}</Component>}
|
||||
</BundleContainer>
|
||||
);
|
||||
}
|
||||
|
||||
renderLoading = () => {
|
||||
return <ColumnLoading />;
|
||||
}
|
||||
|
||||
renderError = (props) => {
|
||||
return <BundleColumnError {...props} />;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { component: Component, content, ...rest } = this.props;
|
||||
|
||||
return <Route {...rest} render={this.renderComponent} />;
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "إلغاء المتابعة",
|
||||
"account.unmute": "إلغاء الكتم عن @{name}",
|
||||
"boost_modal.combo": "يمكنك ضغط {combo} لتخطّي هذه في المرّة القادمة",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "الحسابات المحجوبة",
|
||||
"column.community": "الخيط العام المحلي",
|
||||
"column.favourites": "المفضلة",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "لا تقم بإدراجه على الخيوط العامة",
|
||||
"privacy.unlisted.short": "غير مدرج",
|
||||
"reply_indicator.cancel": "إلغاء",
|
||||
"report.heading": "تقرير جديد",
|
||||
"report.placeholder": "تعليقات إضافية",
|
||||
"report.submit": "إرسال",
|
||||
"report.target": "إبلاغ",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Не следвай",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Local timeline",
|
||||
"column.favourites": "Favourites",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Do not show in public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Отказ",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Deixar de seguir",
|
||||
"account.unmute": "Treure silenci de @{name}",
|
||||
"boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Usuaris bloquejats",
|
||||
"column.community": "Línia de temps local",
|
||||
"column.favourites": "Favorits",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "No publicar en línies de temps públiques",
|
||||
"privacy.unlisted.short": "No llistat",
|
||||
"reply_indicator.cancel": "Cancel·lar",
|
||||
"report.heading": "Nou informe",
|
||||
"report.placeholder": "Comentaris addicionals",
|
||||
"report.submit": "Enviar",
|
||||
"report.target": "Informes",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Entfolgen",
|
||||
"account.unmute": "@{name} nicht mehr stummschalten",
|
||||
"boost_modal.combo": "Du kannst {combo} drücken, um dies beim nächsten Mal zu überspringen",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blockierte Benutzer",
|
||||
"column.community": "Lokale Zeitleiste",
|
||||
"column.favourites": "Favoriten",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Nicht in öffentlichen Zeitleisten anzeigen",
|
||||
"privacy.unlisted.short": "Nicht gelistet",
|
||||
"reply_indicator.cancel": "Abbrechen",
|
||||
"report.heading": "Neue Meldung",
|
||||
"report.placeholder": "Zusätzliche Kommentare",
|
||||
"report.submit": "Absenden",
|
||||
"report.target": "Melden",
|
||||
|
@ -643,6 +643,14 @@
|
||||
"defaultMessage": "Getting started",
|
||||
"id": "getting_started.heading"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Home",
|
||||
"id": "tabs_bar.home"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Notifications",
|
||||
"id": "tabs_bar.notifications"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Federated timeline",
|
||||
"id": "navigation_bar.public_timeline"
|
||||
@ -956,27 +964,6 @@
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/public_timeline/index.json"
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "New report",
|
||||
"id": "report.heading"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Additional comments",
|
||||
"id": "report.placeholder"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Submit",
|
||||
"id": "report.submit"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Reporting",
|
||||
"id": "report.target"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/report/index.json"
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
@ -1036,6 +1023,40 @@
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/ui/components/boost_modal.json"
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Network error",
|
||||
"id": "bundle_column_error.title"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Something went wrong while loading this component.",
|
||||
"id": "bundle_column_error.body"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Try again",
|
||||
"id": "bundle_column_error.retry"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/ui/components/bundle_column_error.json"
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "Something went wrong while loading this component.",
|
||||
"id": "bundle_modal_error.message"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Try again",
|
||||
"id": "bundle_modal_error.retry"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Close",
|
||||
"id": "bundle_modal_error.close"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/ui/components/bundle_modal_error.json"
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Unfollow",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Local timeline",
|
||||
"column.favourites": "Favourites",
|
||||
@ -141,7 +147,6 @@
|
||||
"privacy.unlisted.long": "Do not post to public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Cancel",
|
||||
"report.heading": "Report {target}",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting {target}",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Malsekvi",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Loka tempolinio",
|
||||
"column.favourites": "Favourites",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Do not show in public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Rezigni",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Dejar de seguir",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Usuarios bloqueados",
|
||||
"column.community": "Historia local",
|
||||
"column.favourites": "Favoritos",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "No mostrar en la historia federada",
|
||||
"privacy.unlisted.short": "Sin federar",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "پایان پیگیری",
|
||||
"account.unmute": "باصدا کردن @{name}",
|
||||
"boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "کاربران مسدودشده",
|
||||
"column.community": "نوشتههای محلی",
|
||||
"column.favourites": "پسندیدهها",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "عمومی، ولی فهرست نکن",
|
||||
"privacy.unlisted.short": "فهرستنشده",
|
||||
"reply_indicator.cancel": "لغو",
|
||||
"report.heading": "گزارش تازه",
|
||||
"report.placeholder": "توضیح اضافه",
|
||||
"report.submit": "بفرست",
|
||||
"report.target": "گزارشدادن",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Lopeta seuraaminen",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Paikallinen aikajana",
|
||||
"column.favourites": "Favourites",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Do not show in public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Peruuta",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
|
@ -7,7 +7,7 @@
|
||||
"account.followers": "Abonné⋅e⋅s",
|
||||
"account.follows": "Abonnements",
|
||||
"account.follows_you": "Vous suit",
|
||||
"account.media": "Media",
|
||||
"account.media": "Média",
|
||||
"account.mention": "Mentionner",
|
||||
"account.mute": "Masquer",
|
||||
"account.posts": "Statuts",
|
||||
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Ne plus suivre",
|
||||
"account.unmute": "Ne plus masquer",
|
||||
"boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Comptes bloqués",
|
||||
"column.community": "Fil public local",
|
||||
"column.favourites": "Favoris",
|
||||
@ -31,10 +37,10 @@
|
||||
"column_header.unpin": "Retirer",
|
||||
"column_subheading.navigation": "Navigation",
|
||||
"column_subheading.settings": "Paramètres",
|
||||
"compose_form.lock_disclaimer": "Votre compte n'est pas {locked}. Tout le monde peut vous suivre et voir vos pouets restreints.",
|
||||
"compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos pouets restreints.",
|
||||
"compose_form.lock_disclaimer.lock": "verrouillé",
|
||||
"compose_form.placeholder": "Qu’avez-vous en tête ?",
|
||||
"compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodon. Si {domains} {domainsCount, plural, one {n’est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n’y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d’une autre manière à d’autres personnes imprévues.",
|
||||
"compose_form.placeholder": "Qu’avez-vous en tête ?",
|
||||
"compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodon. Si {domains} {domainsCount, plural, one {n’est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n’y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d’une autre manière à d’autres personnes imprévues.",
|
||||
"compose_form.publish": "Pouet ",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Marquer le média comme délicat",
|
||||
@ -42,13 +48,13 @@
|
||||
"compose_form.spoiler_placeholder": "Avertissement",
|
||||
"confirmation_modal.cancel": "Annuler",
|
||||
"confirmations.block.confirm": "Bloquer",
|
||||
"confirmations.block.message": "Confirmez vous le blocage de {name} ?",
|
||||
"confirmations.block.message": "Confirmez vous le blocage de {name} ?",
|
||||
"confirmations.delete.confirm": "Supprimer",
|
||||
"confirmations.delete.message": "Confirmez vous la suppression de ce pouet ?",
|
||||
"confirmations.delete.message": "Confirmez vous la suppression de ce pouet ?",
|
||||
"confirmations.domain_block.confirm": "Masquer le domaine entier",
|
||||
"confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou silenciations ciblés sont suffisants et préférables.",
|
||||
"confirmations.domain_block.message": "Êtes-vous vraiment, vraiment sûr⋅e de vouloir bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou silenciations ciblés sont suffisants et préférables.",
|
||||
"confirmations.mute.confirm": "Silencer",
|
||||
"confirmations.mute.message": "Confirmez vous la silenciation {name} ?",
|
||||
"confirmations.mute.message": "Confirmez vous la silenciation {name} ?",
|
||||
"emoji_button.activity": "Activités",
|
||||
"emoji_button.flags": "Drapeaux",
|
||||
"emoji_button.food": "Boire et manger",
|
||||
@ -59,20 +65,20 @@
|
||||
"emoji_button.search": "Recherche…",
|
||||
"emoji_button.symbols": "Symboles",
|
||||
"emoji_button.travel": "Lieux et voyages",
|
||||
"empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !",
|
||||
"empty_column.community": "Le fil public local est vide. Écrivez-donc quelque chose pour le remplir !",
|
||||
"empty_column.hashtag": "Il n’y a encore aucun contenu relatif à ce hashtag",
|
||||
"empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateurs⋅trices.",
|
||||
"empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.",
|
||||
"empty_column.home.inactivity": "Votre accueil est vide. Si vous ne vous êtes pas connecté⋅e depuis un moment, il se remplira automatiquement très bientôt.",
|
||||
"empty_column.home.public_timeline": "le fil public",
|
||||
"empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateurs⋅trices pour débuter la conversation.",
|
||||
"empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateurs⋅trices d’autres instances pour remplir le fil public.",
|
||||
"empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateur⋅ice⋅s pour débuter la conversation.",
|
||||
"empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice⋅s d’autres instances pour remplir le fil public.",
|
||||
"follow_request.authorize": "Autoriser",
|
||||
"follow_request.reject": "Rejeter",
|
||||
"getting_started.appsshort": "Applications",
|
||||
"getting_started.faq": "FAQ",
|
||||
"getting_started.heading": "Pour commencer",
|
||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer et envoyer vos commentaires et rapports de bogues via {github} sur GitHub.",
|
||||
"getting_started.userguide": "Guide d'utilisation",
|
||||
"getting_started.userguide": "Guide d’utilisation",
|
||||
"home.column_settings.advanced": "Avancé",
|
||||
"home.column_settings.basic": "Basique",
|
||||
"home.column_settings.filter_regex": "Filtrer avec une expression rationnelle",
|
||||
@ -93,37 +99,37 @@
|
||||
"navigation_bar.mutes": "Comptes silencés",
|
||||
"navigation_bar.preferences": "Préférences",
|
||||
"navigation_bar.public_timeline": "Fil public global",
|
||||
"notification.favourite": "{name} a ajouté à ses favoris :",
|
||||
"notification.favourite": "{name} a ajouté à ses favoris :",
|
||||
"notification.follow": "{name} vous suit.",
|
||||
"notification.mention": "{name} vous a mentionné⋅e :",
|
||||
"notification.reblog": "{name} a partagé votre statut :",
|
||||
"notification.mention": "{name} vous a mentionné⋅e :",
|
||||
"notification.reblog": "{name} a partagé votre statut :",
|
||||
"notifications.clear": "Nettoyer",
|
||||
"notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?",
|
||||
"notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?",
|
||||
"notifications.column_settings.alert": "Notifications locales",
|
||||
"notifications.column_settings.favourite": "Favoris :",
|
||||
"notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :",
|
||||
"notifications.column_settings.mention": "Mentions :",
|
||||
"notifications.column_settings.reblog": "Partages :",
|
||||
"notifications.column_settings.favourite": "Favoris :",
|
||||
"notifications.column_settings.follow": "Nouveaux⋅elles abonn⋅é⋅s :",
|
||||
"notifications.column_settings.mention": "Mentions :",
|
||||
"notifications.column_settings.reblog": "Partages :",
|
||||
"notifications.column_settings.show": "Afficher dans la colonne",
|
||||
"notifications.column_settings.sound": "Émettre un son",
|
||||
"onboarding.done": "Effectué",
|
||||
"onboarding.next": "Suivant",
|
||||
"onboarding.page_five.public_timelines": "Le fil public global affiche les posts de tou⋅te⋅s les utilisateurs⋅trices suivi⋅es par les membres de {domain}. Le fil public local est identique mais se limite aux utilisateurs⋅trices de {domain}.",
|
||||
"onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te⋅s les utilisateurs⋅trices que vous suivez",
|
||||
"onboarding.page_five.public_timelines": "Le fil public global affiche les posts de tou⋅te⋅s les utilisateur⋅ice⋅s suivi⋅es par les membres de {domain}. Le fil public local est identique mais se limite aux utilisateur⋅ice⋅s de {domain}.",
|
||||
"onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te⋅s les utilisateur⋅ice⋅s que vous suivez",
|
||||
"onboarding.page_four.notifications": "Les Notifications vous informent lorsque quelqu’un interagit avec vous",
|
||||
"onboarding.page_one.federation": "Mastodon est un réseau social qui appartient à tou⋅te⋅s.",
|
||||
"onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d’utilisateur⋅trice complet est {handle}",
|
||||
"onboarding.page_one.welcome": "Bienvenue sur Mastodon !",
|
||||
"onboarding.page_one.handle": "Vous êtes sur {domain}, une des nombreuses instances indépendantes de Mastodon. Votre nom d’utilisateur⋅ice complet est {handle}",
|
||||
"onboarding.page_one.welcome": "Bienvenue sur Mastodon !",
|
||||
"onboarding.page_six.admin": "L’administrateur⋅trice de votre instance est {admin}",
|
||||
"onboarding.page_six.almost_done": "Nous y sommes presque…",
|
||||
"onboarding.page_six.appetoot": "Bon Appetoot!",
|
||||
"onboarding.page_six.apps_available": "De nombreuses {apps} sont disponibles pour iOS, Android et autres. Et maintenant… Bon Appetoot!",
|
||||
"onboarding.page_six.github": "Mastodon est un logiciel libre, gratuit et open-source. Vous pouvez rapporter des bogues, suggérer des fonctionnalités, ou contribuer à son développement sur {github}.",
|
||||
"onboarding.page_six.guidelines": "règles de la communauté",
|
||||
"onboarding.page_six.read_guidelines": "S’il vous plaît, n’oubliez pas de lire les {guidelines} !",
|
||||
"onboarding.page_six.read_guidelines": "S’il vous plaît, n’oubliez pas de lire les {guidelines} !",
|
||||
"onboarding.page_six.various_app": "applications mobiles",
|
||||
"onboarding.page_three.profile": "Modifiez votre profil pour changer votre avatar, votre description ainsi que votre nom. Vous y trouverez également d’autres préférences.",
|
||||
"onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateurs⋅trices et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d’utilisateur⋅trice complet.",
|
||||
"onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateur⋅ice⋅s et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d’utilisateur⋅ice complet.",
|
||||
"onboarding.page_two.compose": "Écrivez depuis la colonne de composition. Vous pouvez ajouter des images, changer les réglages de confidentialité, et ajouter des avertissements de contenu (Content Warning) grâce aux icônes en dessous.",
|
||||
"onboarding.skip": "Passer",
|
||||
"privacy.change": "Ajuster la confidentialité du message",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Ne pas afficher dans les fils publics",
|
||||
"privacy.unlisted.short": "Non-listé",
|
||||
"reply_indicator.cancel": "Annuler",
|
||||
"report.heading": "Nouveau signalement",
|
||||
"report.placeholder": "Commentaires additionnels",
|
||||
"report.submit": "Envoyer",
|
||||
"report.target": "Signalement",
|
||||
@ -151,7 +156,7 @@
|
||||
"status.mute_conversation": "Masquer la conversation",
|
||||
"status.open": "Déplier ce statut",
|
||||
"status.reblog": "Partager",
|
||||
"status.reblogged_by": "{name} a partagé :",
|
||||
"status.reblogged_by": "{name} a partagé :",
|
||||
"status.reply": "Répondre",
|
||||
"status.replyAll": "Répondre au fil",
|
||||
"status.report": "Signaler @{name}",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "הפסקת מעקב",
|
||||
"account.unmute": "הפסקת השתקת @{name}",
|
||||
"boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "חסימות",
|
||||
"column.community": "ציר זמן מקומי",
|
||||
"column.favourites": "חיבובים",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "לא יופיע בפידים הציבוריים המשותפים",
|
||||
"privacy.unlisted.short": "לא לפיד הכללי",
|
||||
"reply_indicator.cancel": "ביטול",
|
||||
"report.heading": "דווח חדש",
|
||||
"report.placeholder": "הערות נוספות",
|
||||
"report.submit": "שליחה",
|
||||
"report.target": "דיווח",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Prestani slijediti",
|
||||
"account.unmute": "Poništi utišavanje @{name}",
|
||||
"boost_modal.combo": "Možeš pritisnuti {combo} kako bi ovo preskočio sljedeći put",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blokirani korisnici",
|
||||
"column.community": "Lokalni timeline",
|
||||
"column.favourites": "Favoriti",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Ne prikazuj u javnim timelineovima",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Otkaži",
|
||||
"report.heading": "Nova prijava",
|
||||
"report.placeholder": "Dodatni komentari",
|
||||
"report.submit": "Pošalji",
|
||||
"report.target": "Prijavljivanje",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Követés abbahagyása",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Local timeline",
|
||||
"column.favourites": "Favourites",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Do not show in public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Mégsem",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Berhenti mengikuti",
|
||||
"account.unmute": "Berhenti membisukan @{name}",
|
||||
"boost_modal.combo": "Anda dapat menekan {combo} untuk melewati ini",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Pengguna diblokir",
|
||||
"column.community": "Linimasa Lokal",
|
||||
"column.favourites": "Favorit",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Tidak ditampilkan di linimasa publik",
|
||||
"privacy.unlisted.short": "Tak Terdaftar",
|
||||
"reply_indicator.cancel": "Batal",
|
||||
"report.heading": "Laporan baru",
|
||||
"report.placeholder": "Komentar tambahan",
|
||||
"report.submit": "Kirim",
|
||||
"report.target": "Melaporkan",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Ne plus sequar",
|
||||
"account.unmute": "Ne plus celar @{name}",
|
||||
"boost_modal.combo": "Tu povas presar sur {combo} por omisar co en la venonta foyo",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blokusita uzeri",
|
||||
"column.community": "Lokala tempolineo",
|
||||
"column.favourites": "Favorati",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Ne montrar en publika tempolinei",
|
||||
"privacy.unlisted.short": "Ne enlistigota",
|
||||
"reply_indicator.cancel": "Nihiligar",
|
||||
"report.heading": "Nova denunco",
|
||||
"report.placeholder": "Plusa komenti",
|
||||
"report.submit": "Sendar",
|
||||
"report.target": "Denuncante",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Non seguire",
|
||||
"account.unmute": "Non silenziare @{name}",
|
||||
"boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Utenti bloccati",
|
||||
"column.community": "Timeline locale",
|
||||
"column.favourites": "Apprezzati",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Non mostrare sulla timeline pubblica",
|
||||
"privacy.unlisted.short": "Non elencato",
|
||||
"reply_indicator.cancel": "Annulla",
|
||||
"report.heading": "Nuova segnalazione",
|
||||
"report.placeholder": "Commenti aggiuntivi",
|
||||
"report.submit": "Invia",
|
||||
"report.target": "Invio la segnalazione",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "フォロー解除",
|
||||
"account.unmute": "ミュート解除",
|
||||
"boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。",
|
||||
"bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。",
|
||||
"bundle_column_error.retry": "再試行",
|
||||
"bundle_column_error.title": "ネットワークエラー",
|
||||
"bundle_modal_error.close": "閉じる",
|
||||
"bundle_modal_error.message": "コンポーネントの読み込み中に問題が発生しました。",
|
||||
"bundle_modal_error.retry": "再試行",
|
||||
"column.blocks": "ブロックしたユーザー",
|
||||
"column.community": "ローカルタイムライン",
|
||||
"column.favourites": "お気に入り",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "公開TLで表示しない",
|
||||
"privacy.unlisted.short": "未収載",
|
||||
"reply_indicator.cancel": "キャンセル",
|
||||
"report.heading": "新規通報",
|
||||
"report.placeholder": "コメント",
|
||||
"report.submit": "通報する",
|
||||
"report.target": "問題のユーザー",
|
||||
|
181
app/javascript/mastodon/locales/ko.json
Normal file
181
app/javascript/mastodon/locales/ko.json
Normal file
@ -0,0 +1,181 @@
|
||||
{
|
||||
"account.block": "차단",
|
||||
"account.block_domain": "{domain} 전체를 숨김",
|
||||
"account.disclaimer": "이 사용자는 다른 인스턴스에 소속되어 있으므로, 수치가 정확하지 않을 수도 있습니다.",
|
||||
"account.edit_profile": "프로필 편집",
|
||||
"account.follow": "팔로우",
|
||||
"account.followers": "팔로워",
|
||||
"account.follows": "팔로우",
|
||||
"account.follows_you": "날 팔로우합니다",
|
||||
"account.media": "미디어",
|
||||
"account.mention": "답장",
|
||||
"account.mute": "뮤트",
|
||||
"account.posts": "포스트",
|
||||
"account.report": "신고",
|
||||
"account.requested": "승인 대기 중",
|
||||
"account.unblock": "차단 해제",
|
||||
"account.unblock_domain": "{domain} 숨김 해제",
|
||||
"account.unfollow": "팔로우 해제",
|
||||
"account.unmute": "뮤트 해제",
|
||||
"boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "차단 중인 사용자",
|
||||
"column.community": "로컬 타임라인",
|
||||
"column.favourites": "즐겨찾기",
|
||||
"column.follow_requests": "팔로우 요청",
|
||||
"column.home": "홈",
|
||||
"column.mutes": "뮤트 중인 사용자",
|
||||
"column.notifications": "알림",
|
||||
"column.public": "연합 타임라인",
|
||||
"column_back_button.label": "돌아가기",
|
||||
"column_header.pin": "고정하기",
|
||||
"column_header.unpin": "고정 해제",
|
||||
"column_subheading.navigation": "내비게이션",
|
||||
"column_subheading.settings": "설정",
|
||||
"compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.",
|
||||
"compose_form.lock_disclaimer.lock": "비공개",
|
||||
"compose_form.placeholder": "지금 무엇을 하고 있나요?",
|
||||
"compose_form.privacy_disclaimer": "이 계정의 비공개 포스트는 멘션된 사용자가 소속된 {domains}으로 전송됩니다. {domainsCount, plural, one {이 서버를} other {이 서버들을}} 신뢰할 수 있습니까? 포스팅의 프라이버시 보호는 Mastodon 서버에서만 유효합니다. {domains}가 Mastodon 인스턴스가 아닐 경우, 이 투고가 사적인 것으로 취급되지 않은 채 부스트 되거나 원하지 않는 사용자에게 보여질 가능성이 있습니다.",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "이 미디어를 민감한 미디어로 취급",
|
||||
"compose_form.spoiler": "텍스트 숨기기",
|
||||
"compose_form.spoiler_placeholder": "경고",
|
||||
"confirmation_modal.cancel": "취소",
|
||||
"confirmations.block.confirm": "차단",
|
||||
"confirmations.block.message": "정말로 {name}를 차단하시겠습니까?",
|
||||
"confirmations.delete.confirm": "삭제",
|
||||
"confirmations.delete.message": "정말로 삭제하시겠습니까?",
|
||||
"confirmations.domain_block.confirm": "도메인 전체를 숨김",
|
||||
"confirmations.domain_block.message": "정말로 {domain} 전체를 숨기시겠습니까? 대부분의 경우 개별 차단이나 뮤트로 충분합니다.",
|
||||
"confirmations.mute.confirm": "뮤트",
|
||||
"confirmations.mute.message": "정말로 {name}를 뮤트하시겠습니까?",
|
||||
"emoji_button.activity": "활동",
|
||||
"emoji_button.flags": "국기",
|
||||
"emoji_button.food": "음식",
|
||||
"emoji_button.label": "emoji를 추가",
|
||||
"emoji_button.nature": "자연",
|
||||
"emoji_button.objects": "물건",
|
||||
"emoji_button.people": "사람들",
|
||||
"emoji_button.search": "검색...",
|
||||
"emoji_button.symbols": "기호",
|
||||
"emoji_button.travel": "여행과 장소",
|
||||
"empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!",
|
||||
"empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.",
|
||||
"empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.",
|
||||
"empty_column.home.inactivity": "홈 피드에 아무 것도 없습니다. 한동안 활동하지 않은 경우 곧 원래대로 돌아올 것입니다.",
|
||||
"empty_column.home.public_timeline": "연합 타임라인",
|
||||
"empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요!",
|
||||
"empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스 유저를 팔로우 해서 가득 채워보세요!",
|
||||
"follow_request.authorize": "허가",
|
||||
"follow_request.reject": "거부",
|
||||
"getting_started.appsshort": "어플리케이션",
|
||||
"getting_started.faq": "자주 있는 질문",
|
||||
"getting_started.heading": "시작",
|
||||
"getting_started.open_source_notice": "Mastodon은 오픈 소스 소프트웨어입니다. 누구나 GitHub({github})에서 개발에 참여하거나, 문제를 보고할 수 있습니다.",
|
||||
"getting_started.userguide": "사용자 가이드",
|
||||
"home.column_settings.advanced": "고급 사용자용",
|
||||
"home.column_settings.basic": "기본 설정",
|
||||
"home.column_settings.filter_regex": "정규 표현식으로 필터링",
|
||||
"home.column_settings.show_reblogs": "부스트 표시",
|
||||
"home.column_settings.show_replies": "답글 표시",
|
||||
"home.settings": "컬럼 설정",
|
||||
"lightbox.close": "닫기",
|
||||
"loading_indicator.label": "불러오는 중...",
|
||||
"media_gallery.toggle_visible": "표시 전환",
|
||||
"missing_indicator.label": "찾을 수 없습니다",
|
||||
"navigation_bar.blocks": "차단한 사용자",
|
||||
"navigation_bar.community_timeline": "로컬 타임라인",
|
||||
"navigation_bar.edit_profile": "프로필 편집",
|
||||
"navigation_bar.favourites": "즐겨찾기",
|
||||
"navigation_bar.follow_requests": "팔로우 요청",
|
||||
"navigation_bar.info": "이 인스턴스에 대해서",
|
||||
"navigation_bar.logout": "로그아웃",
|
||||
"navigation_bar.mutes": "뮤트 중인 사용자",
|
||||
"navigation_bar.preferences": "사용자 설정",
|
||||
"navigation_bar.public_timeline": "연합 타임라인",
|
||||
"notification.favourite": "{name}님이 즐겨찾기 했습니다",
|
||||
"notification.follow": "{name}님이 나를 팔로우 했습니다",
|
||||
"notification.mention": "{name}님이 답글을 보냈습니다",
|
||||
"notification.reblog": "{name}님이 부스트 했습니다",
|
||||
"notifications.clear": "알림 지우기",
|
||||
"notifications.clear_confirmation": "정말로 알림을 삭제하시겠습니까?",
|
||||
"notifications.column_settings.alert": "데스크탑 알림",
|
||||
"notifications.column_settings.favourite": "즐겨찾기",
|
||||
"notifications.column_settings.follow": "새 팔로워",
|
||||
"notifications.column_settings.mention": "답글",
|
||||
"notifications.column_settings.reblog": "부스트",
|
||||
"notifications.column_settings.show": "컬럼에 표시",
|
||||
"notifications.column_settings.sound": "효과음 재생",
|
||||
"onboarding.done": "완료",
|
||||
"onboarding.next": "다음",
|
||||
"onboarding.page_five.public_timelines": "연합 타임라인에서는 {domain}의 사람들이 팔로우 중인 Mastodon 전체 인스턴스의 공개 포스트를 표시합니다. 로컬 타임라인에서는 {domain} 만의 공개 포스트를 표시합니다.",
|
||||
"onboarding.page_four.home": "홈 타임라인에서는 내가 팔로우 중인 사람들의 포스트를 표시합니다.",
|
||||
"onboarding.page_four.notifications": "알림에서는 다른 사람들과의 연결을 표시합니다.",
|
||||
"onboarding.page_one.federation": "Mastodon은 누구나 참가할 수 있는 SNS입니다.",
|
||||
"onboarding.page_one.handle": "여러분은 지금 수많은 Mastodon 인스턴스 중 하나인 {domain}에 있습니다. 당신의 유저 이름은 {handle} 입니다.",
|
||||
"onboarding.page_one.welcome": "Mastodon에 어서 오세요!",
|
||||
"onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.",
|
||||
"onboarding.page_six.almost_done": "이상입니다.",
|
||||
"onboarding.page_six.appetoot": "Bon Appetoot!",
|
||||
"onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.",
|
||||
"onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.",
|
||||
"onboarding.page_six.guidelines": "커뮤니티 가이드라인",
|
||||
"onboarding.page_six.read_guidelines": "{guidelines}을 확인하는 것을 잊지 마세요.",
|
||||
"onboarding.page_six.various_app": "다양한 모바일 어플리케이션",
|
||||
"onboarding.page_three.profile": "[프로필 편집] 에서 자기 소개나 이름을 변경할 수 있습니다. 또한 다른 설정도 변경할 수 있습니다.",
|
||||
"onboarding.page_three.search": "검색 바에서 {illustration} 나 {introductions} 와 같이 특정 해시태그가 달린 포스트를 보거나, 사용자를 찾을 수 있습니다.",
|
||||
"onboarding.page_two.compose": "이 폼에서 포스팅 할 수 있습니다. 이미지나 공개 범위 설정, 스포일러 경고 설정은 아래 아이콘으로 설정할 수 있습니다.",
|
||||
"onboarding.skip": "건너뛰기",
|
||||
"privacy.change": "포스트의 프라이버시 설정을 변경",
|
||||
"privacy.direct.long": "멘션한 사용자에게만 공개",
|
||||
"privacy.direct.short": "다이렉트",
|
||||
"privacy.private.long": "팔로워에게만 공개",
|
||||
"privacy.private.short": "비공개",
|
||||
"privacy.public.long": "공개 타임라인에 표시",
|
||||
"privacy.public.short": "공개",
|
||||
"privacy.unlisted.long": "공개 타임라인에 표시하지 않음",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "취소",
|
||||
"report.placeholder": "코멘트",
|
||||
"report.submit": "신고하기",
|
||||
"report.target": "문제가 된 사용자",
|
||||
"search.placeholder": "검색",
|
||||
"search_results.total": "{count, number}건의 결과",
|
||||
"status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
|
||||
"status.delete": "삭제",
|
||||
"status.favourite": "즐겨찾기",
|
||||
"status.load_more": "더 보기",
|
||||
"status.media_hidden": "미디어 숨겨짐",
|
||||
"status.mention": "답장",
|
||||
"status.mute_conversation": "이 대화를 뮤트",
|
||||
"status.open": "상세 정보 표시",
|
||||
"status.reblog": "부스트",
|
||||
"status.reblogged_by": "{name}님이 부스트 했습니다",
|
||||
"status.reply": "답장",
|
||||
"status.replyAll": "전원에게 답장",
|
||||
"status.report": "신고",
|
||||
"status.sensitive_toggle": "클릭해서 표시하기",
|
||||
"status.sensitive_warning": "민감한 미디어",
|
||||
"status.show_less": "숨기기",
|
||||
"status.show_more": "더 보기",
|
||||
"status.unmute_conversation": "이 대화의 뮤트 해제하기",
|
||||
"tabs_bar.compose": "포스트",
|
||||
"tabs_bar.federated_timeline": "연합",
|
||||
"tabs_bar.home": "홈",
|
||||
"tabs_bar.local_timeline": "로컬",
|
||||
"tabs_bar.notifications": "알림",
|
||||
"upload_area.title": "드래그 & 드롭으로 업로드",
|
||||
"upload_button.label": "미디어 추가",
|
||||
"upload_form.undo": "재시도",
|
||||
"upload_progress.label": "업로드 중...",
|
||||
"video_player.expand": "동영상 자세히 보기",
|
||||
"video_player.toggle_sound": "소리 토글하기",
|
||||
"video_player.toggle_visible": "표시 전환",
|
||||
"video_player.video_error": "동영상 재생에 실패했습니다"
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
"account.media": "Media",
|
||||
"account.mention": "Vermeld @{name}",
|
||||
"account.mute": "Negeer @{name}",
|
||||
"account.posts": "Berichten",
|
||||
"account.posts": "Toots",
|
||||
"account.report": "Rapporteer @{name}",
|
||||
"account.requested": "Wacht op goedkeuring",
|
||||
"account.unblock": "Deblokkeer @{name}",
|
||||
@ -18,11 +18,17 @@
|
||||
"account.unfollow": "Ontvolgen",
|
||||
"account.unmute": "@{name} niet meer negeren",
|
||||
"boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Geblokkeerde gebruikers",
|
||||
"column.community": "Lokale tijdlijn",
|
||||
"column.favourites": "Favorieten",
|
||||
"column.follow_requests": "Volgverzoeken",
|
||||
"column.home": "Jouw tijdlijn",
|
||||
"column.home": "Start",
|
||||
"column.mutes": "Genegeerde gebruikers",
|
||||
"column.notifications": "Meldingen",
|
||||
"column.public": "Globale tijdlijn",
|
||||
@ -62,7 +68,7 @@
|
||||
"empty_column.community": "De lokale tijdlijn is leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!",
|
||||
"empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
|
||||
"empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.",
|
||||
"empty_column.home.inactivity": "Jouw tijdlijn is leeg. Wanneer je een tijdje inactief bent geweest wordt deze snel opnieuw aangemaakt.",
|
||||
"empty_column.home.inactivity": "Deze tijdlijn is leeg. Wanneer je een tijdje inactief bent geweest wordt deze snel opnieuw aangemaakt.",
|
||||
"empty_column.home.public_timeline": "de globale tijdlijn",
|
||||
"empty_column.notifications": "Je hebt nog geen meldingen. Heb interactie met andere mensen om het gesprek aan te gaan.",
|
||||
"empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere Mastodon-servers om het te vullen.",
|
||||
@ -109,7 +115,7 @@
|
||||
"onboarding.done": "Klaar",
|
||||
"onboarding.next": "Volgende",
|
||||
"onboarding.page_five.public_timelines": "De lokale tijdlijn toont openbare toots van iedereen op {domain}. De globale tijdlijn toont openbare toots van iedereen die door gebruikers van {domain} worden gevolgd, dus ook mensen van andere Mastodon-servers. Dit zijn de openbare tijdlijnen en vormen een uitstekende manier om nieuwe mensen te ontdekken.",
|
||||
"onboarding.page_four.home": "Jouw tijdlijn laat toots zien van mensen die jij volgt.",
|
||||
"onboarding.page_four.home": "Deze tijdlijn laat toots zien van mensen die jij volgt.",
|
||||
"onboarding.page_four.notifications": "De kolom met meldingen toont alle interacties die je met andere Mastodon-gebruikers hebt.",
|
||||
"onboarding.page_one.federation": "Mastodon is een netwerk van onafhankelijke servers die samen een groot sociaal netwerk vormen.",
|
||||
"onboarding.page_one.handle": "Je bevindt je nu op {domain}, dus is jouw volledige Mastodon-adres {handle}",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Niet op openbare tijdlijnen tonen",
|
||||
"privacy.unlisted.short": "Minder openbaar",
|
||||
"reply_indicator.cancel": "Annuleren",
|
||||
"report.heading": "Rapporteren",
|
||||
"report.placeholder": "Extra opmerkingen",
|
||||
"report.submit": "Verzenden",
|
||||
"report.target": "Rapporteren van",
|
||||
@ -162,7 +167,7 @@
|
||||
"status.unmute_conversation": "Conversatie niet meer negeren",
|
||||
"tabs_bar.compose": "Schrijven",
|
||||
"tabs_bar.federated_timeline": "Globaal",
|
||||
"tabs_bar.home": "Jouw tijdlijn",
|
||||
"tabs_bar.home": "Start",
|
||||
"tabs_bar.local_timeline": "Lokaal",
|
||||
"tabs_bar.notifications": "Meldingen",
|
||||
"upload_area.title": "Hierin slepen om te uploaden",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Avfølg",
|
||||
"account.unmute": "Avdemp @{name}",
|
||||
"boost_modal.combo": "You kan trykke {combo} for å hoppe over dette neste gang",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blokkerte brukere",
|
||||
"column.community": "Lokal tidslinje",
|
||||
"column.favourites": "Likt",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Ikke vis i offentlige tidslinjer",
|
||||
"privacy.unlisted.short": "Uoppført",
|
||||
"reply_indicator.cancel": "Avbryt",
|
||||
"report.heading": "Ny rapport",
|
||||
"report.placeholder": "Tilleggskommentarer",
|
||||
"report.submit": "Send inn",
|
||||
"report.target": "Rapporterer",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Quitar de sègre",
|
||||
"account.unmute": "Quitar de rescondre @{name}",
|
||||
"boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Personas blocadas",
|
||||
"column.community": "Flux d’actualitat public local",
|
||||
"column.favourites": "Favorits",
|
||||
@ -34,7 +40,7 @@
|
||||
"compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.",
|
||||
"compose_form.lock_disclaimer.lock": "clavat",
|
||||
"compose_form.placeholder": "A de qué pensatz ?",
|
||||
"compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste{domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias a Mastodons. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists",
|
||||
"compose_form.privacy_disclaimer": "Vòstre estatut privat serà enviat a las personas mencionadas sus {domains}. Vos fisatz d’aqueste {domainsCount, plural, one { servidor} other {s servidors}} per divulgar pas vòstre estatut ? Los estatuts privats foncionan pas que sus las instàncias de Mastodon. Se {domains} {domainsCount, plural, one {es pas una instància a Mastodon} other {son pas d'instàncias a Mastodon}}, i aurà pas d’indicacion disent que vòstre estatut es privat e poirà èsser partejat o èsser visible a de mond pas prevists",
|
||||
"compose_form.publish": "Tut",
|
||||
"compose_form.publish_loud": "{publish} !",
|
||||
"compose_form.sensitive": "Marcar lo mèdia coma sensible",
|
||||
@ -51,7 +57,7 @@
|
||||
"confirmations.mute.message": "Sètz segur de voler metre en silenci {name} ?",
|
||||
"emoji_button.activity": "Activitat",
|
||||
"emoji_button.flags": "Drapèus",
|
||||
"emoji_button.food": "Manjar e beure",
|
||||
"emoji_button.food": "Beure e manjar",
|
||||
"emoji_button.label": "Inserir un emoji",
|
||||
"emoji_button.nature": "Natura",
|
||||
"emoji_button.objects": "Objèctes",
|
||||
@ -136,10 +142,9 @@
|
||||
"privacy.unlisted.long": "Mostrar pas dins los fluxes publics",
|
||||
"privacy.unlisted.short": "Pas-listat",
|
||||
"reply_indicator.cancel": "Anullar",
|
||||
"report.heading": "Nòu senhalament",
|
||||
"report.placeholder": "Comentaris addicionals",
|
||||
"report.submit": "Mandat",
|
||||
"report.target": "Senhalament",
|
||||
"report.submit": "Mandar",
|
||||
"report.target": "Senhalar {target}",
|
||||
"search.placeholder": "Recercar",
|
||||
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
|
||||
"status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
|
||||
|
@ -6,7 +6,7 @@
|
||||
"account.follow": "Obserwuj",
|
||||
"account.followers": "Obserwujący",
|
||||
"account.follows": "Obserwacje",
|
||||
"account.follows_you": "Obserwują cię",
|
||||
"account.follows_you": "Obserwuje cię",
|
||||
"account.media": "Media",
|
||||
"account.mention": "Wspomnij o @{name}",
|
||||
"account.mute": "Wycisz @{name}",
|
||||
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Przestań obserwować",
|
||||
"account.unmute": "Cofnij wyciszenie @{name}",
|
||||
"boost_modal.combo": "Naciśnij {combo}, aby pominąć to następnym razem",
|
||||
"bundle_column_error.body": "Coś poszło nie tak podczas ładowania tego składnika.",
|
||||
"bundle_column_error.retry": "Spróbuj ponownie",
|
||||
"bundle_column_error.title": "Błąd sieci",
|
||||
"bundle_modal_error.close": "Zamknij",
|
||||
"bundle_modal_error.message": "Coś poszło nie tak podczas ładowania tego składnika.",
|
||||
"bundle_modal_error.retry": "Spróbuj ponownie",
|
||||
"column.blocks": "Zablokowani użytkownicy",
|
||||
"column.community": "Lokalna oś czasu",
|
||||
"column.favourites": "Ulubione",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Niewidoczne na publicznych osiach czasu",
|
||||
"privacy.unlisted.short": "Niewidoczne",
|
||||
"reply_indicator.cancel": "Anuluj",
|
||||
"report.heading": "Zgłoś {target}",
|
||||
"report.placeholder": "Dodatkowe komentarze",
|
||||
"report.submit": "Wyślij",
|
||||
"report.target": "Zgłaszanie {target}",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Deixar de seguir",
|
||||
"account.unmute": "Não silenciar @{name}",
|
||||
"boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Utilizadores Bloqueados",
|
||||
"column.community": "Local",
|
||||
"column.favourites": "Favoritos",
|
||||
@ -72,7 +78,6 @@
|
||||
"getting_started.faq": "FAQ",
|
||||
"getting_started.heading": "Primeiros passos",
|
||||
"getting_started.open_source_notice": "Mastodon é software de fonte aberta. Podes contribuir ou repostar problemas no GitHub do projecto: {github}.",
|
||||
"getting_started.support": "{faq} • {userguide} • {apps}",
|
||||
"getting_started.userguide": "User Guide",
|
||||
"home.column_settings.advanced": "Avançado",
|
||||
"home.column_settings.basic": "Básico",
|
||||
@ -107,7 +112,6 @@
|
||||
"notifications.column_settings.reblog": "Partilhas:",
|
||||
"notifications.column_settings.show": "Mostrar nas colunas",
|
||||
"notifications.column_settings.sound": "Reproduzir som",
|
||||
"notifications.settings": "Parâmetros da listagem de Notificações",
|
||||
"onboarding.done": "Done",
|
||||
"onboarding.next": "Next",
|
||||
"onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.",
|
||||
@ -138,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Não publicar nos feeds públicos",
|
||||
"privacy.unlisted.short": "Não listar",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"report.heading": "Nova denúncia",
|
||||
"report.placeholder": "Comentários adicionais",
|
||||
"report.submit": "Enviar",
|
||||
"report.target": "Denunciar",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Deixar de seguir",
|
||||
"account.unmute": "Não silenciar @{name}",
|
||||
"boost_modal.combo": "Pode clicar {combo} para não voltar a ver",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Utilizadores Bloqueados",
|
||||
"column.community": "Local",
|
||||
"column.favourites": "Favoritos",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Não publicar nos feeds públicos",
|
||||
"privacy.unlisted.short": "Não listar",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"report.heading": "Nova denúncia",
|
||||
"report.placeholder": "Comentários adicionais",
|
||||
"report.submit": "Enviar",
|
||||
"report.target": "Denunciar",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Отписаться",
|
||||
"account.unmute": "Снять глушение",
|
||||
"boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Список блокировки",
|
||||
"column.community": "Локальная лента",
|
||||
"column.favourites": "Понравившееся",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Не показывать в лентах",
|
||||
"privacy.unlisted.short": "Скрытый",
|
||||
"reply_indicator.cancel": "Отмена",
|
||||
"report.heading": "Новая жалоба",
|
||||
"report.placeholder": "Комментарий",
|
||||
"report.submit": "Отправить",
|
||||
"report.target": "Жалуемся на",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Unfollow",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Local timeline",
|
||||
"column.favourites": "Favourites",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Do not post to public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Cancel",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Takipten vazgeç",
|
||||
"account.unmute": "Sesi aç @{name}",
|
||||
"boost_modal.combo": "Bir dahaki sefere {combo} tuşuna basabilirsiniz",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Engellenen kullanıcılar",
|
||||
"column.community": "Yerel zaman tüneli",
|
||||
"column.favourites": "Favoriler",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Herkese açık zaman tüneline gönderme",
|
||||
"privacy.unlisted.short": "Listelenmemiş",
|
||||
"reply_indicator.cancel": "İptal",
|
||||
"report.heading": "Yeni rapor",
|
||||
"report.placeholder": "Ek yorumlar",
|
||||
"report.submit": "Gönder",
|
||||
"report.target": "Raporlama",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "Відписатися",
|
||||
"account.unmute": "Зняти глушення",
|
||||
"boost_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "Заблоковані користувачі",
|
||||
"column.community": "Локальна стрічка",
|
||||
"column.favourites": "Вподобане",
|
||||
@ -136,7 +142,6 @@
|
||||
"privacy.unlisted.long": "Не показувати у публічних стрічках",
|
||||
"privacy.unlisted.short": "Прихований",
|
||||
"reply_indicator.cancel": "Відмінити",
|
||||
"report.heading": "Нова скарга",
|
||||
"report.placeholder": "Додаткові коментарі",
|
||||
"report.submit": "Відправити",
|
||||
"report.target": "Скаржимося на",
|
||||
|
2
app/javascript/mastodon/locales/whitelist_ko.json
Normal file
2
app/javascript/mastodon/locales/whitelist_ko.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
@ -1,3 +1,2 @@
|
||||
[
|
||||
"getting_started.support"
|
||||
]
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "取消关注",
|
||||
"account.unmute": "取消 @{name} 的静音",
|
||||
"boost_modal.combo": "如你想在下次路过时显示,请按{combo},",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "屏蔽用户",
|
||||
"column.community": "本站时间轴",
|
||||
"column.favourites": "赞过的嘟文",
|
||||
@ -72,7 +78,6 @@
|
||||
"getting_started.faq": "FAQ",
|
||||
"getting_started.heading": "开始使用",
|
||||
"getting_started.open_source_notice": "Mastodon 是一个开放源码的软件。你可以在官方 GitHub ({github}) 贡献或者回报问题。",
|
||||
"getting_started.support": "{faq} • {userguide} • {apps}",
|
||||
"getting_started.userguide": "User Guide",
|
||||
"home.column_settings.advanced": "高端",
|
||||
"home.column_settings.basic": "基本",
|
||||
@ -107,7 +112,6 @@
|
||||
"notifications.column_settings.reblog": "你的嘟文被转嘟:",
|
||||
"notifications.column_settings.show": "在通知栏显示",
|
||||
"notifications.column_settings.sound": "播放音效",
|
||||
"notifications.settings": "字段设置",
|
||||
"onboarding.done": "出发!",
|
||||
"onboarding.next": "下一步",
|
||||
"onboarding.page_five.public_timelines": "本站时间轴显示来自 {domain} 的所有人的公共嘟文。 跨站公共时间轴显示 {domain} 上的各位关注的来自所有Mastodon服务器实例上的人发表的公共嘟文。这些就是寻人好去处的公共时间轴啦。",
|
||||
@ -138,7 +142,6 @@
|
||||
"privacy.unlisted.long": "公开,但不在公共时间轴显示",
|
||||
"privacy.unlisted.short": "公开",
|
||||
"reply_indicator.cancel": "取消",
|
||||
"report.heading": "举报",
|
||||
"report.placeholder": "额外消息",
|
||||
"report.submit": "提交",
|
||||
"report.target": "Reporting",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "取消關注",
|
||||
"account.unmute": "取消 @{name} 的靜音",
|
||||
"boost_modal.combo": "如你想在下次路過這顯示,請按{combo},",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "封鎖用戶",
|
||||
"column.community": "本站時間軸",
|
||||
"column.favourites": "喜歡的文章",
|
||||
@ -72,7 +78,6 @@
|
||||
"getting_started.faq": "常見問題",
|
||||
"getting_started.heading": "開始使用",
|
||||
"getting_started.open_source_notice": "Mastodon(萬象)是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。",
|
||||
"getting_started.support": "{faq} • {userguide} • {apps}",
|
||||
"getting_started.userguide": "使用指南",
|
||||
"home.column_settings.advanced": "進階",
|
||||
"home.column_settings.basic": "基本",
|
||||
@ -107,7 +112,6 @@
|
||||
"notifications.column_settings.reblog": "轉推你的文章:",
|
||||
"notifications.column_settings.show": "在通知欄顯示",
|
||||
"notifications.column_settings.sound": "播放音效",
|
||||
"notifications.settings": "欄位設定",
|
||||
"onboarding.done": "開始使用",
|
||||
"onboarding.next": "繼續",
|
||||
"onboarding.page_five.public_timelines": "「本站時間軸」顯示在 {domain} 各用戶的公開文章。「跨站時間軸」顯示在 {domain} 各人關注的所有用戶(包括其他服務站)的公開文章。這些都是「公共時間軸」,是認識新朋友的好地方。",
|
||||
@ -138,7 +142,6 @@
|
||||
"privacy.unlisted.long": "公開,但不在公共時間軸顯示",
|
||||
"privacy.unlisted.short": "公開",
|
||||
"reply_indicator.cancel": "取消",
|
||||
"report.heading": "舉報",
|
||||
"report.placeholder": "額外訊息",
|
||||
"report.submit": "提交",
|
||||
"report.target": "舉報",
|
||||
|
@ -18,6 +18,12 @@
|
||||
"account.unfollow": "取消關注",
|
||||
"account.unmute": "不再消音 @{name}",
|
||||
"boost_modal.combo": "下次你可以按 {combo} 來跳過",
|
||||
"bundle_column_error.body": "Something went wrong while loading this component.",
|
||||
"bundle_column_error.retry": "Try again",
|
||||
"bundle_column_error.title": "Network error",
|
||||
"bundle_modal_error.close": "Close",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.retry": "Try again",
|
||||
"column.blocks": "封鎖的使用者",
|
||||
"column.community": "本地時間軸",
|
||||
"column.favourites": "最愛",
|
||||
@ -72,7 +78,6 @@
|
||||
"getting_started.faq": "FAQ",
|
||||
"getting_started.heading": "馬上開始",
|
||||
"getting_started.open_source_notice": "Mastodon 是開源軟體。你可以在 GitHub {github} 上做出貢獻或是回報問題。",
|
||||
"getting_started.support": "{faq} • {userguide} • {apps}",
|
||||
"getting_started.userguide": "使用者指南",
|
||||
"home.column_settings.advanced": "進階",
|
||||
"home.column_settings.basic": "基本",
|
||||
@ -107,7 +112,6 @@
|
||||
"notifications.column_settings.reblog": "轉推:",
|
||||
"notifications.column_settings.show": "顯示在欄位中",
|
||||
"notifications.column_settings.sound": "播放音效",
|
||||
"notifications.settings": "欄位設定",
|
||||
"onboarding.done": "完成",
|
||||
"onboarding.next": "下一步",
|
||||
"onboarding.page_five.public_timelines": "本地時間軸顯示 {domain} 上所有人的公開貼文。聯盟時間軸顯示 {domain} 上所有人關注的公開貼文。這就是公開時間軸,發現新朋友的好地方。",
|
||||
@ -138,7 +142,6 @@
|
||||
"privacy.unlisted.long": "不要貼到公開時間軸",
|
||||
"privacy.unlisted.short": "不列出來",
|
||||
"reply_indicator.cancel": "取消",
|
||||
"report.heading": "新的通報",
|
||||
"report.placeholder": "更多訊息",
|
||||
"report.submit": "送出",
|
||||
"report.target": "通報中",
|
||||
|
@ -1,9 +1,5 @@
|
||||
const perf = require('./performance');
|
||||
|
||||
// import default stylesheet with variables
|
||||
require('font-awesome/css/font-awesome.css');
|
||||
require('mastodon-application-style');
|
||||
|
||||
function onDomContentLoaded(callback) {
|
||||
if (document.readyState !== 'loading') {
|
||||
callback();
|
||||
@ -20,6 +16,14 @@ function main() {
|
||||
|
||||
require.context('../images/', true);
|
||||
|
||||
if (window.history && history.replaceState) {
|
||||
const { pathname, search, hash } = window.location;
|
||||
const path = pathname + search + hash;
|
||||
if (!(/^\/web[$/]/).test(path)) {
|
||||
history.replaceState(null, document.title, `/web${path}`);
|
||||
}
|
||||
}
|
||||
|
||||
onDomContentLoaded(() => {
|
||||
const mountNode = document.getElementById('mastodon');
|
||||
const props = JSON.parse(mountNode.getAttribute('data-props'));
|
||||
|
@ -44,7 +44,7 @@ import {
|
||||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
} from '../actions/favourites';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
const normalizeAccount = (state, account) => {
|
||||
account = { ...account };
|
||||
@ -53,7 +53,7 @@ const normalizeAccount = (state, account) => {
|
||||
delete account.following_count;
|
||||
delete account.statuses_count;
|
||||
|
||||
return state.set(account.id, Immutable.fromJS(account));
|
||||
return state.set(account.id, fromJS(account));
|
||||
};
|
||||
|
||||
const normalizeAccounts = (state, accounts) => {
|
||||
@ -82,7 +82,7 @@ const normalizeAccountsFromStatuses = (state, statuses) => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function accounts(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
|
@ -46,9 +46,9 @@ import {
|
||||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
} from '../actions/favourites';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
const normalizeAccount = (state, account) => state.set(account.id, Immutable.fromJS({
|
||||
const normalizeAccount = (state, account) => state.set(account.id, fromJS({
|
||||
followers_count: account.followers_count,
|
||||
following_count: account.following_count,
|
||||
statuses_count: account.statuses_count,
|
||||
@ -80,12 +80,12 @@ const normalizeAccountsFromStatuses = (state, statuses) => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function accountsCounters(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
return state.merge(action.state.get('accounts').map(item => Immutable.fromJS({
|
||||
return state.merge(action.state.get('accounts').map(item => fromJS({
|
||||
followers_count: item.get('followers_count'),
|
||||
following_count: item.get('following_count'),
|
||||
statuses_count: item.get('statuses_count'),
|
||||
|
@ -3,14 +3,14 @@ import {
|
||||
ALERT_DISMISS,
|
||||
ALERT_CLEAR,
|
||||
} from '../actions/alerts';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
const initialState = Immutable.List([]);
|
||||
const initialState = ImmutableList([]);
|
||||
|
||||
export default function alerts(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case ALERT_SHOW:
|
||||
return state.push(Immutable.Map({
|
||||
return state.push(ImmutableMap({
|
||||
key: state.size > 0 ? state.last().get('key') + 1 : 0,
|
||||
title: action.title,
|
||||
message: action.message,
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { STATUS_CARD_FETCH_SUCCESS } from '../actions/cards';
|
||||
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function cards(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STATUS_CARD_FETCH_SUCCESS:
|
||||
return state.set(action.id, Immutable.fromJS(action.card));
|
||||
return state.set(action.id, fromJS(action.card));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -25,12 +25,12 @@ import {
|
||||
} from '../actions/compose';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
import uuid from '../uuid';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
mounted: false,
|
||||
advanced_options: Immutable.Map({
|
||||
advanced_options: ImmutableMap({
|
||||
do_not_federate: false,
|
||||
}),
|
||||
sensitive: false,
|
||||
@ -44,20 +44,21 @@ const initialState = Immutable.Map({
|
||||
is_submitting: false,
|
||||
is_uploading: false,
|
||||
progress: 0,
|
||||
media_attachments: Immutable.List(),
|
||||
media_attachments: ImmutableList(),
|
||||
suggestion_token: null,
|
||||
suggestions: Immutable.List(),
|
||||
suggestions: ImmutableList(),
|
||||
me: null,
|
||||
default_advanced_options: Immutable.Map({
|
||||
default_advanced_options: ImmutableMap({
|
||||
do_not_federate: false,
|
||||
}),
|
||||
default_privacy: 'public',
|
||||
default_sensitive: false,
|
||||
resetFileKey: Math.floor((Math.random() * 0x10000)),
|
||||
idempotencyKey: null,
|
||||
});
|
||||
|
||||
function statusToTextMentions(state, status) {
|
||||
let set = Immutable.OrderedSet([]);
|
||||
let set = ImmutableOrderedSet([]);
|
||||
let me = state.get('me');
|
||||
|
||||
if (status.getIn(['account', 'id']) !== me) {
|
||||
@ -83,6 +84,8 @@ function clearAll(state) {
|
||||
};
|
||||
|
||||
function appendMedia(state, media) {
|
||||
const prevSize = state.get('media_attachments').size;
|
||||
|
||||
return state.withMutations(map => {
|
||||
map.update('media_attachments', list => list.push(media));
|
||||
map.set('is_uploading', false);
|
||||
@ -90,6 +93,10 @@ function appendMedia(state, media) {
|
||||
map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`);
|
||||
map.set('focusDate', new Date());
|
||||
map.set('idempotencyKey', uuid());
|
||||
|
||||
if (prevSize === 0 && state.get('default_sensitive')) {
|
||||
map.set('sensitive', true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -112,7 +119,7 @@ const insertSuggestion = (state, position, token, completion) => {
|
||||
return state.withMutations(map => {
|
||||
map.update('text', oldText => `${oldText.slice(0, position)}${completion} ${oldText.slice(position + token.length)}`);
|
||||
map.set('suggestion_token', null);
|
||||
map.update('suggestions', Immutable.List(), list => list.clear());
|
||||
map.update('suggestions', ImmutableList(), list => list.clear());
|
||||
map.set('focusDate', new Date());
|
||||
map.set('idempotencyKey', uuid());
|
||||
});
|
||||
@ -180,7 +187,7 @@ export default function compose(state = initialState, action) {
|
||||
map.set('in_reply_to', action.status.get('id'));
|
||||
map.set('text', statusToTextMentions(state, action.status));
|
||||
map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
|
||||
map.set('advanced_options', new Immutable.Map({
|
||||
map.set('advanced_options', new ImmutableMap({
|
||||
do_not_federate: /👁\ufe0f?<\/p>$/.test(action.status.get('content')),
|
||||
}));
|
||||
map.set('focusDate', new Date());
|
||||
@ -216,7 +223,7 @@ export default function compose(state = initialState, action) {
|
||||
map.set('is_uploading', true);
|
||||
});
|
||||
case COMPOSE_UPLOAD_SUCCESS:
|
||||
return appendMedia(state, Immutable.fromJS(action.media));
|
||||
return appendMedia(state, fromJS(action.media));
|
||||
case COMPOSE_UPLOAD_FAIL:
|
||||
return state.set('is_uploading', false);
|
||||
case COMPOSE_UPLOAD_UNDO:
|
||||
@ -229,9 +236,9 @@ export default function compose(state = initialState, action) {
|
||||
.set('focusDate', new Date())
|
||||
.set('idempotencyKey', uuid());
|
||||
case COMPOSE_SUGGESTIONS_CLEAR:
|
||||
return state.update('suggestions', Immutable.List(), list => list.clear()).set('suggestion_token', null);
|
||||
return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null);
|
||||
case COMPOSE_SUGGESTIONS_READY:
|
||||
return state.set('suggestions', Immutable.List(action.accounts.map(item => item.id))).set('suggestion_token', action.token);
|
||||
return state.set('suggestions', ImmutableList(action.accounts.map(item => item.id))).set('suggestion_token', action.token);
|
||||
case COMPOSE_SUGGESTION_SELECT:
|
||||
return insertSuggestion(state, action.position, action.token, action.completion);
|
||||
case TIMELINE_DELETE:
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
ancestors: Immutable.Map(),
|
||||
descendants: Immutable.Map(),
|
||||
const initialState = ImmutableMap({
|
||||
ancestors: ImmutableMap(),
|
||||
descendants: ImmutableMap(),
|
||||
});
|
||||
|
||||
const normalizeContext = (state, id, ancestors, descendants) => {
|
||||
@ -18,12 +18,12 @@ const normalizeContext = (state, id, ancestors, descendants) => {
|
||||
};
|
||||
|
||||
const deleteFromContexts = (state, id) => {
|
||||
state.getIn(['descendants', id], Immutable.List()).forEach(descendantId => {
|
||||
state = state.updateIn(['ancestors', descendantId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
|
||||
state.getIn(['descendants', id], ImmutableList()).forEach(descendantId => {
|
||||
state = state.updateIn(['ancestors', descendantId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
|
||||
});
|
||||
|
||||
state.getIn(['ancestors', id], Immutable.List()).forEach(ancestorId => {
|
||||
state = state.updateIn(['descendants', ancestorId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
|
||||
state.getIn(['ancestors', id], ImmutableList()).forEach(ancestorId => {
|
||||
state = state.updateIn(['descendants', ancestorId], ImmutableList(), list => list.filterNot(itemId => itemId === id));
|
||||
});
|
||||
|
||||
state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
|
||||
@ -34,7 +34,7 @@ const deleteFromContexts = (state, id) => {
|
||||
export default function contexts(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case CONTEXT_FETCH_SUCCESS:
|
||||
return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants));
|
||||
return normalizeContext(state, action.id, fromJS(action.ancestors), fromJS(action.descendants));
|
||||
case TIMELINE_DELETE:
|
||||
return deleteFromContexts(state, action.id);
|
||||
default:
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { combineReducers } from 'redux-immutable';
|
||||
import timelines from './timelines';
|
||||
import meta from './meta';
|
||||
import compose from './compose';
|
||||
import alerts from './alerts';
|
||||
import { loadingBarReducer } from 'react-redux-loading-bar';
|
||||
import modal from './modal';
|
||||
@ -9,21 +8,21 @@ import user_lists from './user_lists';
|
||||
import accounts from './accounts';
|
||||
import accounts_counters from './accounts_counters';
|
||||
import statuses from './statuses';
|
||||
import media_attachments from './media_attachments';
|
||||
import relationships from './relationships';
|
||||
import search from './search';
|
||||
import notifications from './notifications';
|
||||
import settings from './settings';
|
||||
import local_settings from '../../glitch/reducers/local_settings';
|
||||
import status_lists from './status_lists';
|
||||
import cards from './cards';
|
||||
import reports from './reports';
|
||||
import contexts from './contexts';
|
||||
import compose from './compose';
|
||||
import search from './search';
|
||||
import media_attachments from './media_attachments';
|
||||
import notifications from './notifications';
|
||||
|
||||
export default combineReducers({
|
||||
const reducers = {
|
||||
timelines,
|
||||
meta,
|
||||
compose,
|
||||
alerts,
|
||||
loadingBar: loadingBarReducer,
|
||||
modal,
|
||||
@ -31,14 +30,17 @@ export default combineReducers({
|
||||
status_lists,
|
||||
accounts,
|
||||
accounts_counters,
|
||||
media_attachments,
|
||||
statuses,
|
||||
relationships,
|
||||
search,
|
||||
notifications,
|
||||
settings,
|
||||
local_settings,
|
||||
cards,
|
||||
reports,
|
||||
contexts,
|
||||
});
|
||||
compose,
|
||||
search,
|
||||
media_attachments,
|
||||
notifications,
|
||||
};
|
||||
|
||||
export default combineReducers(reducers);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
accept_content_types: [],
|
||||
});
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
streaming_api_base_url: null,
|
||||
access_token: null,
|
||||
me: null,
|
||||
|
@ -11,10 +11,10 @@ import {
|
||||
} from '../actions/notifications';
|
||||
import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
items: Immutable.List(),
|
||||
const initialState = ImmutableMap({
|
||||
items: ImmutableList(),
|
||||
next: null,
|
||||
top: true,
|
||||
unread: 0,
|
||||
@ -22,7 +22,7 @@ const initialState = Immutable.Map({
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
const notificationToMap = notification => Immutable.Map({
|
||||
const notificationToMap = notification => ImmutableMap({
|
||||
id: notification.id,
|
||||
type: notification.type,
|
||||
account: notification.account.id,
|
||||
@ -46,7 +46,7 @@ const normalizeNotification = (state, notification) => {
|
||||
};
|
||||
|
||||
const normalizeNotifications = (state, notifications, next) => {
|
||||
let items = Immutable.List();
|
||||
let items = ImmutableList();
|
||||
const loaded = state.get('loaded');
|
||||
|
||||
notifications.forEach((n, i) => {
|
||||
@ -64,7 +64,7 @@ const normalizeNotifications = (state, notifications, next) => {
|
||||
};
|
||||
|
||||
const appendNormalizedNotifications = (state, notifications, next) => {
|
||||
let items = Immutable.List();
|
||||
let items = ImmutableList();
|
||||
|
||||
notifications.forEach((n, i) => {
|
||||
items = items.set(i, notificationToMap(n));
|
||||
@ -110,7 +110,7 @@ export default function notifications(state = initialState, action) {
|
||||
case ACCOUNT_BLOCK_SUCCESS:
|
||||
return filterNotifications(state, action.relationship);
|
||||
case NOTIFICATIONS_CLEAR:
|
||||
return state.set('items', Immutable.List()).set('next', null);
|
||||
return state.set('items', ImmutableList()).set('next', null);
|
||||
case TIMELINE_DELETE:
|
||||
return deleteByStatus(state, action.id);
|
||||
default:
|
||||
|
@ -11,9 +11,9 @@ import {
|
||||
DOMAIN_BLOCK_SUCCESS,
|
||||
DOMAIN_UNBLOCK_SUCCESS,
|
||||
} from '../actions/domain_blocks';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
const normalizeRelationship = (state, relationship) => state.set(relationship.id, Immutable.fromJS(relationship));
|
||||
const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship));
|
||||
|
||||
const normalizeRelationships = (state, relationships) => {
|
||||
relationships.forEach(relationship => {
|
||||
@ -23,7 +23,7 @@ const normalizeRelationships = (state, relationships) => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function relationships(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
|
@ -7,13 +7,13 @@ import {
|
||||
REPORT_STATUS_TOGGLE,
|
||||
REPORT_COMMENT_CHANGE,
|
||||
} from '../actions/reports';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, Set as ImmutableSet } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
new: Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
new: ImmutableMap({
|
||||
isSubmitting: false,
|
||||
account_id: null,
|
||||
status_ids: Immutable.Set(),
|
||||
status_ids: ImmutableSet(),
|
||||
comment: '',
|
||||
}),
|
||||
});
|
||||
@ -26,14 +26,14 @@ export default function reports(state = initialState, action) {
|
||||
map.setIn(['new', 'account_id'], action.account.get('id'));
|
||||
|
||||
if (state.getIn(['new', 'account_id']) !== action.account.get('id')) {
|
||||
map.setIn(['new', 'status_ids'], action.status ? Immutable.Set([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : Immutable.Set());
|
||||
map.setIn(['new', 'status_ids'], action.status ? ImmutableSet([action.status.getIn(['reblog', 'id'], action.status.get('id'))]) : ImmutableSet());
|
||||
map.setIn(['new', 'comment'], '');
|
||||
} else {
|
||||
map.updateIn(['new', 'status_ids'], Immutable.Set(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id'))));
|
||||
map.updateIn(['new', 'status_ids'], ImmutableSet(), set => set.add(action.status.getIn(['reblog', 'id'], action.status.get('id'))));
|
||||
}
|
||||
});
|
||||
case REPORT_STATUS_TOGGLE:
|
||||
return state.updateIn(['new', 'status_ids'], Immutable.Set(), set => {
|
||||
return state.updateIn(['new', 'status_ids'], ImmutableSet(), set => {
|
||||
if (action.checked) {
|
||||
return set.add(action.statusId);
|
||||
}
|
||||
@ -50,7 +50,7 @@ export default function reports(state = initialState, action) {
|
||||
case REPORT_SUBMIT_SUCCESS:
|
||||
return state.withMutations(map => {
|
||||
map.setIn(['new', 'account_id'], null);
|
||||
map.setIn(['new', 'status_ids'], Immutable.Set());
|
||||
map.setIn(['new', 'status_ids'], ImmutableSet());
|
||||
map.setIn(['new', 'comment'], '');
|
||||
map.setIn(['new', 'isSubmitting'], false);
|
||||
});
|
||||
|
@ -5,13 +5,13 @@ import {
|
||||
SEARCH_SHOW,
|
||||
} from '../actions/search';
|
||||
import { COMPOSE_MENTION, COMPOSE_REPLY } from '../actions/compose';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
value: '',
|
||||
submitted: false,
|
||||
hidden: false,
|
||||
results: Immutable.Map(),
|
||||
results: ImmutableMap(),
|
||||
});
|
||||
|
||||
export default function search(state = initialState, action) {
|
||||
@ -21,7 +21,7 @@ export default function search(state = initialState, action) {
|
||||
case SEARCH_CLEAR:
|
||||
return state.withMutations(map => {
|
||||
map.set('value', '');
|
||||
map.set('results', Immutable.Map());
|
||||
map.set('results', ImmutableMap());
|
||||
map.set('submitted', false);
|
||||
map.set('hidden', false);
|
||||
});
|
||||
@ -31,10 +31,10 @@ export default function search(state = initialState, action) {
|
||||
case COMPOSE_MENTION:
|
||||
return state.set('hidden', true);
|
||||
case SEARCH_FETCH_SUCCESS:
|
||||
return state.set('results', Immutable.Map({
|
||||
accounts: Immutable.List(action.results.accounts.map(item => item.id)),
|
||||
statuses: Immutable.List(action.results.statuses.map(item => item.id)),
|
||||
hashtags: Immutable.List(action.results.hashtags),
|
||||
return state.set('results', ImmutableMap({
|
||||
accounts: ImmutableList(action.results.accounts.map(item => item.id)),
|
||||
statuses: ImmutableList(action.results.statuses.map(item => item.id)),
|
||||
hashtags: ImmutableList(action.results.hashtags),
|
||||
})).set('submitted', true);
|
||||
default:
|
||||
return state;
|
||||
|
@ -1,40 +1,40 @@
|
||||
import { SETTING_CHANGE } from '../actions/settings';
|
||||
import { COLUMN_ADD, COLUMN_REMOVE, COLUMN_MOVE } from '../actions/columns';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import uuid from '../uuid';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
onboarded: false,
|
||||
layout: 'auto',
|
||||
|
||||
home: Immutable.Map({
|
||||
shows: Immutable.Map({
|
||||
home: ImmutableMap({
|
||||
shows: ImmutableMap({
|
||||
reblog: true,
|
||||
reply: true,
|
||||
}),
|
||||
|
||||
regex: Immutable.Map({
|
||||
regex: ImmutableMap({
|
||||
body: '',
|
||||
}),
|
||||
}),
|
||||
|
||||
notifications: Immutable.Map({
|
||||
alerts: Immutable.Map({
|
||||
notifications: ImmutableMap({
|
||||
alerts: ImmutableMap({
|
||||
follow: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
mention: true,
|
||||
}),
|
||||
|
||||
shows: Immutable.Map({
|
||||
shows: ImmutableMap({
|
||||
follow: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
mention: true,
|
||||
}),
|
||||
|
||||
sounds: Immutable.Map({
|
||||
sounds: ImmutableMap({
|
||||
follow: true,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
@ -42,20 +42,20 @@ const initialState = Immutable.Map({
|
||||
}),
|
||||
}),
|
||||
|
||||
community: Immutable.Map({
|
||||
regex: Immutable.Map({
|
||||
community: ImmutableMap({
|
||||
regex: ImmutableMap({
|
||||
body: '',
|
||||
}),
|
||||
}),
|
||||
|
||||
public: Immutable.Map({
|
||||
regex: Immutable.Map({
|
||||
public: ImmutableMap({
|
||||
regex: ImmutableMap({
|
||||
body: '',
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const defaultColumns = Immutable.fromJS([
|
||||
const defaultColumns = fromJS([
|
||||
{ id: 'COMPOSE', uuid: uuid(), params: {} },
|
||||
{ id: 'HOME', uuid: uuid(), params: {} },
|
||||
{ id: 'NOTIFICATIONS', uuid: uuid(), params: {} },
|
||||
@ -83,7 +83,7 @@ export default function settings(state = initialState, action) {
|
||||
case SETTING_CHANGE:
|
||||
return state.setIn(action.key, action.value);
|
||||
case COLUMN_ADD:
|
||||
return state.update('columns', list => list.push(Immutable.fromJS({ id: action.id, uuid: uuid(), params: action.params })));
|
||||
return state.update('columns', list => list.push(fromJS({ id: action.id, uuid: uuid(), params: action.params })));
|
||||
case COLUMN_REMOVE:
|
||||
return state.update('columns', list => list.filterNot(item => item.get('uuid') === action.uuid));
|
||||
case COLUMN_MOVE:
|
||||
|
@ -2,13 +2,13 @@ import {
|
||||
FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
} from '../actions/favourites';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
favourites: Immutable.Map({
|
||||
const initialState = ImmutableMap({
|
||||
favourites: ImmutableMap({
|
||||
next: null,
|
||||
loaded: false,
|
||||
items: Immutable.List(),
|
||||
items: ImmutableList(),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -16,7 +16,7 @@ const normalizeList = (state, listType, statuses, next) => {
|
||||
return state.update(listType, listMap => listMap.withMutations(map => {
|
||||
map.set('next', next);
|
||||
map.set('loaded', true);
|
||||
map.set('items', Immutable.List(statuses.map(item => item.id)));
|
||||
map.set('items', ImmutableList(statuses.map(item => item.id)));
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -33,7 +33,7 @@ import {
|
||||
FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
} from '../actions/favourites';
|
||||
import { SEARCH_FETCH_SUCCESS } from '../actions/search';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
const normalizeStatus = (state, status) => {
|
||||
if (!status) {
|
||||
@ -51,7 +51,7 @@ const normalizeStatus = (state, status) => {
|
||||
const searchContent = [status.spoiler_text, status.content].join(' ').replace(/<br \/>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
normalStatus.search_index = new DOMParser().parseFromString(searchContent, 'text/html').documentElement.textContent;
|
||||
|
||||
return state.update(status.id, Immutable.Map(), map => map.mergeDeep(Immutable.fromJS(normalStatus)));
|
||||
return state.update(status.id, ImmutableMap(), map => map.mergeDeep(fromJS(normalStatus)));
|
||||
};
|
||||
|
||||
const normalizeStatuses = (state, statuses) => {
|
||||
@ -82,7 +82,7 @@ const filterStatuses = (state, relationship) => {
|
||||
return state;
|
||||
};
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function statuses(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
|
@ -15,25 +15,25 @@ import {
|
||||
ACCOUNT_BLOCK_SUCCESS,
|
||||
ACCOUNT_MUTE_SUCCESS,
|
||||
} from '../actions/accounts';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map();
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
const initialTimeline = Immutable.Map({
|
||||
const initialTimeline = ImmutableMap({
|
||||
unread: 0,
|
||||
online: false,
|
||||
top: true,
|
||||
loaded: false,
|
||||
isLoading: false,
|
||||
next: false,
|
||||
items: Immutable.List(),
|
||||
items: ImmutableList(),
|
||||
});
|
||||
|
||||
const normalizeTimeline = (state, timeline, statuses, next) => {
|
||||
const ids = Immutable.List(statuses.map(status => status.get('id')));
|
||||
const ids = ImmutableList(statuses.map(status => status.get('id')));
|
||||
const wasLoaded = state.getIn([timeline, 'loaded']);
|
||||
const hadNext = state.getIn([timeline, 'next']);
|
||||
const oldIds = state.getIn([timeline, 'items'], Immutable.List());
|
||||
const oldIds = state.getIn([timeline, 'items'], ImmutableList());
|
||||
|
||||
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
|
||||
mMap.set('loaded', true);
|
||||
@ -44,8 +44,8 @@ const normalizeTimeline = (state, timeline, statuses, next) => {
|
||||
};
|
||||
|
||||
const appendNormalizedTimeline = (state, timeline, statuses, next) => {
|
||||
const ids = Immutable.List(statuses.map(status => status.get('id')));
|
||||
const oldIds = state.getIn([timeline, 'items'], Immutable.List());
|
||||
const ids = ImmutableList(statuses.map(status => status.get('id')));
|
||||
const oldIds = state.getIn([timeline, 'items'], ImmutableList());
|
||||
|
||||
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
|
||||
mMap.set('isLoading', false);
|
||||
@ -56,7 +56,7 @@ const appendNormalizedTimeline = (state, timeline, statuses, next) => {
|
||||
|
||||
const updateTimeline = (state, timeline, status, references) => {
|
||||
const top = state.getIn([timeline, 'top']);
|
||||
const ids = state.getIn([timeline, 'items'], Immutable.List());
|
||||
const ids = state.getIn([timeline, 'items'], ImmutableList());
|
||||
const includesId = ids.includes(status.get('id'));
|
||||
const unread = state.getIn([timeline, 'unread'], 0);
|
||||
|
||||
@ -124,11 +124,11 @@ export default function timelines(state = initialState, action) {
|
||||
case TIMELINE_EXPAND_FAIL:
|
||||
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
|
||||
case TIMELINE_REFRESH_SUCCESS:
|
||||
return normalizeTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next);
|
||||
return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next);
|
||||
case TIMELINE_EXPAND_SUCCESS:
|
||||
return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses), action.next);
|
||||
return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next);
|
||||
case TIMELINE_UPDATE:
|
||||
return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references);
|
||||
return updateTimeline(state, action.timeline, fromJS(action.status), action.references);
|
||||
case TIMELINE_DELETE:
|
||||
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
|
||||
case ACCOUNT_BLOCK_SUCCESS:
|
||||
|
@ -20,22 +20,22 @@ import {
|
||||
MUTES_FETCH_SUCCESS,
|
||||
MUTES_EXPAND_SUCCESS,
|
||||
} from '../actions/mutes';
|
||||
import Immutable from 'immutable';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
const initialState = Immutable.Map({
|
||||
followers: Immutable.Map(),
|
||||
following: Immutable.Map(),
|
||||
reblogged_by: Immutable.Map(),
|
||||
favourited_by: Immutable.Map(),
|
||||
follow_requests: Immutable.Map(),
|
||||
blocks: Immutable.Map(),
|
||||
mutes: Immutable.Map(),
|
||||
const initialState = ImmutableMap({
|
||||
followers: ImmutableMap(),
|
||||
following: ImmutableMap(),
|
||||
reblogged_by: ImmutableMap(),
|
||||
favourited_by: ImmutableMap(),
|
||||
follow_requests: ImmutableMap(),
|
||||
blocks: ImmutableMap(),
|
||||
mutes: ImmutableMap(),
|
||||
});
|
||||
|
||||
const normalizeList = (state, type, id, accounts, next) => {
|
||||
return state.setIn([type, id], Immutable.Map({
|
||||
return state.setIn([type, id], ImmutableMap({
|
||||
next,
|
||||
items: Immutable.List(accounts.map(item => item.id)),
|
||||
items: ImmutableList(accounts.map(item => item.id)),
|
||||
}));
|
||||
};
|
||||
|
||||
@ -56,22 +56,22 @@ export default function userLists(state = initialState, action) {
|
||||
case FOLLOWING_EXPAND_SUCCESS:
|
||||
return appendToList(state, 'following', action.id, action.accounts, action.next);
|
||||
case REBLOGS_FETCH_SUCCESS:
|
||||
return state.setIn(['reblogged_by', action.id], Immutable.List(action.accounts.map(item => item.id)));
|
||||
return state.setIn(['reblogged_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
|
||||
case FAVOURITES_FETCH_SUCCESS:
|
||||
return state.setIn(['favourited_by', action.id], Immutable.List(action.accounts.map(item => item.id)));
|
||||
return state.setIn(['favourited_by', action.id], ImmutableList(action.accounts.map(item => item.id)));
|
||||
case FOLLOW_REQUESTS_FETCH_SUCCESS:
|
||||
return state.setIn(['follow_requests', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
|
||||
return state.setIn(['follow_requests', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
|
||||
case FOLLOW_REQUESTS_EXPAND_SUCCESS:
|
||||
return state.updateIn(['follow_requests', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['follow_requests', 'next'], action.next);
|
||||
case FOLLOW_REQUEST_AUTHORIZE_SUCCESS:
|
||||
case FOLLOW_REQUEST_REJECT_SUCCESS:
|
||||
return state.updateIn(['follow_requests', 'items'], list => list.filterNot(item => item === action.id));
|
||||
case BLOCKS_FETCH_SUCCESS:
|
||||
return state.setIn(['blocks', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
|
||||
return state.setIn(['blocks', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
|
||||
case BLOCKS_EXPAND_SUCCESS:
|
||||
return state.updateIn(['blocks', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['blocks', 'next'], action.next);
|
||||
case MUTES_FETCH_SUCCESS:
|
||||
return state.setIn(['mutes', 'items'], Immutable.List(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
|
||||
return state.setIn(['mutes', 'items'], ImmutableList(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
|
||||
case MUTES_EXPAND_SUCCESS:
|
||||
return state.updateIn(['mutes', 'items'], list => list.concat(action.accounts.map(item => item.id))).setIn(['mutes', 'next'], action.next);
|
||||
default:
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import Immutable from 'immutable';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
|
||||
const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
|
||||
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
||||
@ -73,10 +73,10 @@ export const makeGetNotification = () => {
|
||||
};
|
||||
|
||||
export const getAccountGallery = createSelector([
|
||||
(state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], Immutable.List()),
|
||||
(state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()),
|
||||
state => state.get('statuses'),
|
||||
], (statusIds, statuses) => {
|
||||
let medias = Immutable.List();
|
||||
let medias = ImmutableList();
|
||||
|
||||
statusIds.forEach(statusId => {
|
||||
const status = statuses.get(statusId);
|
||||
|
Reference in New Issue
Block a user