Merge remote-tracking branch 'origin/styling/add-groups-link' into groups-updates

* origin/styling/add-groups-link: (31 commits)
  Comment out the "groups" button until ready to reveal.
  Changed the method of adding main navigation icons. Created a png sprite sized @2x based on largest usage (for retina). This will fix some rendering issues caused by using svg images. It will allow adding depth and more color / shading if we choose later.
  intents fix
  federation fix
  two more federation fixes
  Removed unused imports
  Removed unused PublicTimeline component
  Updated CommunityTimeline to add option for "all federated" content
  Removed unused import in unauthorized_modal
  Updated registration legal links
  Updated compose_form to account for if compose modal open
  Added empty message to pinned statuses page
  Updated nextProps withReplies for account timeline
  Added empty message to account_gallery media page
  Updated timeline/notification dequeue to be in componentDidMount
  Added TimelineQueueButtonHeader to status_list
  Added queue functionality status_list_container for status timelines
  Updated all Redis.current.publish, PushUpdateWorker.perform_async to work again
  Added timeline dequeue functionality to onSubmitCompose action
  Added redux functionality for queueing/dequeueing timelines
  ...
This commit is contained in:
2458773093 2019-07-16 14:29:38 +03:00
commit c56a8914f3
136 changed files with 606 additions and 618 deletions

View File

@ -5,7 +5,7 @@ class IntentsController < ApplicationController
rescue_from Addressable::URI::InvalidURIError, with: :handle_invalid_uri rescue_from Addressable::URI::InvalidURIError, with: :handle_invalid_uri
def show def show
if uri.scheme == 'web+gabsocial' if uri.scheme == 'web+mastodon'
case uri.host case uri.host
when 'follow' when 'follow'
return redirect_to authorize_interaction_path(uri: uri.query_values['uri'].gsub(/\Aacct:/, '')) return redirect_to authorize_interaction_path(uri: uri.query_values['uri'].gsub(/\Aacct:/, ''))

View File

@ -1,16 +0,0 @@
import { saveSettings } from './settings';
export const COLUMN_PARAMS_CHANGE = 'COLUMN_PARAMS_CHANGE';
export function changeColumnParams(uuid, path, value) {
return dispatch => {
dispatch({
type: COLUMN_PARAMS_CHANGE,
uuid,
path,
value,
});
dispatch(saveSettings());
};
}

View File

@ -6,7 +6,7 @@ import { tagHistory } from '../settings';
import { useEmoji } from './emojis'; import { useEmoji } from './emojis';
import resizeImage from '../utils/resize_image'; import resizeImage from '../utils/resize_image';
import { importFetchedAccounts } from './importer'; import { importFetchedAccounts } from './importer';
import { updateTimeline } from './timelines'; import { updateTimeline, dequeueTimeline } from './timelines';
import { showAlertForError } from './alerts'; import { showAlertForError } from './alerts';
import { showAlert } from './alerts'; import { showAlert } from './alerts';
import { defineMessages } from 'react-intl'; import { defineMessages } from 'react-intl';
@ -169,6 +169,10 @@ export function submitCompose(routerHistory, group) {
const timeline = getState().getIn(['timelines', timelineId]); const timeline = getState().getIn(['timelines', timelineId]);
if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) { if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) {
let dequeueArgs = {};
if (timelineId === 'community') dequeueArgs.onlyMedia = getState().getIn(['settings', 'community', 'other', 'onlyMedia']),
dispatch(dequeueTimeline(timelineId, null, dequeueArgs));
dispatch(updateTimeline(timelineId, { ...response.data })); dispatch(updateTimeline(timelineId, { ...response.data }));
} }
}; };

View File

@ -16,6 +16,8 @@ import { me } from 'gabsocial/initial_state';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST'; export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS'; export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
@ -26,6 +28,8 @@ export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
export const MAX_QUEUED_NOTIFICATIONS = 40;
defineMessages({ defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
@ -42,18 +46,6 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
export function updateNotifications(notification, intlMessages, intlLocale) { export function updateNotifications(notification, intlMessages, intlLocale) {
return (dispatch, getState) => { return (dispatch, getState) => {
const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true); const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
const filters = getFilters(getState(), { contextType: 'notifications' });
let filtered = false;
if (notification.type === 'mention') {
const regex = regexFromFilters(filters);
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
filtered = regex && regex.test(searchIndex);
}
if (showInColumn) { if (showInColumn) {
dispatch(importFetchedAccount(notification.account)); dispatch(importFetchedAccount(notification.account));
@ -65,21 +57,33 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
dispatch({ dispatch({
type: NOTIFICATIONS_UPDATE, type: NOTIFICATIONS_UPDATE,
notification, notification,
meta: (playSound && !filtered) ? { sound: 'ribbit' } : undefined,
}); });
fetchRelatedRelationships(dispatch, [notification]); fetchRelatedRelationships(dispatch, [notification]);
} else if (playSound && !filtered) { }
dispatch({ };
type: NOTIFICATIONS_UPDATE_NOOP, };
meta: { sound: 'ribbit' },
}); export function updateNotificationsQueue(notification, intlMessages, intlLocale, curPath) {
return (dispatch, getState) => {
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const filters = getFilters(getState(), { contextType: 'notifications' });
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
let filtered = false;
const isOnNotificationsPage = curPath === '/notifications';
if (notification.type === 'mention') {
const regex = regexFromFilters(filters);
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
filtered = regex && regex.test(searchIndex);
} }
// Desktop notifications // Desktop notifications
if (typeof window.Notification !== 'undefined' && showAlert && !filtered) { if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }); const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : ''); const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id }); const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
@ -88,7 +92,49 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
notify.close(); notify.close();
}); });
} }
};
if (playSound && !filtered) {
dispatch({
type: NOTIFICATIONS_UPDATE_NOOP,
meta: { sound: 'ribbit' },
});
}
if (isOnNotificationsPage) {
dispatch({
type: NOTIFICATIONS_UPDATE_QUEUE,
notification,
intlMessages,
intlLocale,
});
}
else {
dispatch(updateNotifications(notification, intlMessages, intlLocale));
}
}
};
export function dequeueNotifications() {
return (dispatch, getState) => {
const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList());
const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0);
if (totalQueuedNotificationsCount == 0) {
return;
}
else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
queuedNotifications.forEach(block => {
dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale));
});
}
else {
dispatch(expandNotifications());
}
dispatch({
type: NOTIFICATIONS_DEQUEUE,
});
}
}; };
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS(); const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
@ -169,7 +215,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
export function clearNotifications() { export function clearNotifications() {
return (dispatch, getState) => { return (dispatch, getState) => {
if (!me) return; if (!me) return;
dispatch({ dispatch({
type: NOTIFICATIONS_CLEAR, type: NOTIFICATIONS_CLEAR,
}); });

View File

@ -1,12 +1,12 @@
import { connectStream } from '../stream'; import { connectStream } from '../stream';
import { import {
updateTimeline,
deleteFromTimelines, deleteFromTimelines,
expandHomeTimeline, expandHomeTimeline,
connectTimeline, connectTimeline,
disconnectTimeline, disconnectTimeline,
updateTimelineQueue,
} from './timelines'; } from './timelines';
import { updateNotifications, expandNotifications } from './notifications'; import { updateNotificationsQueue, expandNotifications } from './notifications';
import { updateConversations } from './conversations'; import { updateConversations } from './conversations';
import { fetchFilters } from './filters'; import { fetchFilters } from './filters';
import { getLocale } from '../locales'; import { getLocale } from '../locales';
@ -30,13 +30,13 @@ export function connectTimelineStream (timelineId, path, pollingRefresh = null,
onReceive (data) { onReceive (data) {
switch(data.event) { switch(data.event) {
case 'update': case 'update':
dispatch(updateTimeline(timelineId, JSON.parse(data.payload), accept)); dispatch(updateTimelineQueue(timelineId, JSON.parse(data.payload), accept));
break; break;
case 'delete': case 'delete':
dispatch(deleteFromTimelines(data.payload)); dispatch(deleteFromTimelines(data.payload));
break; break;
case 'notification': case 'notification':
dispatch(updateNotifications(JSON.parse(data.payload), messages, locale)); dispatch(updateNotificationsQueue(JSON.parse(data.payload), messages, locale, window.location.pathname));
break; break;
case 'conversation': case 'conversation':
dispatch(updateConversations(JSON.parse(data.payload))); dispatch(updateConversations(JSON.parse(data.payload)));

View File

@ -1,10 +1,12 @@
import { importFetchedStatus, importFetchedStatuses } from './importer'; import { importFetchedStatus, importFetchedStatuses } from './importer';
import api, { getLinks } from '../api'; import api, { getLinks } from '../api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'; export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE'; export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR'; export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST'; export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS'; export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
@ -13,6 +15,8 @@ export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT'; export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const MAX_QUEUED_ITEMS = 40;
export function updateTimeline(timeline, status, accept) { export function updateTimeline(timeline, status, accept) {
return dispatch => { return dispatch => {
if (typeof accept === 'function' && !accept(status)) { if (typeof accept === 'function' && !accept(status)) {
@ -29,6 +33,64 @@ export function updateTimeline(timeline, status, accept) {
}; };
}; };
export function updateTimelineQueue(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
status,
});
}
};
export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
return (dispatch, getState) => {
const queuedItems = getState().getIn(['timelines', timeline, 'queuedItems'], ImmutableList());
const totalQueuedItemsCount = getState().getIn(['timelines', timeline, 'totalQueuedItemsCount'], 0);
let shouldDispatchDequeue = true;
if (totalQueuedItemsCount == 0) {
return;
}
else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
queuedItems.forEach(status => {
dispatch(updateTimeline(timeline, status.toJS(), null));
});
}
else {
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timeline));
expandFunc();
}
else {
if (timeline === 'home') {
dispatch(clearTimeline(timeline));
dispatch(expandHomeTimeline(optionalExpandArgs));
}
else if (timeline === 'community') {
dispatch(clearTimeline(timeline));
dispatch(expandCommunityTimeline(optionalExpandArgs));
}
else {
shouldDispatchDequeue = false;
}
}
}
if (!shouldDispatchDequeue) return;
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
});
}
};
export function deleteFromTimelines(id) { export function deleteFromTimelines(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']); const accountId = getState().getIn(['statuses', id, 'account']);

View File

@ -1,5 +1,4 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { ScrollContainer } from 'react-router-scroll-4';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container'; import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
import LoadMore from './load_more'; import LoadMore from './load_more';

View File

@ -7,6 +7,7 @@ import StatusContainer from '../containers/status_container';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import LoadGap from './load_gap'; import LoadGap from './load_gap';
import ScrollableList from './scrollable_list'; import ScrollableList from './scrollable_list';
import TimelineQueueButtonHeader from './timeline_queue_button_header';
export default class StatusList extends ImmutablePureComponent { export default class StatusList extends ImmutablePureComponent {
@ -22,6 +23,12 @@ export default class StatusList extends ImmutablePureComponent {
emptyMessage: PropTypes.node, emptyMessage: PropTypes.node,
alwaysPrepend: PropTypes.bool, alwaysPrepend: PropTypes.bool,
timelineId: PropTypes.string, timelineId: PropTypes.string,
queuedItemSize: PropTypes.number,
onDequeueTimeline: PropTypes.func,
};
componentDidMount() {
this.handleDequeueTimeline();
}; };
getFeaturedStatusCount = () => { getFeaturedStatusCount = () => {
@ -64,13 +71,18 @@ export default class StatusList extends ImmutablePureComponent {
} }
} }
handleDequeueTimeline = () => {
const { onDequeueTimeline, timelineId } = this.props;
if (!onDequeueTimeline || !timelineId) return;
onDequeueTimeline(timelineId);
}
setRef = c => { setRef = c => {
this.node = c; this.node = c;
} }
render () { render () {
const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props; const { statusIds, featuredStatusIds, onLoadMore, timelineId, totalQueuedItemsCount, isLoading, isPartial, ...other } = this.props;
const { isLoading, isPartial } = other;
if (isPartial) { if (isPartial) {
return ( return (
@ -119,11 +131,12 @@ export default class StatusList extends ImmutablePureComponent {
)).concat(scrollableContent); )).concat(scrollableContent);
} }
return ( return [
<ScrollableList {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}> <TimelineQueueButtonHeader key='timeline-queue-button-header' onClick={this.handleDequeueTimeline} count={totalQueuedItemsCount} itemType='gab' />,
<ScrollableList key='scrollable-list' {...other} showLoading={isLoading && statusIds.size === 0} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
{scrollableContent} {scrollableContent}
</ScrollableList> </ScrollableList>
); ];
} }
} }

View File

@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { shortNumberFormat } from '../utils/numbers';
export default class TimelineQueueButtonHeader extends React.PureComponent {
static propTypes = {
onClick: PropTypes.func.isRequired,
count: PropTypes.number,
itemType: PropTypes.string,
};
static defaultProps = {
count: 0,
itemType: 'item',
};
render () {
const { count, itemType, onClick } = this.props;
if (count <= 0) return null;
return (
<div className='timeline-queue-header'>
<a className='timeline-queue-header__btn' onClick={onClick}>
<FormattedMessage
id='timeline_queue.label'
defaultMessage='Click to see {count} new {type}'
values={{
count: shortNumberFormat(count),
type: count == 1 ? itemType : `${itemType}s`,
}}
/>
</a>
</div>
);
}
}

View File

@ -24,7 +24,7 @@ const messages = defineMessages({
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' }, linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' }, account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' }, mention: { id: 'account.mention', defaultMessage: 'Mention' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' }, unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
block: { id: 'account.block', defaultMessage: 'Block @{name}' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' },
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
@ -311,7 +311,7 @@ class Header extends ImmutablePureComponent {
{actionBtn} {actionBtn}
{account.get('id') !== me && {account.get('id') !== me &&
<Button className='button button-alternative-2' onClick={this.props.onMention}> <Button className='button button-alternative-2' onClick={this.props.onMention}>
<FormattedMessage id='account.mention' defaultMessage='Mention @{name}' values={{ <FormattedMessage id='account.mention' defaultMessage='Mention' values={{
name: account.get('acct') name: account.get('acct')
}} /> }} />
</Button> </Button>

View File

@ -212,6 +212,13 @@ class AccountGallery extends ImmutablePureComponent {
<MediaItem key={attachment.get('id')} attachment={attachment} displayWidth={width} onOpenMedia={this.handleOpenMedia} /> <MediaItem key={attachment.get('id')} attachment={attachment} displayWidth={width} onOpenMedia={this.handleOpenMedia} />
))} ))}
{
attachments.size == 0 &&
<div className='empty-column-indicator'>
<FormattedMessage id='account_gallery.none' defaultMessage='No media to show.'/>
</div>
}
{loadOlder} {loadOlder}
</div> </div>

View File

@ -93,7 +93,7 @@ class AccountTimeline extends ImmutablePureComponent {
this.props.dispatch(expandAccountFeaturedTimeline(nextProps.accountId)); this.props.dispatch(expandAccountFeaturedTimeline(nextProps.accountId));
} }
this.props.dispatch(expandAccountTimeline(nextProps.accountId, { withReplies: nextProps.params.withReplies })); this.props.dispatch(expandAccountTimeline(nextProps.accountId, { withReplies: nextProps.withReplies }));
} }
} }

View File

@ -11,7 +11,6 @@ class ColumnSettings extends React.PureComponent {
settings: ImmutablePropTypes.map.isRequired, settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
}; };
render () { render () {
@ -21,6 +20,7 @@ class ColumnSettings extends React.PureComponent {
<div> <div>
<div className='column-settings__row'> <div className='column-settings__row'>
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />} /> <SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />} />
<SettingToggle settings={settings} settingPath={['other', 'allFediverse']} onChange={onChange} label={<FormattedMessage id='community.column_settings.all_fediverse' defaultMessage='All Fediverse' />} />
</div> </div>
</div> </div>
); );

View File

@ -1,26 +1,15 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ColumnSettings from '../components/column_settings'; import ColumnSettings from '../components/column_settings';
import { changeSetting } from '../../../actions/settings'; import { changeSetting } from '../../../actions/settings';
import { changeColumnParams } from '../../../actions/columns';
const mapStateToProps = (state, { columnId }) => { const mapStateToProps = state => ({
const uuid = columnId; settings: state.getIn(['settings', 'community']),
const columns = state.getIn(['settings', 'columns']); });
const index = columns.findIndex(c => c.get('uuid') === uuid);
return { const mapDispatchToProps = (dispatch) => {
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'community']),
};
};
const mapDispatchToProps = (dispatch, { columnId }) => {
return { return {
onChange (key, checked) { onChange (key, checked) {
if (columnId) { dispatch(changeSetting(['community', ...key], checked));
dispatch(changeColumnParams(columnId, key, checked));
} else {
dispatch(changeSetting(['community', ...key], checked));
}
}, },
}; };
}; };

View File

@ -4,23 +4,32 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column'; import Column from '../../components/column';
import { expandCommunityTimeline } from '../../actions/timelines';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
import { connectCommunityStream } from '../../actions/streaming';
import HomeColumnHeader from '../../components/home_column_header'; import HomeColumnHeader from '../../components/home_column_header';
import {
expandCommunityTimeline,
expandPublicTimeline,
} from '../../actions/timelines';
import {
connectCommunityStream,
connectPublicStream,
} from '../../actions/streaming';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.community', defaultMessage: 'Local timeline' }, title: { id: 'column.community', defaultMessage: 'Community timeline' },
}); });
const mapStateToProps = (state, { onlyMedia, columnId }) => { const mapStateToProps = state => {
const uuid = columnId; const allFediverse = state.getIn(['settings', 'community', 'other', 'allFediverse']);
const columns = state.getIn(['settings', 'columns']); const onlyMedia = state.getIn(['settings', 'community', 'other', 'onlyMedia']);
const index = columns.findIndex(c => c.get('uuid') === uuid);
const timelineId = allFediverse ? 'public' : 'community';
return { return {
hasUnread: state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`, 'unread']) > 0, timelineId,
onlyMedia: (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'community', 'other', 'onlyMedia']), allFediverse,
onlyMedia,
hasUnread: state.getIn(['timelines', `${timelineId}${onlyMedia ? ':media' : ''}`, 'unread']) > 0,
}; };
}; };
@ -34,30 +43,45 @@ class CommunityTimeline extends React.PureComponent {
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
allFediverse: false,
}; };
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
onlyMedia: PropTypes.bool, onlyMedia: PropTypes.bool,
allFediverse: PropTypes.bool,
timelineId: PropTypes.string,
}; };
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia, allFediverse } = this.props;
dispatch(expandCommunityTimeline({ onlyMedia })); if (allFediverse) {
this.disconnect = dispatch(connectCommunityStream({ onlyMedia })); dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
}
else {
dispatch(expandCommunityTimeline({ onlyMedia }));
this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
}
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
if (prevProps.onlyMedia !== this.props.onlyMedia) { if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.allFediverse !== this.props.allFediverse) {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia, allFediverse } = this.props;
this.disconnect(); this.disconnect();
dispatch(expandCommunityTimeline({ onlyMedia }));
this.disconnect = dispatch(connectCommunityStream({ onlyMedia })); if (allFediverse) {
dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
}
else {
dispatch(expandCommunityTimeline({ onlyMedia }));
this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
}
} }
} }
@ -69,27 +93,29 @@ class CommunityTimeline extends React.PureComponent {
} }
handleLoadMore = maxId => { handleLoadMore = maxId => {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia, allFediverse } = this.props;
dispatch(expandCommunityTimeline({ maxId, onlyMedia })); if (allFediverse) {
dispatch(expandPublicTimeline({ maxId, onlyMedia }));
}
else {
dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
}
} }
render () { render () {
const { intl, hasUnread, columnId, onlyMedia } = this.props; const { intl, hasUnread, onlyMedia, timelineId, allFediverse } = this.props;
return ( return (
<Column label={intl.formatMessage(messages.title)}> <Column label={intl.formatMessage(messages.title)}>
<HomeColumnHeader <HomeColumnHeader activeItem='all' active={hasUnread} >
activeItem='all'
active={hasUnread}
>
<ColumnSettingsContainer /> <ColumnSettingsContainer />
</HomeColumnHeader> </HomeColumnHeader>
<StatusListContainer <StatusListContainer
scrollKey={`community_timeline-${columnId}`} scrollKey={`${timelineId}_timeline`}
timelineId={`community${onlyMedia ? ':media' : ''}`} timelineId={`${timelineId}${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />} emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The community timeline is empty. Write something publicly to get the ball rolling!' />}
/> />
</Column> </Column>
); );

View File

@ -69,6 +69,7 @@ class ComposeForm extends ImmutablePureComponent {
shouldCondense: PropTypes.bool, shouldCondense: PropTypes.bool,
autoFocus: PropTypes.bool, autoFocus: PropTypes.bool,
group: ImmutablePropTypes.map, group: ImmutablePropTypes.map,
isModalOpen: PropTypes.bool,
}; };
static defaultProps = { static defaultProps = {
@ -151,6 +152,8 @@ class ComposeForm extends ImmutablePureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
if (!this.autosuggestTextarea) return;
// This statement does several things: // This statement does several things:
// - If we're beginning a reply, and, // - If we're beginning a reply, and,
// - Replying to zero or one users, places the cursor at the end of the textbox. // - Replying to zero or one users, places the cursor at the end of the textbox.
@ -204,7 +207,7 @@ class ComposeForm extends ImmutablePureComponent {
} }
render () { render () {
const { intl, onPaste, showSearch, anyMedia, shouldCondense, autoFocus } = this.props; const { intl, onPaste, showSearch, anyMedia, shouldCondense, autoFocus, isModalOpen } = this.props;
const condensed = shouldCondense && !this.props.text && !this.state.composeFocused; const condensed = shouldCondense && !this.props.text && !this.state.composeFocused;
const disabled = this.props.isSubmitting; const disabled = this.props.isSubmitting;
const text = [this.props.spoilerText, countableText(this.props.text)].join(''); const text = [this.props.spoilerText, countableText(this.props.text)].join('');
@ -253,7 +256,7 @@ class ComposeForm extends ImmutablePureComponent {
</div> </div>
<AutosuggestTextarea <AutosuggestTextarea
ref={this.setAutosuggestTextarea} ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
placeholder={intl.formatMessage(messages.placeholder)} placeholder={intl.formatMessage(messages.placeholder)}
disabled={disabled} disabled={disabled}
value={this.props.text} value={this.props.text}

View File

@ -25,6 +25,7 @@ const mapStateToProps = state => ({
isUploading: state.getIn(['compose', 'is_uploading']), isUploading: state.getIn(['compose', 'is_uploading']),
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
isModalOpen: state.get('modal').modalType === 'COMPOSE',
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({

View File

@ -18,7 +18,6 @@ class DirectTimeline extends React.PureComponent {
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
}; };
@ -45,14 +44,14 @@ class DirectTimeline extends React.PureComponent {
} }
render () { render () {
const { intl, hasUnread, columnId } = this.props; const { intl, hasUnread } = this.props;
return ( return (
<Column label={intl.formatMessage(messages.title)}> <Column label={intl.formatMessage(messages.title)}>
<ColumnHeader icon='envelope' active={hasUnread} title={intl.formatMessage(messages.title)} /> <ColumnHeader icon='envelope' active={hasUnread} title={intl.formatMessage(messages.title)} />
<ConversationsListContainer <ConversationsListContainer
scrollKey={`direct_timeline-${columnId}`} scrollKey='direct_timeline'
timelineId='direct' timelineId='direct'
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />} emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}

View File

@ -29,7 +29,6 @@ class Favourites extends ImmutablePureComponent {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
statusIds: ImmutablePropTypes.list.isRequired, statusIds: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
isMyAccount: PropTypes.bool.isRequired, isMyAccount: PropTypes.bool.isRequired,
@ -44,7 +43,7 @@ class Favourites extends ImmutablePureComponent {
}, 300, { leading: true }) }, 300, { leading: true })
render () { render () {
const { intl, statusIds, columnId, hasMore, isLoading, isMyAccount } = this.props; const { intl, statusIds, hasMore, isLoading, isMyAccount } = this.props;
if (!isMyAccount) { if (!isMyAccount) {
return ( return (
@ -60,7 +59,7 @@ class Favourites extends ImmutablePureComponent {
<Column> <Column>
<StatusList <StatusList
statusIds={statusIds} statusIds={statusIds}
scrollKey={`favourited_statuses-${columnId}`} scrollKey='favourited_statuses'
hasMore={hasMore} hasMore={hasMore}
isLoading={isLoading} isLoading={isLoading}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}

View File

@ -88,15 +88,15 @@ class GroupTimeline extends React.PureComponent {
</div> </div>
)} )}
<div className='group__feed'> <div className='group__feed'>
<StatusListContainer <StatusListContainer
alwaysPrepend alwaysPrepend
scrollKey={`group_timeline-${columnId}`} scrollKey={`group_timeline-${columnId}`}
timelineId={`group:${id}`} timelineId={`group:${id}`}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.group' defaultMessage='There is nothing in this group yet. When members of this group post new statuses, they will appear here.' />} emptyMessage={<FormattedMessage id='empty_column.group' defaultMessage='There is nothing in this group yet. When members of this group post new statuses, they will appear here.' />}
/> />
</div> </div>
</div> </div>
); );
} }

View File

@ -1,113 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Toggle from 'react-toggle';
import AsyncSelect from 'react-select/lib/Async';
const messages = defineMessages({
placeholder: { id: 'hashtag.column_settings.select.placeholder', defaultMessage: 'Enter hashtags…' },
noOptions: { id: 'hashtag.column_settings.select.no_options_message', defaultMessage: 'No suggestions found' },
});
export default @injectIntl
class ColumnSettings extends React.PureComponent {
static propTypes = {
settings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired,
onLoad: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
state = {
open: this.hasTags(),
};
hasTags () {
return ['all', 'any', 'none'].map(mode => this.tags(mode).length > 0).includes(true);
}
tags (mode) {
let tags = this.props.settings.getIn(['tags', mode]) || [];
if (tags.toJSON) {
return tags.toJSON();
} else {
return tags;
}
};
onSelect = mode => value => this.props.onChange(['tags', mode], value);
onToggle = () => {
if (this.state.open && this.hasTags()) {
this.props.onChange('tags', {});
}
this.setState({ open: !this.state.open });
};
noOptionsMessage = () => this.props.intl.formatMessage(messages.noOptions);
modeSelect (mode) {
return (
<div className='column-settings__row'>
<span className='column-settings__section'>
{this.modeLabel(mode)}
</span>
<AsyncSelect
isMulti
autoFocus
value={this.tags(mode)}
onChange={this.onSelect(mode)}
loadOptions={this.props.onLoad}
className='column-select__container'
classNamePrefix='column-select'
name='tags'
placeholder={this.props.intl.formatMessage(messages.placeholder)}
noOptionsMessage={this.noOptionsMessage}
/>
</div>
);
}
modeLabel (mode) {
switch(mode) {
case 'any':
return <FormattedMessage id='hashtag.column_settings.tag_mode.any' defaultMessage='Any of these' />;
case 'all':
return <FormattedMessage id='hashtag.column_settings.tag_mode.all' defaultMessage='All of these' />;
case 'none':
return <FormattedMessage id='hashtag.column_settings.tag_mode.none' defaultMessage='None of these' />;
default:
return '';
}
};
render () {
return (
<div>
<div className='column-settings__row'>
<div className='setting-toggle'>
<Toggle id='hashtag.column_settings.tag_toggle' onChange={this.onToggle} checked={this.state.open} />
<span className='setting-toggle__label'>
<FormattedMessage id='hashtag.column_settings.tag_toggle' defaultMessage='Include additional tags in this column' />
</span>
</div>
</div>
{this.state.open && (
<div className='column-settings__hashtags'>
{this.modeSelect('any')}
{this.modeSelect('all')}
{this.modeSelect('none')}
</div>
)}
</div>
);
}
}

View File

@ -1,31 +0,0 @@
import { connect } from 'react-redux';
import ColumnSettings from '../components/column_settings';
import { changeColumnParams } from '../../../actions/columns';
import api from '../../../api';
const mapStateToProps = (state, { columnId }) => {
const columns = state.getIn(['settings', 'columns']);
const index = columns.findIndex(c => c.get('uuid') === columnId);
if (!(columnId && index >= 0)) {
return {};
}
return { settings: columns.get(index).get('params') };
};
const mapDispatchToProps = (dispatch, { columnId }) => ({
onChange (key, value) {
dispatch(changeColumnParams(columnId, key, value));
},
onLoad (value) {
return api().get('/api/v2/search', { params: { q: value } }).then(response => {
return (response.data.hashtags || []).map((tag) => {
return { value: tag.name, label: `#${tag.name}` };
});
});
},
});
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);

View File

@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import ColumnSettingsContainer from './containers/column_settings_container';
import { expandHashtagTimeline, clearTimeline } from '../../actions/timelines'; import { expandHashtagTimeline, clearTimeline } from '../../actions/timelines';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { connectHashtagStream } from '../../actions/streaming'; import { connectHashtagStream } from '../../actions/streaming';
@ -21,7 +20,6 @@ class HashtagTimeline extends React.PureComponent {
static propTypes = { static propTypes = {
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
columnId: PropTypes.string,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
}; };
@ -104,17 +102,14 @@ class HashtagTimeline extends React.PureComponent {
} }
render () { render () {
const { hasUnread, columnId } = this.props; const { hasUnread } = this.props;
const { id } = this.props.params; const { id } = this.props.params;
return ( return (
<Column label={`#${id}`}> <Column label={`#${id}`}>
<ColumnHeader icon='hashtag' active={hasUnread} title={this.title()}> <ColumnHeader icon='hashtag' active={hasUnread} title={this.title()} />
{columnId && <ColumnSettingsContainer columnId={columnId} />}
</ColumnHeader>
<StatusListContainer <StatusListContainer
scrollKey={`hashtag_timeline-${columnId}`} scrollKey='hashtag_timeline'
timelineId={`hashtag:${id}`} timelineId={`hashtag:${id}`}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />} emptyMessage={<FormattedMessage id='empty_column.hashtag' defaultMessage='There is nothing in this hashtag yet.' />}

View File

@ -26,7 +26,6 @@ class HomeTimeline extends React.PureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
isPartial: PropTypes.bool, isPartial: PropTypes.bool,
columnId: PropTypes.string,
}; };
handleLoadMore = maxId => { handleLoadMore = maxId => {
@ -67,7 +66,7 @@ class HomeTimeline extends React.PureComponent {
} }
render () { render () {
const { intl, hasUnread, columnId } = this.props; const { intl, hasUnread } = this.props;
return ( return (
<Column label={intl.formatMessage(messages.title)}> <Column label={intl.formatMessage(messages.title)}>
@ -78,7 +77,7 @@ class HomeTimeline extends React.PureComponent {
<ColumnSettingsContainer /> <ColumnSettingsContainer />
</HomeColumnHeader> </HomeColumnHeader>
<StatusListContainer <StatusListContainer
scrollKey={`home_timeline-${columnId}`} scrollKey='home_timeline'
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
timelineId='home' timelineId='home'
emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty. Start following other users to recieve their content here.'/>} emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage='Your home timeline is empty. Start following other users to recieve their content here.'/>}

View File

@ -36,7 +36,6 @@ class ListTimeline extends React.PureComponent {
static propTypes = { static propTypes = {
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
list: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]), list: PropTypes.oneOfType([ImmutablePropTypes.map, PropTypes.bool]),
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -69,7 +68,7 @@ class ListTimeline extends React.PureComponent {
} }
handleDeleteClick = () => { handleDeleteClick = () => {
const { dispatch, columnId, intl } = this.props; const { dispatch, intl } = this.props;
const { id } = this.props.params; const { id } = this.props.params;
dispatch(openModal('CONFIRM', { dispatch(openModal('CONFIRM', {
@ -77,18 +76,13 @@ class ListTimeline extends React.PureComponent {
confirm: intl.formatMessage(messages.deleteConfirm), confirm: intl.formatMessage(messages.deleteConfirm),
onConfirm: () => { onConfirm: () => {
dispatch(deleteList(id)); dispatch(deleteList(id));
this.context.router.history.push('/lists');
if (!!columnId) {
//
} else {
this.context.router.history.push('/lists');
}
}, },
})); }));
} }
render () { render () {
const { hasUnread, columnId, list } = this.props; const { hasUnread, list } = this.props;
const { id } = this.props.params; const { id } = this.props.params;
const title = list ? list.get('title') : id; const title = list ? list.get('title') : id;
@ -126,7 +120,7 @@ class ListTimeline extends React.PureComponent {
</ColumnHeader> </ColumnHeader>
<StatusListContainer <StatusListContainer
scrollKey={`list_timeline-${columnId}`} scrollKey='list_timeline'
timelineId={`list:${id}`} timelineId={`list:${id}`}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />} emptyMessage={<FormattedMessage id='empty_column.list' defaultMessage='There is nothing in this list yet. When members of this list post new statuses, they will appear here.' />}

View File

@ -4,7 +4,11 @@ import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { expandNotifications, scrollTopNotifications } from '../../actions/notifications'; import {
expandNotifications,
scrollTopNotifications,
dequeueNotifications,
} from '../../actions/notifications';
import NotificationContainer from './containers/notification_container'; import NotificationContainer from './containers/notification_container';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
@ -14,6 +18,7 @@ import { List as ImmutableList } from 'immutable';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import LoadGap from '../../components/load_gap'; import LoadGap from '../../components/load_gap';
import TimelineQueueButtonHeader from '../../components/timeline_queue_button_header';
const messages = defineMessages({ const messages = defineMessages({
title: { id: 'column.notifications', defaultMessage: 'Notifications' }, title: { id: 'column.notifications', defaultMessage: 'Notifications' },
@ -40,6 +45,7 @@ const mapStateToProps = state => ({
isLoading: state.getIn(['notifications', 'isLoading'], true), isLoading: state.getIn(['notifications', 'isLoading'], true),
isUnread: state.getIn(['notifications', 'unread']) > 0, isUnread: state.getIn(['notifications', 'unread']) > 0,
hasMore: state.getIn(['notifications', 'hasMore']), hasMore: state.getIn(['notifications', 'hasMore']),
totalQueuedNotificationsCount: state.getIn(['notifications', 'totalQueuedNotificationsCount'], 0),
}); });
export default @connect(mapStateToProps) export default @connect(mapStateToProps)
@ -47,7 +53,6 @@ export default @connect(mapStateToProps)
class Notifications extends React.PureComponent { class Notifications extends React.PureComponent {
static propTypes = { static propTypes = {
columnId: PropTypes.string,
notifications: ImmutablePropTypes.list.isRequired, notifications: ImmutablePropTypes.list.isRequired,
showFilterBar: PropTypes.bool.isRequired, showFilterBar: PropTypes.bool.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -55,6 +60,8 @@ class Notifications extends React.PureComponent {
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
isUnread: PropTypes.bool, isUnread: PropTypes.bool,
hasMore: PropTypes.bool, hasMore: PropTypes.bool,
dequeueNotifications: PropTypes.func,
totalQueuedNotificationsCount: PropTypes.number,
}; };
componentWillUnmount () { componentWillUnmount () {
@ -65,6 +72,7 @@ class Notifications extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
this.handleDequeueNotifications();
this.props.dispatch(scrollTopNotifications(true)); this.props.dispatch(scrollTopNotifications(true));
} }
@ -113,8 +121,12 @@ class Notifications extends React.PureComponent {
} }
} }
handleDequeueNotifications = () => {
this.props.dispatch(dequeueNotifications());
};
render () { render () {
const { intl, notifications, isLoading, isUnread, columnId, hasMore, showFilterBar } = this.props; const { intl, notifications, isLoading, isUnread, hasMore, showFilterBar, totalQueuedNotificationsCount } = this.props;
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />; const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
let scrollableContent = null; let scrollableContent = null;
@ -150,7 +162,7 @@ class Notifications extends React.PureComponent {
const scrollContainer = ( const scrollContainer = (
<ScrollableList <ScrollableList
scrollKey={`notifications-${columnId}`} scrollKey='notifications'
isLoading={isLoading} isLoading={isLoading}
showLoading={isLoading && notifications.size === 0} showLoading={isLoading && notifications.size === 0}
hasMore={hasMore} hasMore={hasMore}
@ -169,6 +181,7 @@ class Notifications extends React.PureComponent {
<ColumnSettingsContainer /> <ColumnSettingsContainer />
</ColumnHeader> </ColumnHeader>
{filterBarContainer} {filterBarContainer}
<TimelineQueueButtonHeader onClick={this.handleDequeueNotifications} count={totalQueuedNotificationsCount} itemType='notification' />
{scrollContainer} {scrollContainer}
</Column> </Column>
); );

View File

@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import { fetchPinnedStatuses } from '../../actions/pin_statuses'; import { fetchPinnedStatuses } from '../../actions/pin_statuses';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
import StatusList from '../../components/status_list'; import StatusList from '../../components/status_list';
import { injectIntl } from 'react-intl'; import { injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { meUsername } from 'gabsocial/initial_state'; import { meUsername } from 'gabsocial/initial_state';
import MissingIndicator from 'gabsocial/components/missing_indicator'; import MissingIndicator from 'gabsocial/components/missing_indicator';
@ -51,6 +51,7 @@ class PinnedStatuses extends ImmutablePureComponent {
statusIds={statusIds} statusIds={statusIds}
scrollKey='pinned_statuses' scrollKey='pinned_statuses'
hasMore={hasMore} hasMore={hasMore}
emptyMessage={<FormattedMessage id='pinned_statuses.none' defaultMessage='No pins to show.'/>}
/> />
</Column> </Column>
); );

View File

@ -1,28 +0,0 @@
import { connect } from 'react-redux';
import ColumnSettings from '../../community_timeline/components/column_settings';
import { changeSetting } from '../../../actions/settings';
import { changeColumnParams } from '../../../actions/columns';
const mapStateToProps = (state, { columnId }) => {
const uuid = columnId;
const columns = state.getIn(['settings', 'columns']);
const index = columns.findIndex(c => c.get('uuid') === uuid);
return {
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'public']),
};
};
const mapDispatchToProps = (dispatch, { columnId }) => {
return {
onChange (key, checked) {
if (columnId) {
dispatch(changeColumnParams(columnId, key, checked));
} else {
dispatch(changeSetting(['public', ...key], checked));
}
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);

View File

@ -1,96 +0,0 @@
import React from 'react';
import { connect } from 'react-redux';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { expandPublicTimeline } from '../../actions/timelines';
import ColumnSettingsContainer from './containers/column_settings_container';
import { connectPublicStream } from '../../actions/streaming';
const messages = defineMessages({
title: { id: 'column.public', defaultMessage: 'Federated timeline' },
});
const mapStateToProps = (state, { onlyMedia, columnId }) => {
const uuid = columnId;
const columns = state.getIn(['settings', 'columns']);
const index = columns.findIndex(c => c.get('uuid') === uuid);
return {
hasUnread: state.getIn(['timelines', `public${onlyMedia ? ':media' : ''}`, 'unread']) > 0,
onlyMedia: (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'public', 'other', 'onlyMedia']),
};
};
export default @connect(mapStateToProps)
@injectIntl
class PublicTimeline extends React.PureComponent {
static contextTypes = {
router: PropTypes.object,
};
static defaultProps = {
onlyMedia: false,
};
static propTypes = {
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
hasUnread: PropTypes.bool,
onlyMedia: PropTypes.bool,
};
componentDidMount () {
const { dispatch, onlyMedia } = this.props;
dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
}
componentDidUpdate (prevProps) {
if (prevProps.onlyMedia !== this.props.onlyMedia) {
const { dispatch, onlyMedia } = this.props;
this.disconnect();
dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
}
}
componentWillUnmount () {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
}
handleLoadMore = maxId => {
const { dispatch, onlyMedia } = this.props;
dispatch(expandPublicTimeline({ maxId, onlyMedia }));
}
render () {
const { intl, hasUnread, onlyMedia } = this.props;
return (
<Column label={intl.formatMessage(messages.title)}>
<ColumnHeader icon='globe' active={hasUnread} title={intl.formatMessage(messages.title)}>
<ColumnSettingsContainer columnId={columnId} />
</ColumnHeader>
<StatusListContainer
timelineId={`public${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore}
scrollKey={`public_timeline-${columnId}`}
emptyMessage={<FormattedMessage id='empty_column.public' defaultMessage='There is nothing here! Write something publicly, or manually follow users from other servers to fill it up' />}
/>
</Column>
);
}
}

View File

@ -12,7 +12,7 @@ import BundleContainer from '../containers/bundle_container';
import ColumnLoading from './column_loading'; import ColumnLoading from './column_loading';
import DrawerLoading from './drawer_loading'; import DrawerLoading from './drawer_loading';
import BundleColumnError from './bundle_column_error'; import BundleColumnError from './bundle_column_error';
import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components'; import { Compose, Notifications, HomeTimeline, CommunityTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components';
import Icon from 'gabsocial/components/icon'; import Icon from 'gabsocial/components/icon';
const messages = defineMessages({ const messages = defineMessages({

View File

@ -27,9 +27,6 @@ export const privateLinks = [
<NavLink className='tabs-bar__link groups' to='/groups' data-preview-title-id='column.groups' > <NavLink className='tabs-bar__link groups' to='/groups' data-preview-title-id='column.groups' >
<FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' /> <FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' />
</NavLink>, </NavLink>,
// <NavLink className='tabs-bar__link home' to='/groups' data-preview-title-id='column.groups' >
// <FormattedMessage id='tabs_bar.groups' defaultMessage='Groups' />
// </NavLink>,
<NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' > <NavLink className='tabs-bar__link optional' to='/search' data-preview-title-id='tabs_bar.search' >
<FormattedMessage id='tabs_bar.search' defaultMessage='Search' /> <FormattedMessage id='tabs_bar.search' defaultMessage='Search' />
</NavLink>, </NavLink>,

View File

@ -6,7 +6,6 @@ import { me } from '../../../initial_state';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import Avatar from '../../../components/avatar'; import Avatar from '../../../components/avatar';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import ComposeFormContainer from '../../compose/containers/compose_form_container';
import IconButton from 'gabsocial/components/icon_button'; import IconButton from 'gabsocial/components/icon_button';
const messages = defineMessages({ const messages = defineMessages({

View File

@ -4,6 +4,7 @@ import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { me } from '../../../initial_state'; import { me } from '../../../initial_state';
import { dequeueTimeline } from 'gabsocial/actions/timelines';
const makeGetStatusIds = () => createSelector([ const makeGetStatusIds = () => createSelector([
(state, { type }) => state.getIn(['settings', type], ImmutableMap()), (state, { type }) => state.getIn(['settings', type], ImmutableMap()),
@ -28,17 +29,22 @@ const makeGetStatusIds = () => createSelector([
}); });
}); });
const makeMapStateToProps = () => { const mapStateToProps = (state, {timelineId}) => {
const getStatusIds = makeGetStatusIds(); const getStatusIds = makeGetStatusIds();
const mapStateToProps = (state, { timelineId }) => ({ return {
statusIds: getStatusIds(state, { type: timelineId }), statusIds: getStatusIds(state, { type: timelineId }),
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true), isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false), isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
hasMore: state.getIn(['timelines', timelineId, 'hasMore']), hasMore: state.getIn(['timelines', timelineId, 'hasMore']),
}); totalQueuedItemsCount: state.getIn(['timelines', timelineId, 'totalQueuedItemsCount']),
};
return mapStateToProps;
}; };
export default connect(makeMapStateToProps)(StatusList); const mapDispatchToProps = (dispatch, ownProps) => ({
onDequeueTimeline(timelineId) {
dispatch(dequeueTimeline(timelineId, ownProps.onLoadMore));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(StatusList);

View File

@ -31,7 +31,6 @@ import SearchPage from 'gabsocial/pages/search_page';
import HomePage from 'gabsocial/pages/home_page'; import HomePage from 'gabsocial/pages/home_page';
import { import {
Compose,
Status, Status,
GettingStarted, GettingStarted,
CommunityTimeline, CommunityTimeline,

View File

@ -14,10 +14,6 @@ export function HomeTimeline () {
return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline'); return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline');
} }
export function PublicTimeline () {
return import(/* webpackChunkName: "features/public_timeline" */'../../public_timeline');
}
export function CommunityTimeline () { export function CommunityTimeline () {
return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline'); return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline');
} }

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "تم التحقق مِن مِلْكية هذا الرابط بتاريخ {date}", "account.link_verified_on": "تم التحقق مِن مِلْكية هذا الرابط بتاريخ {date}",
"account.locked_info": "تم تأمين خصوصية هذا الحساب عبر قفل. صاحب الحساب يُراجِع يدويا طلبات المتابَعة و الاشتراك بحسابه.", "account.locked_info": "تم تأمين خصوصية هذا الحساب عبر قفل. صاحب الحساب يُراجِع يدويا طلبات المتابَعة و الاشتراك بحسابه.",
"account.media": "وسائط", "account.media": "وسائط",
"account.mention": "أُذكُر/ي @{name}", "account.mention": "أُذكُر/ي",
"account.moved_to": "{name} إنتقل إلى :", "account.moved_to": "{name} إنتقل إلى :",
"account.mute": "كتم @{name}", "account.mute": "كتم @{name}",
"account.mute_notifications": "كتم الإخطارات من @{name}", "account.mute_notifications": "كتم الإخطارات من @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mentar a @{name}", "account.mention": "Mentar",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Silenciar a @{name}", "account.mute": "Silenciar a @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "এই লিংকের মালিকানা চেক করা হয়েছে {date} তারিকে", "account.link_verified_on": "এই লিংকের মালিকানা চেক করা হয়েছে {date} তারিকে",
"account.locked_info": "এই নিবন্ধনের গোপনীয়তার ক্ষেত্র তালা দেওয়া আছে। নিবন্ধনকারী অনুসরণ করার অনুমতি যাদেরকে দেবেন, শুধু তারাই অনুসরণ করতে পারবেন।", "account.locked_info": "এই নিবন্ধনের গোপনীয়তার ক্ষেত্র তালা দেওয়া আছে। নিবন্ধনকারী অনুসরণ করার অনুমতি যাদেরকে দেবেন, শুধু তারাই অনুসরণ করতে পারবেন।",
"account.media": "ছবি বা ভিডিও", "account.media": "ছবি বা ভিডিও",
"account.mention": "@{name} কে উল্লেখ করুন", "account.mention": "কে উল্লেখ করুন",
"account.moved_to": "{name} চলে গেছে এখানে:", "account.moved_to": "{name} চলে গেছে এখানে:",
"account.mute": "@{name}র কার্যক্রম সরিয়ে ফেলুন", "account.mute": "@{name}র কার্যক্রম সরিয়ে ফেলুন",
"account.mute_notifications": "@{name}র প্রজ্ঞাপন আপনার কাছ থেকে সরিয়ে ফেলুন", "account.mute_notifications": "@{name}র প্রজ্ঞাপন আপনার কাছ থেকে সরিয়ে ফেলুন",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}", "account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}",
"account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.", "account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.",
"account.media": "Mèdia", "account.media": "Mèdia",
"account.mention": "Esmentar @{name}", "account.mention": "Esmentar",
"account.moved_to": "{name} s'ha mogut a:", "account.moved_to": "{name} s'ha mogut a:",
"account.mute": "Silencia @{name}", "account.mute": "Silencia @{name}",
"account.mute_notifications": "Notificacions desactivades de @{name}", "account.mute_notifications": "Notificacions desactivades de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}", "account.link_verified_on": "A prupietà di stu ligame hè stata verificata u {date}",
"account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.", "account.locked_info": "U statutu di vita privata di u contu hè chjosu. U pruprietariu esamina manualmente e dumande d'abbunamentu.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mintuvà @{name}", "account.mention": "Mintuvà",
"account.moved_to": "{name} hè partutu nant'à:", "account.moved_to": "{name} hè partutu nant'à:",
"account.mute": "Piattà @{name}", "account.mute": "Piattà @{name}",
"account.mute_notifications": "Piattà nutificazione da @{name}", "account.mute_notifications": "Piattà nutificazione da @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}", "account.link_verified_on": "Vlastnictví tohoto odkazu bylo zkontrolováno {date}",
"account.locked_info": "Stav soukromí tohoto účtu je nastaven na zamčeno. Jeho vlastník ručně posuzuje, kdo ho může sledovat.", "account.locked_info": "Stav soukromí tohoto účtu je nastaven na zamčeno. Jeho vlastník ručně posuzuje, kdo ho může sledovat.",
"account.media": "Média", "account.media": "Média",
"account.mention": "Zmínit uživatele @{name}", "account.mention": "Zmínit uživatele",
"account.moved_to": "{name} se přesunul/a na:", "account.moved_to": "{name} se přesunul/a na:",
"account.mute": "Skrýt uživatele @{name}", "account.mute": "Skrýt uživatele @{name}",
"account.mute_notifications": "Skrýt oznámení od uživatele @{name}", "account.mute_notifications": "Skrýt oznámení od uživatele @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Gwiriwyd perchnogaeth y ddolen yma ar {date}", "account.link_verified_on": "Gwiriwyd perchnogaeth y ddolen yma ar {date}",
"account.locked_info": "Mae'r statws preifatrwydd cyfrif hwn wedi'i osod i gloi. Mae'r perchennog yn adolygu'r sawl sy'n gallu eu dilyn.", "account.locked_info": "Mae'r statws preifatrwydd cyfrif hwn wedi'i osod i gloi. Mae'r perchennog yn adolygu'r sawl sy'n gallu eu dilyn.",
"account.media": "Cyfryngau", "account.media": "Cyfryngau",
"account.mention": "Crybwyll @{name}", "account.mention": "Crybwyll",
"account.moved_to": "Mae @{name} wedi symud i:", "account.moved_to": "Mae @{name} wedi symud i:",
"account.mute": "Tawelu @{name}", "account.mute": "Tawelu @{name}",
"account.mute_notifications": "Cuddio hysbysiadau o @{name}", "account.mute_notifications": "Cuddio hysbysiadau o @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ejerskabet af dette link blev tjekket den %{date}", "account.link_verified_on": "Ejerskabet af dette link blev tjekket den %{date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Medie", "account.media": "Medie",
"account.mention": "Nævn @{name}", "account.mention": "Nævn",
"account.moved_to": "{name} er flyttet til:", "account.moved_to": "{name} er flyttet til:",
"account.mute": "Dæmp @{name}", "account.mute": "Dæmp @{name}",
"account.mute_notifications": "Dæmp notifikationer fra @{name}", "account.mute_notifications": "Dæmp notifikationer fra @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Besitz dieses Links wurde geprüft am {date}", "account.link_verified_on": "Besitz dieses Links wurde geprüft am {date}",
"account.locked_info": "Der Privatsphärenstatus dieses Accounts wurde auf gesperrt gesetzt. Die Person bestimmt manuell wer ihm/ihr folgen darf.", "account.locked_info": "Der Privatsphärenstatus dieses Accounts wurde auf gesperrt gesetzt. Die Person bestimmt manuell wer ihm/ihr folgen darf.",
"account.media": "Medien", "account.media": "Medien",
"account.mention": "@{name} erwähnen", "account.mention": "erwähnen",
"account.moved_to": "{name} ist umgezogen auf:", "account.moved_to": "{name} ist umgezogen auf:",
"account.mute": "@{name} stummschalten", "account.mute": "@{name} stummschalten",
"account.mute_notifications": "Benachrichtigungen von @{name} verbergen", "account.mute_notifications": "Benachrichtigungen von @{name} verbergen",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου εκλέχθηκε την {date}", "account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου εκλέχθηκε την {date}",
"account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού είναι κλειδωμένη. Ο ιδιοκτήτης επιβεβαιώνει χειροκίνητα ποιος μπορεί να τον ακολουθήσει.", "account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού είναι κλειδωμένη. Ο ιδιοκτήτης επιβεβαιώνει χειροκίνητα ποιος μπορεί να τον ακολουθήσει.",
"account.media": "Πολυμέσα", "account.media": "Πολυμέσα",
"account.mention": "Ανάφερε @{name}", "account.mention": "Ανάφερε",
"account.moved_to": "{name} μεταφέρθηκε στο:", "account.moved_to": "{name} μεταφέρθηκε στο:",
"account.mute": "Σώπασε τον/την @{name}", "account.mute": "Σώπασε τον/την @{name}",
"account.mute_notifications": "Σώπασε τις ειδοποιήσεις από τον/την @{name}", "account.mute_notifications": "Σώπασε τις ειδοποιήσεις από τον/την @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mention @{name}", "account.mention": "Mention",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}", "account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "La posedanto de tiu ligilo estis kontrolita je {date}", "account.link_verified_on": "La posedanto de tiu ligilo estis kontrolita je {date}",
"account.locked_info": "La privateco de tiu konto estas elektita kiel fermita. La posedanto povas mane akcepti tiun, kiu povas sekvi rin.", "account.locked_info": "La privateco de tiu konto estas elektita kiel fermita. La posedanto povas mane akcepti tiun, kiu povas sekvi rin.",
"account.media": "Aŭdovidaĵoj", "account.media": "Aŭdovidaĵoj",
"account.mention": "Mencii @{name}", "account.mention": "Mencii",
"account.moved_to": "{name} moviĝis al:", "account.moved_to": "{name} moviĝis al:",
"account.mute": "Silentigi @{name}", "account.mute": "Silentigi @{name}",
"account.mute_notifications": "Silentigi sciigojn el @{name}", "account.mute_notifications": "Silentigi sciigojn el @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mencionar a @{name}", "account.mention": "Mencionar",
"account.moved_to": "{name} se ha mudado a:", "account.moved_to": "{name} se ha mudado a:",
"account.mute": "Silenciar a @{name}", "account.mute": "Silenciar a @{name}",
"account.mute_notifications": "Silenciar notificaciones de @{name}", "account.mute_notifications": "Silenciar notificaciones de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Esteka honen jabetzaren egiaztaketa data: {date}", "account.link_verified_on": "Esteka honen jabetzaren egiaztaketa data: {date}",
"account.locked_info": "Kontu honen pribatutasun egoera blokeatuta gisa ezarri da. Jabeak eskuz erabakitzen du nork jarraitu diezaioken.", "account.locked_info": "Kontu honen pribatutasun egoera blokeatuta gisa ezarri da. Jabeak eskuz erabakitzen du nork jarraitu diezaioken.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Aipatu @{name}", "account.mention": "Aipatu",
"account.moved_to": "{name} hona lekualdatu da:", "account.moved_to": "{name} hona lekualdatu da:",
"account.mute": "Mututu @{name}", "account.mute": "Mututu @{name}",
"account.mute_notifications": "Mututu @{name}(r)en jakinarazpenak", "account.mute_notifications": "Mututu @{name}(r)en jakinarazpenak",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "مالکیت این نشانی در تایخ {date} بررسی شد", "account.link_verified_on": "مالکیت این نشانی در تایخ {date} بررسی شد",
"account.locked_info": "این حساب خصوصی است. صاحب این حساب تصمیم می‌گیرد که چه کسی می‌تواند پیگیرش باشد.", "account.locked_info": "این حساب خصوصی است. صاحب این حساب تصمیم می‌گیرد که چه کسی می‌تواند پیگیرش باشد.",
"account.media": "عکس و ویدیو", "account.media": "عکس و ویدیو",
"account.mention": "نام‌بردن از @{name}", "account.mention": "نام‌بردن از",
"account.moved_to": "{name} منتقل شده است به:", "account.moved_to": "{name} منتقل شده است به:",
"account.mute": "بی‌صدا کردن @{name}", "account.mute": "بی‌صدا کردن @{name}",
"account.mute_notifications": "بی‌صداکردن اعلان‌ها از طرف @{name}", "account.mute_notifications": "بی‌صداکردن اعلان‌ها از طرف @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Tämän linkin omistaja tarkistettiin {date}", "account.link_verified_on": "Tämän linkin omistaja tarkistettiin {date}",
"account.locked_info": "Tämän tili on yksityinen. Käyttäjä vahvistaa itse kuka voi seurata häntä.", "account.locked_info": "Tämän tili on yksityinen. Käyttäjä vahvistaa itse kuka voi seurata häntä.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mainitse @{name}", "account.mention": "Mainitse",
"account.moved_to": "{name} on muuttanut instanssiin:", "account.moved_to": "{name} on muuttanut instanssiin:",
"account.mute": "Mykistä @{name}", "account.mute": "Mykistä @{name}",
"account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}", "account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "La propriété de ce lien a été vérifiée le {date}", "account.link_verified_on": "La propriété de ce lien a été vérifiée le {date}",
"account.locked_info": "Ce compte est verrouillé. Son propriétaire approuve manuellement qui peut le ou la suivre.", "account.locked_info": "Ce compte est verrouillé. Son propriétaire approuve manuellement qui peut le ou la suivre.",
"account.media": "Média", "account.media": "Média",
"account.mention": "Mentionner @{name}", "account.mention": "Mentionner",
"account.moved_to": "{name} a déménagé vers:", "account.moved_to": "{name} a déménagé vers:",
"account.mute": "Masquer @{name}", "account.mute": "Masquer @{name}",
"account.mute_notifications": "Ignorer les notifications de @{name}", "account.mute_notifications": "Ignorer les notifications de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "A propiedade de esta ligazón foi comprobada en {date}", "account.link_verified_on": "A propiedade de esta ligazón foi comprobada en {date}",
"account.locked_info": "O estado da intimidade de esta conta estableceuse en pechado. A persoa dona da conta revisa quen pode seguila.", "account.locked_info": "O estado da intimidade de esta conta estableceuse en pechado. A persoa dona da conta revisa quen pode seguila.",
"account.media": "Medios", "account.media": "Medios",
"account.mention": "Mencionar @{name}", "account.mention": "Mencionar",
"account.moved_to": "{name} marchou a:", "account.moved_to": "{name} marchou a:",
"account.mute": "Acalar @{name}", "account.mute": "Acalar @{name}",
"account.mute_notifications": "Acalar as notificacións de @{name}", "account.mute_notifications": "Acalar as notificacións de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "מדיה", "account.media": "מדיה",
"account.mention": "אזכור של @{name}", "account.mention": "אזכור של",
"account.moved_to": "החשבון {name} הועבר אל:", "account.moved_to": "החשבון {name} הועבר אל:",
"account.mute": "להשתיק את @{name}", "account.mute": "להשתיק את @{name}",
"account.mute_notifications": "להסתיר התראות מאת @{name}", "account.mute_notifications": "להסתיר התראות מאת @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mention @{name}", "account.mention": "Mention",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}", "account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Spomeni @{name}", "account.mention": "Spomeni",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Utišaj @{name}", "account.mute": "Utišaj @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Média", "account.media": "Média",
"account.mention": "@{name} említése", "account.mention": "említése",
"account.moved_to": "{name} átköltözött:", "account.moved_to": "{name} átköltözött:",
"account.mute": "@{name} némítása", "account.mute": "@{name} némítása",
"account.mute_notifications": "@{name} értesítések némítása", "account.mute_notifications": "@{name} értesítések némítása",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Մեդիա", "account.media": "Մեդիա",
"account.mention": "Նշել @{name}֊ին", "account.mention": "Նշել",
"account.moved_to": "{name}֊ը տեղափոխվել է՝", "account.moved_to": "{name}֊ը տեղափոխվել է՝",
"account.mute": "Լռեցնել @{name}֊ին", "account.mute": "Լռեցնել @{name}֊ին",
"account.mute_notifications": "Անջատել ծանուցումները @{name}֊ից", "account.mute_notifications": "Անջատել ծանուցումները @{name}֊ից",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Balasan @{name}", "account.mention": "Balasan",
"account.moved_to": "{name} telah pindah ke:", "account.moved_to": "{name} telah pindah ke:",
"account.mute": "Bisukan @{name}", "account.mute": "Bisukan @{name}",
"account.mute_notifications": "Sembunyikan notifikasi dari @{name}", "account.mute_notifications": "Sembunyikan notifikasi dari @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mencionar @{name}", "account.mention": "Mencionar",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Celar @{name}", "account.mute": "Celar @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "La proprietà di questo link è stata controllata il {date}", "account.link_verified_on": "La proprietà di questo link è stata controllata il {date}",
"account.locked_info": "Il livello di privacy di questo account è impostato a \"bloccato\". Il proprietario esamina manualmente le richieste di seguirlo.", "account.locked_info": "Il livello di privacy di questo account è impostato a \"bloccato\". Il proprietario esamina manualmente le richieste di seguirlo.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Menziona @{name}", "account.mention": "Menziona",
"account.moved_to": "{name} si è trasferito su:", "account.moved_to": "{name} si è trasferito su:",
"account.mute": "Silenzia @{name}", "account.mute": "Silenzia @{name}",
"account.mute_notifications": "Silenzia notifiche da @{name}", "account.mute_notifications": "Silenzia notifiche da @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "このリンクの所有権は{date}に確認されました", "account.link_verified_on": "このリンクの所有権は{date}に確認されました",
"account.locked_info": "このアカウントは承認制アカウントです。相手が承認するまでフォローは完了しません。", "account.locked_info": "このアカウントは承認制アカウントです。相手が承認するまでフォローは完了しません。",
"account.media": "メディア", "account.media": "メディア",
"account.mention": "@{name}さんにトゥート", "account.mention": "さんにトゥート",
"account.moved_to": "{name}さんは引っ越しました:", "account.moved_to": "{name}さんは引っ越しました:",
"account.mute": "@{name}さんをミュート", "account.mute": "@{name}さんをミュート",
"account.mute_notifications": "@{name}さんからの通知を受け取らない", "account.mute_notifications": "@{name}さんからの通知を受け取らない",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "მედია", "account.media": "მედია",
"account.mention": "ასახელეთ @{name}", "account.mention": "ასახელეთ",
"account.moved_to": "{name} გადავიდა:", "account.moved_to": "{name} გადავიდა:",
"account.mute": "გააჩუმე @{name}", "account.mute": "გააჩუმე @{name}",
"account.mute_notifications": "გააჩუმე შეტყობინებები @{name}-სგან", "account.mute_notifications": "გააჩუმე შეტყობინებები @{name}-სგან",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Сілтеме меншігі расталған күн {date}", "account.link_verified_on": "Сілтеме меншігі расталған күн {date}",
"account.locked_info": "Бұл қолданушы өзі туралы мәліметтерді жасырған. Тек жазылғандар ғана көре алады.", "account.locked_info": "Бұл қолданушы өзі туралы мәліметтерді жасырған. Тек жазылғандар ғана көре алады.",
"account.media": "Медиа", "account.media": "Медиа",
"account.mention": "Аталым @{name}", "account.mention": "Аталым",
"account.moved_to": "{name} көшіп кетті:", "account.moved_to": "{name} көшіп кетті:",
"account.mute": "Үнсіз қылу @{name}", "account.mute": "Үнсіз қылу @{name}",
"account.mute_notifications": "@{name} туралы ескертпелерді жасыру", "account.mute_notifications": "@{name} туралы ескертпелерді жасыру",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "{date}에 이 링크의 소유권이 확인 됨", "account.link_verified_on": "{date}에 이 링크의 소유권이 확인 됨",
"account.locked_info": "이 계정의 프라이버시 설정은 잠금으로 설정되어 있습니다. 계정 소유자가 수동으로 팔로어를 승인합니다.", "account.locked_info": "이 계정의 프라이버시 설정은 잠금으로 설정되어 있습니다. 계정 소유자가 수동으로 팔로어를 승인합니다.",
"account.media": "미디어", "account.media": "미디어",
"account.mention": "@{name}에게 글쓰기", "account.mention": "에게 글쓰기",
"account.moved_to": "{name}는 계정을 이동했습니다:", "account.moved_to": "{name}는 계정을 이동했습니다:",
"account.mute": "@{name} 뮤트", "account.mute": "@{name} 뮤트",
"account.mute_notifications": "@{name}의 알림을 뮤트", "account.mute_notifications": "@{name}의 알림을 뮤트",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Šīs saites piederība ir pārbaudīta {date}", "account.link_verified_on": "Šīs saites piederība ir pārbaudīta {date}",
"account.locked_info": "Šī konta privātuma status ir iestatīts slēgts. Īpašnieks izskatīs un izvēlēsies kas viņam drīkst sekot.", "account.locked_info": "Šī konta privātuma status ir iestatīts slēgts. Īpašnieks izskatīs un izvēlēsies kas viņam drīkst sekot.",
"account.media": "Mēdiji", "account.media": "Mēdiji",
"account.mention": "Piemin @{name}", "account.mention": "Piemin",
"account.moved_to": "{name} ir pārvācies uz:", "account.moved_to": "{name} ir pārvācies uz:",
"account.mute": "Apklusināt @{name}", "account.mute": "Apklusināt @{name}",
"account.mute_notifications": "Nerādīt paziņojumus no @{name}", "account.mute_notifications": "Nerādīt paziņojumus no @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mention @{name}", "account.mention": "Mention",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}", "account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}", "account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}",
"account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.", "account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Vermeld @{name}", "account.mention": "Vermeld",
"account.moved_to": "{name} is verhuisd naar:", "account.moved_to": "{name} is verhuisd naar:",
"account.mute": "Negeer @{name}", "account.mute": "Negeer @{name}",
"account.mute_notifications": "Negeer meldingen van @{name}", "account.mute_notifications": "Negeer meldingen van @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Nevn @{name}", "account.mention": "Nevn",
"account.moved_to": "{name} har flyttet til:", "account.moved_to": "{name} har flyttet til:",
"account.mute": "Demp @{name}", "account.mute": "Demp @{name}",
"account.mute_notifications": "Ignorer varsler fra @{name}", "account.mute_notifications": "Ignorer varsler fra @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "La proprietat daqueste ligam foguèt verificada lo {date}", "account.link_verified_on": "La proprietat daqueste ligam foguèt verificada lo {date}",
"account.locked_info": "Lestatut de privacitat del compte es configurat sus clavat. Lo proprietari causís qual pòt sègre son compte.", "account.locked_info": "Lestatut de privacitat del compte es configurat sus clavat. Lo proprietari causís qual pòt sègre son compte.",
"account.media": "Mèdias", "account.media": "Mèdias",
"account.mention": "Mencionar @{name}", "account.mention": "Mencionar",
"account.moved_to": "{name} a mudat los catons a:", "account.moved_to": "{name} a mudat los catons a:",
"account.mute": "Rescondre @{name}", "account.mute": "Rescondre @{name}",
"account.mute_notifications": "Rescondre las notificacions de @{name}", "account.mute_notifications": "Rescondre las notificacions de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Własność tego odnośnika została potwierdzona {date}", "account.link_verified_on": "Własność tego odnośnika została potwierdzona {date}",
"account.locked_info": "To konto jest prywatne. Właściciel ręcznie wybiera kto może go śledzić.", "account.locked_info": "To konto jest prywatne. Właściciel ręcznie wybiera kto może go śledzić.",
"account.media": "Zawartość multimedialna", "account.media": "Zawartość multimedialna",
"account.mention": "Wspomnij o @{name}", "account.mention": "Wspomnij",
"account.moved_to": "{name} przeniósł(-osła) się do:", "account.moved_to": "{name} przeniósł(-osła) się do:",
"account.mute": "Wycisz @{name}", "account.mute": "Wycisz @{name}",
"account.mute_notifications": "Wycisz powiadomienia o @{name}", "account.mute_notifications": "Wycisz powiadomienia o @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "A posse desse link foi verificada em {date}", "account.link_verified_on": "A posse desse link foi verificada em {date}",
"account.locked_info": "Essa conta está trancada. Se você a seguir sua solicitação será revisada manualmente.", "account.locked_info": "Essa conta está trancada. Se você a seguir sua solicitação será revisada manualmente.",
"account.media": "Mídia", "account.media": "Mídia",
"account.mention": "Mencionar @{name}", "account.mention": "Mencionar",
"account.moved_to": "{name} se mudou para:", "account.moved_to": "{name} se mudou para:",
"account.mute": "Silenciar @{name}", "account.mute": "Silenciar @{name}",
"account.mute_notifications": "Silenciar notificações de @{name}", "account.mute_notifications": "Silenciar notificações de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "A posse deste link foi verificada em {date}", "account.link_verified_on": "A posse deste link foi verificada em {date}",
"account.locked_info": "O estatuto de privacidade desta conta é fechado. O dono revê manualmente que a pode seguir.", "account.locked_info": "O estatuto de privacidade desta conta é fechado. O dono revê manualmente que a pode seguir.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mencionar @{name}", "account.mention": "Mencionar",
"account.moved_to": "{name} mudou a sua conta para:", "account.moved_to": "{name} mudou a sua conta para:",
"account.mute": "Silenciar @{name}", "account.mute": "Silenciar @{name}",
"account.mute_notifications": "Silenciar notificações de @{name}", "account.mute_notifications": "Silenciar notificações de @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Deținerea acestui link a fost verificată la {date}", "account.link_verified_on": "Deținerea acestui link a fost verificată la {date}",
"account.locked_info": "Acest profil este privat. Această persoană gestioneaz manual cine o urmărește.", "account.locked_info": "Acest profil este privat. Această persoană gestioneaz manual cine o urmărește.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Menționează @{name}", "account.mention": "Menționează",
"account.moved_to": "{name} a fost mutat la:", "account.moved_to": "{name} a fost mutat la:",
"account.mute": "Oprește @{name}", "account.mute": "Oprește @{name}",
"account.mute_notifications": "Oprește notificările de la @{name}", "account.mute_notifications": "Oprește notificările de la @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Vlastníctvo tohto odkazu bolo skontrolované {date}", "account.link_verified_on": "Vlastníctvo tohto odkazu bolo skontrolované {date}",
"account.locked_info": "Stav súkromia pre tento účet je nastavený na zamknutý. Jeho vlastník sám prehodnocuje, kto ho môže sledovať.", "account.locked_info": "Stav súkromia pre tento účet je nastavený na zamknutý. Jeho vlastník sám prehodnocuje, kto ho môže sledovať.",
"account.media": "Médiá", "account.media": "Médiá",
"account.mention": "Spomeň @{name}", "account.mention": "Spomeň",
"account.moved_to": "{name} sa presunul/a na:", "account.moved_to": "{name} sa presunul/a na:",
"account.mute": "Ignorovať @{name}", "account.mute": "Ignorovať @{name}",
"account.mute_notifications": "Stĺm oboznámenia od @{name}", "account.mute_notifications": "Stĺm oboznámenia od @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Lastništvo te povezave je bilo preverjeno {date}", "account.link_verified_on": "Lastništvo te povezave je bilo preverjeno {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Mediji", "account.media": "Mediji",
"account.mention": "Omeni @{name}", "account.mention": "Omeni",
"account.moved_to": "{name} se je premaknil na:", "account.moved_to": "{name} se je premaknil na:",
"account.mute": "Utišaj @{name}", "account.mute": "Utišaj @{name}",
"account.mute_notifications": "Utišaj obvestila od @{name}", "account.mute_notifications": "Utišaj obvestila od @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Pronësia e kësaj lidhjeje qe kontrolluar më {date}", "account.link_verified_on": "Pronësia e kësaj lidhjeje qe kontrolluar më {date}",
"account.locked_info": "Gjendja e privatësisë së kësaj llogarie është caktuar si e kyçur. I zoti merr dorazi në shqyrtim cilët mund ta ndjekin.", "account.locked_info": "Gjendja e privatësisë së kësaj llogarie është caktuar si e kyçur. I zoti merr dorazi në shqyrtim cilët mund ta ndjekin.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Përmendni @{name}", "account.mention": "Përmendni",
"account.moved_to": "{name} ka kaluar te:", "account.moved_to": "{name} ka kaluar te:",
"account.mute": "Heshtoni @{name}", "account.mute": "Heshtoni @{name}",
"account.mute_notifications": "Heshtoji njoftimet prej @{name}", "account.mute_notifications": "Heshtoji njoftimet prej @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Медији", "account.media": "Медији",
"account.mention": "Помени корисника @{name}", "account.mention": "Помени корисника",
"account.moved_to": "{name} се померио на:", "account.moved_to": "{name} се померио на:",
"account.mute": "Ућуткај корисника @{name}", "account.mute": "Ућуткај корисника @{name}",
"account.mute_notifications": "Искључи обавештења од корисника @{name}", "account.mute_notifications": "Искључи обавештења од корисника @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Nämna @{name}", "account.mention": "Nämna",
"account.moved_to": "{name} har flyttat till:", "account.moved_to": "{name} har flyttat till:",
"account.mute": "Tysta @{name}", "account.mute": "Tysta @{name}",
"account.mute_notifications": "Stäng av notifieringar från @{name}", "account.mute_notifications": "Stäng av notifieringar från @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Media", "account.media": "Media",
"account.mention": "Mention @{name}", "account.mention": "Mention",
"account.moved_to": "{name} has moved to:", "account.moved_to": "{name} has moved to:",
"account.mute": "Mute @{name}", "account.mute": "Mute @{name}",
"account.mute_notifications": "Mute notifications from @{name}", "account.mute_notifications": "Mute notifications from @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "ఈ లంకె యొక్క యాజమాన్యం {date}న పరీక్షించబడింది", "account.link_verified_on": "ఈ లంకె యొక్క యాజమాన్యం {date}న పరీక్షించబడింది",
"account.locked_info": "ఈ ఖాతా యొక్క గోప్యత స్థితి లాక్ చేయబడి వుంది. ఈ ఖాతాను ఎవరు అనుసరించవచ్చో యజమానే నిర్ణయం తీసుకుంటారు.", "account.locked_info": "ఈ ఖాతా యొక్క గోప్యత స్థితి లాక్ చేయబడి వుంది. ఈ ఖాతాను ఎవరు అనుసరించవచ్చో యజమానే నిర్ణయం తీసుకుంటారు.",
"account.media": "మీడియా", "account.media": "మీడియా",
"account.mention": "@{name}ను ప్రస్తావించు", "account.mention": "ప్రస్తావించు",
"account.moved_to": "{name} ఇక్కడికి మారారు:", "account.moved_to": "{name} ఇక్కడికి మారారు:",
"account.mute": "@{name}ను మ్యూట్ చెయ్యి", "account.mute": "@{name}ను మ్యూట్ చెయ్యి",
"account.mute_notifications": "@{name}నుంచి ప్రకటనలను మ్యూట్ చెయ్యి", "account.mute_notifications": "@{name}నుంచి ప్రకటనలను మ్యూట్ చెయ్యి",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "ตรวจสอบความเป็นเจ้าของของลิงก์นี้เมื่อ {date}", "account.link_verified_on": "ตรวจสอบความเป็นเจ้าของของลิงก์นี้เมื่อ {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "สื่อ", "account.media": "สื่อ",
"account.mention": "กล่าวถึง @{name}", "account.mention": "กล่าวถึง",
"account.moved_to": "{name} ได้ย้ายไปยัง:", "account.moved_to": "{name} ได้ย้ายไปยัง:",
"account.mute": "ปิดเสียง @{name}", "account.mute": "ปิดเสียง @{name}",
"account.mute_notifications": "ปิดเสียงการแจ้งเตือนจาก @{name}", "account.mute_notifications": "ปิดเสียงการแจ้งเตือนจาก @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Bu bağlantının mülkiyeti {date} tarihinde kontrol edildi", "account.link_verified_on": "Bu bağlantının mülkiyeti {date} tarihinde kontrol edildi",
"account.locked_info": "Bu hesabın gizlilik durumu kilitli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini elle inceler.", "account.locked_info": "Bu hesabın gizlilik durumu kilitli olarak ayarlanmış. Sahibi, onu kimin takip edebileceğini elle inceler.",
"account.media": "Medya", "account.media": "Medya",
"account.mention": "@{name} kullanıcısından bahset", "account.mention": "kullanıcısından bahset",
"account.moved_to": "{name} şuraya taşındı:", "account.moved_to": "{name} şuraya taşındı:",
"account.mute": "@{name} kullanıcısını sessize al", "account.mute": "@{name} kullanıcısını sessize al",
"account.mute_notifications": "@{name} kullanıcısının bildirimlerini kapat", "account.mute_notifications": "@{name} kullanıcısının bildirimlerini kapat",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "Медіа", "account.media": "Медіа",
"account.mention": "Згадати @{name}", "account.mention": "Згадати",
"account.moved_to": "{name} переїхав на:", "account.moved_to": "{name} переїхав на:",
"account.mute": "Заглушити @{name}", "account.mute": "Заглушити @{name}",
"account.mute_notifications": "Не показувати сповіщення від @{name}", "account.mute_notifications": "Не показувати сповіщення від @{name}",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "媒体", "account.media": "媒体",
"account.mention": "提及 @{name}", "account.mention": "提及",
"account.moved_to": "{name} 已经迁移到:", "account.moved_to": "{name} 已经迁移到:",
"account.mute": "隐藏 @{name}", "account.mute": "隐藏 @{name}",
"account.mute_notifications": "隐藏来自 @{name} 的通知", "account.mute_notifications": "隐藏来自 @{name} 的通知",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "Ownership of this link was checked on {date}", "account.link_verified_on": "Ownership of this link was checked on {date}",
"account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.", "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
"account.media": "媒體", "account.media": "媒體",
"account.mention": "提及 @{name}", "account.mention": "提及",
"account.moved_to": "{name} 已經遷移到:", "account.moved_to": "{name} 已經遷移到:",
"account.mute": "將 @{name} 靜音", "account.mute": "將 @{name} 靜音",
"account.mute_notifications": "將來自 @{name} 的通知靜音", "account.mute_notifications": "將來自 @{name} 的通知靜音",

View File

@ -18,7 +18,7 @@
"account.link_verified_on": "此連結的所有權已在 {date} 檢查", "account.link_verified_on": "此連結的所有權已在 {date} 檢查",
"account.locked_info": "此帳號的隱私狀態被設為鎖定,擁有者將手動審核可關注此帳號的人。", "account.locked_info": "此帳號的隱私狀態被設為鎖定,擁有者將手動審核可關注此帳號的人。",
"account.media": "媒體", "account.media": "媒體",
"account.mention": "提及 @{name}", "account.mention": "提及",
"account.moved_to": "{name} 已遷移至:", "account.moved_to": "{name} 已遷移至:",
"account.mute": "靜音 @{name}", "account.mute": "靜音 @{name}",
"account.mute_notifications": "靜音來自 @{name} 的通知", "account.mute_notifications": "靜音來自 @{name} 的通知",

View File

@ -6,6 +6,9 @@ import {
NOTIFICATIONS_FILTER_SET, NOTIFICATIONS_FILTER_SET,
NOTIFICATIONS_CLEAR, NOTIFICATIONS_CLEAR,
NOTIFICATIONS_SCROLL_TOP, NOTIFICATIONS_SCROLL_TOP,
NOTIFICATIONS_UPDATE_QUEUE,
NOTIFICATIONS_DEQUEUE,
MAX_QUEUED_NOTIFICATIONS,
} from '../actions/notifications'; } from '../actions/notifications';
import { import {
ACCOUNT_BLOCK_SUCCESS, ACCOUNT_BLOCK_SUCCESS,
@ -21,6 +24,8 @@ const initialState = ImmutableMap({
top: false, top: false,
unread: 0, unread: 0,
isLoading: false, isLoading: false,
queuedNotifications: ImmutableList(), //max = MAX_QUEUED_NOTIFICATIONS
totalQueuedNotificationsCount: 0, //used for queuedItems overflow for MAX_QUEUED_NOTIFICATIONS+
}); });
const notificationToMap = notification => ImmutableMap({ const notificationToMap = notification => ImmutableMap({
@ -93,6 +98,32 @@ const deleteByStatus = (state, statusId) => {
return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId)); return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId));
}; };
const updateNotificationsQueue = (state, notification, intlMessages, intlLocale) => {
const queuedNotifications = state.getIn(['queuedNotifications'], ImmutableList());
const listedNotifications = state.getIn(['items'], ImmutableList());
const totalQueuedNotificationsCount = state.getIn(['totalQueuedNotificationsCount'], 0);
let alreadyExists = queuedNotifications.find(existingQueuedNotification => existingQueuedNotification.id === notification.id);
if (!alreadyExists) alreadyExists = listedNotifications.find(existingListedNotification => existingListedNotification.get('id') === notification.id);
if (alreadyExists) {
return state;
}
let newQueuedNotifications = queuedNotifications;
return state.withMutations(mutable => {
if (totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
mutable.set('queuedNotifications', newQueuedNotifications.push({
notification,
intlMessages,
intlLocale,
}));
}
mutable.set('totalQueuedNotificationsCount', totalQueuedNotificationsCount + 1);
});
};
export default function notifications(state = initialState, action) { export default function notifications(state = initialState, action) {
switch(action.type) { switch(action.type) {
case NOTIFICATIONS_EXPAND_REQUEST: case NOTIFICATIONS_EXPAND_REQUEST:
@ -105,6 +136,13 @@ export default function notifications(state = initialState, action) {
return updateTop(state, action.top); return updateTop(state, action.top);
case NOTIFICATIONS_UPDATE: case NOTIFICATIONS_UPDATE:
return normalizeNotification(state, action.notification); return normalizeNotification(state, action.notification);
case NOTIFICATIONS_UPDATE_QUEUE:
return updateNotificationsQueue(state, action.notification, action.intlMessages, action.intlLocale);
case NOTIFICATIONS_DEQUEUE:
return state.withMutations(mutable => {
mutable.set('queuedNotifications', ImmutableList())
mutable.set('totalQueuedNotificationsCount', 0)
});
case NOTIFICATIONS_EXPAND_SUCCESS: case NOTIFICATIONS_EXPAND_SUCCESS:
return expandNormalizedNotifications(state, action.notifications, action.next); return expandNormalizedNotifications(state, action.notifications, action.next);
case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_BLOCK_SUCCESS:

View File

@ -1,6 +1,5 @@
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings'; import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings';
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications'; import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
import { COLUMN_PARAMS_CHANGE } from '../actions/columns';
import { STORE_HYDRATE } from '../actions/store'; import { STORE_HYDRATE } from '../actions/store';
import { EMOJI_USE } from '../actions/emojis'; import { EMOJI_USE } from '../actions/emojis';
import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists'; import { LIST_DELETE_SUCCESS, LIST_FETCH_FAIL } from '../actions/lists';
@ -88,17 +87,6 @@ const defaultColumns = fromJS([
const hydrate = (state, settings) => state.mergeDeep(settings).update('columns', (val = defaultColumns) => val); const hydrate = (state, settings) => state.mergeDeep(settings).update('columns', (val = defaultColumns) => val);
const changeColumnParams = (state, uuid, path, value) => {
const columns = state.get('columns');
const index = columns.findIndex(item => item.get('uuid') === uuid);
const newColumns = columns.update(index, column => column.updateIn(['params', ...path], () => value));
return state
.set('columns', newColumns)
.set('saved', false);
};
const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false); const updateFrequentEmojis = (state, emoji) => state.update('frequentlyUsedEmojis', ImmutableMap(), map => map.update(emoji.id, 0, count => count + 1)).set('saved', false);
const filterDeadListColumns = (state, listId) => state.update('columns', columns => columns.filterNot(column => column.get('id') === 'LIST' && column.get('params').get('id') === listId)); const filterDeadListColumns = (state, listId) => state.update('columns', columns => columns.filterNot(column => column.get('id') === 'LIST' && column.get('params').get('id') === listId));
@ -112,8 +100,6 @@ export default function settings(state = initialState, action) {
return state return state
.setIn(action.path, action.value) .setIn(action.path, action.value)
.set('saved', false); .set('saved', false);
case COLUMN_PARAMS_CHANGE:
return changeColumnParams(state, action.uuid, action.path, action.value);
case EMOJI_USE: case EMOJI_USE:
return updateFrequentEmojis(state, action.emoji); return updateFrequentEmojis(state, action.emoji);
case SETTING_SAVE: case SETTING_SAVE:

View File

@ -7,6 +7,9 @@ import {
TIMELINE_EXPAND_FAIL, TIMELINE_EXPAND_FAIL,
TIMELINE_CONNECT, TIMELINE_CONNECT,
TIMELINE_DISCONNECT, TIMELINE_DISCONNECT,
TIMELINE_UPDATE_QUEUE,
TIMELINE_DEQUEUE,
MAX_QUEUED_ITEMS,
} from '../actions/timelines'; } from '../actions/timelines';
import { import {
ACCOUNT_BLOCK_SUCCESS, ACCOUNT_BLOCK_SUCCESS,
@ -25,6 +28,8 @@ const initialTimeline = ImmutableMap({
isLoading: false, isLoading: false,
hasMore: true, hasMore: true,
items: ImmutableList(), items: ImmutableList(),
queuedItems: ImmutableList(), //max= MAX_QUEUED_ITEMS
totalQueuedItemsCount: 0, //used for queuedItems overflow for MAX_QUEUED_ITEMS+
}); });
const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => { const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => {
@ -77,6 +82,28 @@ const updateTimeline = (state, timeline, status) => {
})); }));
}; };
const updateTimelineQueue = (state, timeline, status) => {
const queuedStatuses = state.getIn([timeline, 'queuedItems'], ImmutableList());
const listedStatuses = state.getIn([timeline, 'items'], ImmutableList());
const totalQueuedItemsCount = state.getIn([timeline, 'totalQueuedItemsCount'], 0);
let alreadyExists = queuedStatuses.find(existingQueuedStatus => existingQueuedStatus.get('id') === status.get('id'));
if (!alreadyExists) alreadyExists = listedStatuses.find(existingListedStatusId => existingListedStatusId === status.get('id'));
if (alreadyExists) {
return state;
}
let newQueuedStatuses = queuedStatuses;
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
if (totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
mMap.set('queuedItems', newQueuedStatuses.push(status));
}
mMap.set('totalQueuedItemsCount', totalQueuedItemsCount + 1);
}));
};
const deleteStatus = (state, id, accountId, references, exclude_account = null) => { const deleteStatus = (state, id, accountId, references, exclude_account = null) => {
state.keySeq().forEach(timeline => { state.keySeq().forEach(timeline => {
if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`)))
@ -126,6 +153,13 @@ export default function timelines(state = initialState, action) {
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent); return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
case TIMELINE_UPDATE: case TIMELINE_UPDATE:
return updateTimeline(state, action.timeline, fromJS(action.status)); return updateTimeline(state, action.timeline, fromJS(action.status));
case TIMELINE_UPDATE_QUEUE:
return updateTimelineQueue(state, action.timeline, fromJS(action.status));
case TIMELINE_DEQUEUE:
return state.update(action.timeline, initialTimeline, map => map.withMutations(mMap => {
mMap.set('queuedItems', ImmutableList())
mMap.set('totalQueuedItemsCount', 0)
}));
case TIMELINE_DELETE: case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf); return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
case TIMELINE_CLEAR: case TIMELINE_CLEAR:

View File

@ -1 +0,0 @@
<svg id="icon-explore" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"><defs><style>.cls-1{fill:#00d177;}</style></defs><title>icon-explore-00d177</title><path id="_00d177" data-name="00d177" class="cls-1" d="M10,0a7,7,0,1,0,7,7A7,7,0,0,0,10,0Zm2.32,10.09-.32.32a.75.75,0,0,0-.17.3,3.79,3.79,0,0,1-.14.48l-.49,1.32a5.26,5.26,0,0,1-1.2.14v-.78a2.11,2.11,0,0,0-.64-1.44.9.9,0,0,1-.26-.64V8.88a.9.9,0,0,0-.47-.79c-.4-.22-1-.53-1.38-.73a4.18,4.18,0,0,1-.89-.62l0,0a3.12,3.12,0,0,1-.51-.58l-1-1.45A5.68,5.68,0,0,1,7.77,1.82l.67.34a.46.46,0,0,0,.66-.41V1.43a5.06,5.06,0,0,1,.69-.06l.79.79a.45.45,0,0,1,0,.64l-.13.14-.29.29a.22.22,0,0,0,0,.32l.13.13a.22.22,0,0,1,0,.32l-.22.22a.21.21,0,0,1-.16.07H9.65a.2.2,0,0,0-.15.06l-.28.28a.21.21,0,0,0-.05.26l.44.88a.22.22,0,0,1-.2.33H9.25A.22.22,0,0,1,9.1,6l-.26-.23a.46.46,0,0,0-.44-.08L7.52,6a.33.33,0,0,0-.23.32.33.33,0,0,0,.19.3l.31.16a2,2,0,0,0,.86.2c.29,0,.63.77.9.9h1.88a.91.91,0,0,1,.64.27l.39.38a.88.88,0,0,1,.25.61A1.29,1.29,0,0,1,12.32,10.09Zm2.45-2.57a.74.74,0,0,1-.4-.29l-.51-.76a.68.68,0,0,1,0-.75l.56-.83a.58.58,0,0,1,.26-.23L15,4.48A5.48,5.48,0,0,1,15.65,7a5.83,5.83,0,0,1-.06.72Z" transform="translate(-3)"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1 +0,0 @@
<svg id="icon-explore" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"><defs><style>.cls-1{fill:#d8d8d8;}</style></defs><title>icon-explore-d8d8d8</title><path id="d8d8d8" class="cls-1" d="M10,0a7,7,0,1,0,7,7A7,7,0,0,0,10,0Zm2.32,10.09-.32.32a.75.75,0,0,0-.17.3,3.79,3.79,0,0,1-.14.48l-.49,1.32a5.26,5.26,0,0,1-1.2.14v-.78a2.11,2.11,0,0,0-.64-1.44.9.9,0,0,1-.26-.64V8.88a.9.9,0,0,0-.47-.79c-.4-.22-1-.53-1.38-.73a4.18,4.18,0,0,1-.89-.62l0,0a3.12,3.12,0,0,1-.51-.58l-1-1.45A5.68,5.68,0,0,1,7.77,1.82l.67.34a.46.46,0,0,0,.66-.41V1.43a5.06,5.06,0,0,1,.69-.06l.79.79a.45.45,0,0,1,0,.64l-.13.14-.29.29a.22.22,0,0,0,0,.32l.13.13a.22.22,0,0,1,0,.32l-.22.22a.21.21,0,0,1-.16.07H9.65a.2.2,0,0,0-.15.06l-.28.28a.21.21,0,0,0-.05.26l.44.88a.22.22,0,0,1-.2.33H9.25A.22.22,0,0,1,9.1,6l-.26-.23a.46.46,0,0,0-.44-.08L7.52,6a.33.33,0,0,0-.23.32.33.33,0,0,0,.19.3l.31.16a2,2,0,0,0,.86.2c.29,0,.63.77.9.9h1.88a.91.91,0,0,1,.64.27l.39.38a.88.88,0,0,1,.25.61A1.29,1.29,0,0,1,12.32,10.09Zm2.45-2.57a.74.74,0,0,1-.4-.29l-.51-.76a.68.68,0,0,1,0-.75l.56-.83a.58.58,0,0,1,.26-.23L15,4.48A5.48,5.48,0,0,1,15.65,7a5.83,5.83,0,0,1-.06.72Z" transform="translate(-3)"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1 +0,0 @@
<svg id="icon-explore" xmlns="http://www.w3.org/2000/svg" width="14" height="114" viewBox="0 0 14 114"><defs><style>.cls-1{fill:#d8d8d8;}.cls-2{fill:#00d177;}</style></defs><title>icon-explore-sprite</title><path id="d8d8d8" class="cls-1" d="M10,100a7,7,0,1,0,7,7A7,7,0,0,0,10,100Zm2.32,10.09-.32.32a.75.75,0,0,0-.17.3,3.79,3.79,0,0,1-.14.48l-.49,1.32a5.26,5.26,0,0,1-1.2.14v-.78a2.11,2.11,0,0,0-.64-1.44.9.9,0,0,1-.26-.64v-.91a.9.9,0,0,0-.47-.79c-.4-.22-1-.53-1.38-.73a4.18,4.18,0,0,1-.89-.62l0,0a3.12,3.12,0,0,1-.51-.58l-1-1.45a5.68,5.68,0,0,1,2.92-2.87l.67.34a.46.46,0,0,0,.66-.41v-.32a5.06,5.06,0,0,1,.69-.06l.79.79a.45.45,0,0,1,0,.64l-.13.14-.29.29a.22.22,0,0,0,0,.32l.13.13a.22.22,0,0,1,0,.32l-.22.22a.21.21,0,0,1-.16.07H9.65a.2.2,0,0,0-.15.06l-.28.28a.21.21,0,0,0-.05.26l.44.88a.22.22,0,0,1-.2.33H9.25A.22.22,0,0,1,9.1,106l-.26-.23a.46.46,0,0,0-.44-.08l-.88.29a.33.33,0,0,0-.23.32.33.33,0,0,0,.19.3l.31.16a2,2,0,0,0,.86.2c.29,0,.63.77.9.9h1.88a.91.91,0,0,1,.64.27l.39.38a.88.88,0,0,1,.25.61A1.29,1.29,0,0,1,12.32,110.09Zm2.45-2.57a.74.74,0,0,1-.4-.29l-.51-.76a.68.68,0,0,1,0-.75l.56-.83a.58.58,0,0,1,.26-.23l.36-.18a5.48,5.48,0,0,1,.61,2.52,5.83,5.83,0,0,1-.06.72Z" transform="translate(-3)"/><path id="_00d177" data-name="00d177" class="cls-2" d="M10,0a7,7,0,1,0,7,7A7,7,0,0,0,10,0Zm2.32,10.09-.32.32a.75.75,0,0,0-.17.3,3.79,3.79,0,0,1-.14.48l-.49,1.32a5.26,5.26,0,0,1-1.2.14v-.78a2.11,2.11,0,0,0-.64-1.44.9.9,0,0,1-.26-.64V8.88a.9.9,0,0,0-.47-.79c-.4-.22-1-.53-1.38-.73a4.18,4.18,0,0,1-.89-.62l0,0a3.12,3.12,0,0,1-.51-.58l-1-1.45A5.68,5.68,0,0,1,7.77,1.82l.67.34a.46.46,0,0,0,.66-.41V1.43a5.06,5.06,0,0,1,.69-.06l.79.79a.45.45,0,0,1,0,.64l-.13.14-.29.29a.22.22,0,0,0,0,.32l.13.13a.22.22,0,0,1,0,.32l-.22.22a.21.21,0,0,1-.16.07H9.65a.2.2,0,0,0-.15.06l-.28.28a.21.21,0,0,0-.05.26l.44.88a.22.22,0,0,1-.2.33H9.25A.22.22,0,0,1,9.1,6l-.26-.23a.46.46,0,0,0-.44-.08L7.52,6a.33.33,0,0,0-.23.32.33.33,0,0,0,.19.3l.31.16a2,2,0,0,0,.86.2c.29,0,.63.77.9.9h1.88a.91.91,0,0,1,.64.27l.39.38a.88.88,0,0,1,.25.61A1.29,1.29,0,0,1,12.32,10.09Zm2.45-2.57a.74.74,0,0,1-.4-.29l-.51-.76a.68.68,0,0,1,0-.75l.56-.83a.58.58,0,0,1,.26-.23L15,4.48A5.48,5.48,0,0,1,15.65,7a5.83,5.83,0,0,1-.06.72Z" transform="translate(-3)"/></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1 +0,0 @@
<svg id="icon-home" xmlns="http://www.w3.org/2000/svg" width="16.11" height="14" viewBox="0 0 16.11 14"><defs><style>.cls-1{fill:#00d177;}</style></defs><title>navigation-icon-master</title><path id="_00d177" data-name="00d177" class="cls-1" d="M17.69,6.34,15.94,4.81V1.59A.79.79,0,0,0,15.15.8H14.09a.79.79,0,0,0-.79.79v.93L10.7.26A1,1,0,0,0,10.27,0h0l-.16,0H9.91L9.75,0h0A1,1,0,0,0,9.3.26l-7,6.08A1.06,1.06,0,0,0,2.2,7.83,1.12,1.12,0,0,0,3,8.19H4.06v4.75A1.06,1.06,0,0,0,5.11,14h3.6V9.51h2.74V14h3.44a1.06,1.06,0,0,0,1-1.06V8.19H17a1.12,1.12,0,0,0,.78-.36A1.06,1.06,0,0,0,17.69,6.34Z" transform="translate(-1.95)"/></svg>

Before

Width:  |  Height:  |  Size: 622 B

View File

@ -1 +0,0 @@
<svg id="icon-home" xmlns="http://www.w3.org/2000/svg" width="16.11" height="14" viewBox="0 0 16.11 14"><defs><style>.cls-1{fill:#d8d8d8;}</style></defs><title>navigation-icon-master</title><path id="d8d8d8" class="cls-1" d="M17.69,6.34,15.94,4.81V1.59A.79.79,0,0,0,15.15.8H14.09a.79.79,0,0,0-.79.79v.93L10.7.26A1,1,0,0,0,10.27,0h0l-.16,0H9.91L9.75,0h0A1,1,0,0,0,9.3.26l-7,6.08A1.06,1.06,0,0,0,2.2,7.83,1.12,1.12,0,0,0,3,8.19H4.06v4.75A1.06,1.06,0,0,0,5.11,14h3.6V9.51h2.74V14h3.44a1.06,1.06,0,0,0,1-1.06V8.19H17a1.12,1.12,0,0,0,.78-.36A1.06,1.06,0,0,0,17.69,6.34Z" transform="translate(-1.95)"/></svg>

Before

Width:  |  Height:  |  Size: 602 B

View File

@ -1 +0,0 @@
<svg id="icon-home" xmlns="http://www.w3.org/2000/svg" width="16.11" height="114" viewBox="0 0 16.11 114"><defs><style>.cls-1{fill:#d8d8d8;}.cls-2{fill:#00d177;}</style></defs><title>icon-home-sprite</title><path id="d8d8d8" class="cls-1" d="M17.69,106.34l-1.75-1.53v-3.22a.79.79,0,0,0-.79-.79H14.09a.79.79,0,0,0-.79.79v.93l-2.6-2.26a1,1,0,0,0-.43-.23h0l-.16,0H9.91l-.16,0h0a1,1,0,0,0-.43.23l-7,6.08a1.06,1.06,0,0,0-.11,1.49,1.12,1.12,0,0,0,.78.36H4.06v4.75A1.06,1.06,0,0,0,5.11,114h3.6v-4.49h2.74V114h3.44a1.06,1.06,0,0,0,1-1.06v-4.75H17a1.12,1.12,0,0,0,.78-.36A1.06,1.06,0,0,0,17.69,106.34Z" transform="translate(-1.95)"/><path id="_00d177" data-name="00d177" class="cls-2" d="M17.69,6.34,15.94,4.81V1.59A.79.79,0,0,0,15.15.8H14.09a.79.79,0,0,0-.79.79v.93L10.7.26A1,1,0,0,0,10.27,0h0l-.16,0H9.91L9.75,0h0A1,1,0,0,0,9.3.26l-7,6.08A1.06,1.06,0,0,0,2.2,7.83,1.12,1.12,0,0,0,3,8.19H4.06v4.75A1.06,1.06,0,0,0,5.11,14h3.6V9.51h2.74V14h3.44a1.06,1.06,0,0,0,1-1.06V8.19H17a1.12,1.12,0,0,0,.78-.36A1.06,1.06,0,0,0,17.69,6.34Z" transform="translate(-1.95)"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +0,0 @@
<svg id="icon-messages" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"><defs><style>.cls-1{fill:#00d177;}</style></defs><title>icon-messages-00d177</title><path id="_00d177" data-name="00d177" class="cls-1" d="M17,14,15.4,9.21a6.52,6.52,0,1,0-3.19,3.19Z" transform="translate(-3)"/></svg>

Before

Width:  |  Height:  |  Size: 319 B

View File

@ -1 +0,0 @@
<svg id="icon-messages" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14"><defs><style>.cls-1{fill:#d8d8d8;}</style></defs><title>icon-messages-d8d8d8</title><path id="d8d8d8" class="cls-1" d="M17,14,15.4,9.21a6.52,6.52,0,1,0-3.19,3.19Z" transform="translate(-3)"/></svg>

Before

Width:  |  Height:  |  Size: 299 B

View File

@ -1 +0,0 @@
<svg id="icon-messages" xmlns="http://www.w3.org/2000/svg" width="14" height="114" viewBox="0 0 14 114"><defs><style>.cls-1{fill:#d8d8d8;}.cls-2{fill:#00d177;}</style></defs><title>icon-messages-sprite</title><path id="d8d8d8" class="cls-1" d="M17,114l-1.6-4.79a6.52,6.52,0,1,0-3.19,3.19Z" transform="translate(-3)"/><path id="_00d177" data-name="00d177" class="cls-2" d="M17,14,15.4,9.21a6.52,6.52,0,1,0-3.19,3.19Z" transform="translate(-3)"/></svg>

Before

Width:  |  Height:  |  Size: 450 B

View File

@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="16" height="14" viewBox="0 0 16 14"><defs><style>.cls-1{fill:#b2f1d5;}.cls-2{fill:#333;}</style></defs><title>icon-new-gab</title><path class="cls-1" d="M8,11.5a8.14,8.14,0,0,1-2.45-.38l-.71-.22-.61.43a6.62,6.62,0,0,1-1.79.9A8.09,8.09,0,0,0,3.06,11l.33-.88-.64-.68A4.28,4.28,0,0,1,1.5,6.5c0-2.76,2.92-5,6.5-5s6.5,2.24,6.5,5S11.59,11.5,8,11.5Z"/><path class="cls-2" d="M4.5,5.5a1,1,0,1,0,1,1A1,1,0,0,0,4.5,5.5ZM8,5.5a1,1,0,1,0,1,1A1,1,0,0,0,8,5.5Zm3.5,0a1,1,0,1,0,1,1A1,1,0,0,0,11.5,5.5ZM8,0C3.58,0,0,2.91,0,6.5a5.72,5.72,0,0,0,1.66,3.95A8,8,0,0,1,.21,12.73.75.75,0,0,0,.75,14,7.55,7.55,0,0,0,5.1,12.55,9.47,9.47,0,0,0,8,13c4.42,0,8-2.91,8-6.5S12.42,0,8,0ZM8,11.5a8.14,8.14,0,0,1-2.45-.38l-.71-.22-.61.43a6.62,6.62,0,0,1-1.79.9A8.09,8.09,0,0,0,3.06,11l.33-.88-.64-.68A4.28,4.28,0,0,1,1.5,6.5c0-2.76,2.92-5,6.5-5s6.5,2.24,6.5,5S11.59,11.5,8,11.5Z"/></svg>

Before

Width:  |  Height:  |  Size: 933 B

Some files were not shown because too many files have changed in this diff Show More