Merge remote-tracking branch 'parent/main' into upstream-20240624
This commit is contained in:
commit
af2727387e
88 changed files with 703 additions and 597 deletions
16
app/javascript/mastodon/actions/notification_policies.ts
Normal file
16
app/javascript/mastodon/actions/notification_policies.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import {
|
||||
apiGetNotificationPolicy,
|
||||
apiUpdateNotificationsPolicy,
|
||||
} from 'mastodon/api/notification_policies';
|
||||
import type { NotificationPolicy } from 'mastodon/models/notification_policy';
|
||||
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
|
||||
|
||||
export const fetchNotificationPolicy = createDataLoadingThunk(
|
||||
'notificationPolicy/fetch',
|
||||
() => apiGetNotificationPolicy(),
|
||||
);
|
||||
|
||||
export const updateNotificationsPolicy = createDataLoadingThunk(
|
||||
'notificationPolicy/update',
|
||||
(policy: Partial<NotificationPolicy>) => apiUpdateNotificationsPolicy(policy),
|
||||
);
|
|
@ -45,10 +45,6 @@ export const NOTIFICATIONS_MARK_AS_READ = 'NOTIFICATIONS_MARK_AS_READ';
|
|||
export const NOTIFICATIONS_SET_BROWSER_SUPPORT = 'NOTIFICATIONS_SET_BROWSER_SUPPORT';
|
||||
export const NOTIFICATIONS_SET_BROWSER_PERMISSION = 'NOTIFICATIONS_SET_BROWSER_PERMISSION';
|
||||
|
||||
export const NOTIFICATION_POLICY_FETCH_REQUEST = 'NOTIFICATION_POLICY_FETCH_REQUEST';
|
||||
export const NOTIFICATION_POLICY_FETCH_SUCCESS = 'NOTIFICATION_POLICY_FETCH_SUCCESS';
|
||||
export const NOTIFICATION_POLICY_FETCH_FAIL = 'NOTIFICATION_POLICY_FETCH_FAIL';
|
||||
|
||||
export const NOTIFICATION_REQUESTS_FETCH_REQUEST = 'NOTIFICATION_REQUESTS_FETCH_REQUEST';
|
||||
export const NOTIFICATION_REQUESTS_FETCH_SUCCESS = 'NOTIFICATION_REQUESTS_FETCH_SUCCESS';
|
||||
export const NOTIFICATION_REQUESTS_FETCH_FAIL = 'NOTIFICATION_REQUESTS_FETCH_FAIL';
|
||||
|
@ -363,40 +359,6 @@ export function setBrowserPermission (value) {
|
|||
};
|
||||
}
|
||||
|
||||
export const fetchNotificationPolicy = () => (dispatch) => {
|
||||
dispatch(fetchNotificationPolicyRequest());
|
||||
|
||||
api().get('/api/v1/notifications/policy').then(({ data }) => {
|
||||
dispatch(fetchNotificationPolicySuccess(data));
|
||||
}).catch(err => {
|
||||
dispatch(fetchNotificationPolicyFail(err));
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchNotificationPolicyRequest = () => ({
|
||||
type: NOTIFICATION_POLICY_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchNotificationPolicySuccess = policy => ({
|
||||
type: NOTIFICATION_POLICY_FETCH_SUCCESS,
|
||||
policy,
|
||||
});
|
||||
|
||||
export const fetchNotificationPolicyFail = error => ({
|
||||
type: NOTIFICATION_POLICY_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const updateNotificationsPolicy = params => (dispatch) => {
|
||||
dispatch(fetchNotificationPolicyRequest());
|
||||
|
||||
api().put('/api/v1/notifications/policy', params).then(({ data }) => {
|
||||
dispatch(fetchNotificationPolicySuccess(data));
|
||||
}).catch(err => {
|
||||
dispatch(fetchNotificationPolicyFail(err));
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchNotificationRequests = () => (dispatch, getState) => {
|
||||
const params = {};
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
|
|||
},
|
||||
|
||||
onDisconnect() {
|
||||
dispatch(disconnectTimeline(timelineId));
|
||||
dispatch(disconnectTimeline({ timeline: timelineId }));
|
||||
|
||||
if (options.fallback) {
|
||||
// @ts-expect-error
|
||||
|
|
|
@ -6,9 +6,11 @@ import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
|||
|
||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
import { submitMarkers } from './markers';
|
||||
import {timelineDelete} from './timelines_typed';
|
||||
|
||||
export { disconnectTimeline } from './timelines_typed';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
|
||||
|
||||
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
|
||||
|
@ -17,7 +19,6 @@ export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
|
|||
|
||||
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
|
||||
export const TIMELINE_LOAD_PENDING = 'TIMELINE_LOAD_PENDING';
|
||||
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
|
||||
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
|
||||
|
||||
export const TIMELINE_MARK_AS_PARTIAL = 'TIMELINE_MARK_AS_PARTIAL';
|
||||
|
@ -62,16 +63,10 @@ export function updateTimeline(timeline, status, accept) {
|
|||
export function deleteFromTimelines(id) {
|
||||
return (dispatch, getState) => {
|
||||
const accountId = getState().getIn(['statuses', id, 'account']);
|
||||
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => status.get('id'));
|
||||
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => status.get('id')).valueSeq().toJSON();
|
||||
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
|
||||
|
||||
dispatch({
|
||||
type: TIMELINE_DELETE,
|
||||
id,
|
||||
accountId,
|
||||
references,
|
||||
reblogOf,
|
||||
});
|
||||
dispatch(timelineDelete({ statusId: id, accountId, references, reblogOf }));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -227,12 +222,6 @@ export function connectTimeline(timeline) {
|
|||
};
|
||||
}
|
||||
|
||||
export const disconnectTimeline = timeline => ({
|
||||
type: TIMELINE_DISCONNECT,
|
||||
timeline,
|
||||
usePendingItems: preferPendingItems,
|
||||
});
|
||||
|
||||
export const markAsPartial = timeline => ({
|
||||
type: TIMELINE_MARK_AS_PARTIAL,
|
||||
timeline,
|
||||
|
|
20
app/javascript/mastodon/actions/timelines_typed.ts
Normal file
20
app/javascript/mastodon/actions/timelines_typed.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
||||
|
||||
export const disconnectTimeline = createAction(
|
||||
'timeline/disconnect',
|
||||
({ timeline }: { timeline: string }) => ({
|
||||
payload: {
|
||||
timeline,
|
||||
usePendingItems: preferPendingItems,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
export const timelineDelete = createAction<{
|
||||
statusId: string;
|
||||
accountId: string;
|
||||
references: string[];
|
||||
reblogOf: string | null;
|
||||
}>('timelines/delete');
|
10
app/javascript/mastodon/api/notification_policies.ts
Normal file
10
app/javascript/mastodon/api/notification_policies.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { apiRequest } from 'mastodon/api';
|
||||
import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies';
|
||||
|
||||
export const apiGetNotificationPolicy = () =>
|
||||
apiRequest<NotificationPolicyJSON>('GET', '/v1/notifications/policy');
|
||||
|
||||
export const apiUpdateNotificationsPolicy = (
|
||||
policy: Partial<NotificationPolicyJSON>,
|
||||
) =>
|
||||
apiRequest<NotificationPolicyJSON>('PUT', '/v1/notifications/policy', policy);
|
12
app/javascript/mastodon/api_types/notification_policies.ts
Normal file
12
app/javascript/mastodon/api_types/notification_policies.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
// See app/serializers/rest/notification_policy_serializer.rb
|
||||
|
||||
export interface NotificationPolicyJSON {
|
||||
filter_not_following: boolean;
|
||||
filter_not_followers: boolean;
|
||||
filter_new_accounts: boolean;
|
||||
filter_private_mentions: boolean;
|
||||
summary: {
|
||||
pending_requests_count: number;
|
||||
pending_notifications_count: number;
|
||||
};
|
||||
}
|
|
@ -133,7 +133,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (prevProps.accountId === me && accountId !== me) {
|
||||
dispatch(disconnectTimeline(`account:${me}`));
|
||||
dispatch(disconnectTimeline({ timeline: `account:${me}` }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,7 +141,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||
const { dispatch, accountId } = this.props;
|
||||
|
||||
if (accountId === me) {
|
||||
dispatch(disconnectTimeline(`account:${me}`));
|
||||
dispatch(disconnectTimeline({ timeline: `account:${me}` }));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ class ColumnSettings extends PureComponent {
|
|||
alertsEnabled: PropTypes.bool,
|
||||
browserSupport: PropTypes.bool,
|
||||
browserPermission: PropTypes.string,
|
||||
notificationPolicy: ImmutablePropTypes.map,
|
||||
notificationPolicy: PropTypes.object.isRequired,
|
||||
onChangePolicy: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -88,22 +88,22 @@ class ColumnSettings extends PureComponent {
|
|||
<h3><FormattedMessage id='notifications.policy.title' defaultMessage='Filter out notifications from…' /></h3>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<CheckboxWithLabel checked={notificationPolicy.get('filter_not_following')} onChange={this.handleFilterNotFollowing}>
|
||||
<CheckboxWithLabel checked={notificationPolicy.filter_not_following} onChange={this.handleFilterNotFollowing}>
|
||||
<strong><FormattedMessage id='notifications.policy.filter_not_following_title' defaultMessage="People you don't follow" /></strong>
|
||||
<span className='hint'><FormattedMessage id='notifications.policy.filter_not_following_hint' defaultMessage='Until you manually approve them' /></span>
|
||||
</CheckboxWithLabel>
|
||||
|
||||
<CheckboxWithLabel checked={notificationPolicy.get('filter_not_followers')} onChange={this.handleFilterNotFollowers}>
|
||||
<CheckboxWithLabel checked={notificationPolicy.filter_not_followers} onChange={this.handleFilterNotFollowers}>
|
||||
<strong><FormattedMessage id='notifications.policy.filter_not_followers_title' defaultMessage='People not following you' /></strong>
|
||||
<span className='hint'><FormattedMessage id='notifications.policy.filter_not_followers_hint' defaultMessage='Including people who have been following you fewer than {days, plural, one {one day} other {# days}}' values={{ days: 3 }} /></span>
|
||||
</CheckboxWithLabel>
|
||||
|
||||
<CheckboxWithLabel checked={notificationPolicy.get('filter_new_accounts')} onChange={this.handleFilterNewAccounts}>
|
||||
<CheckboxWithLabel checked={notificationPolicy.filter_new_accounts} onChange={this.handleFilterNewAccounts}>
|
||||
<strong><FormattedMessage id='notifications.policy.filter_new_accounts_title' defaultMessage='New accounts' /></strong>
|
||||
<span className='hint'><FormattedMessage id='notifications.policy.filter_new_accounts.hint' defaultMessage='Created within the past {days, plural, one {one day} other {# days}}' values={{ days: 30 }} /></span>
|
||||
</CheckboxWithLabel>
|
||||
|
||||
<CheckboxWithLabel checked={notificationPolicy.get('filter_private_mentions')} onChange={this.handleFilterPrivateMentions}>
|
||||
<CheckboxWithLabel checked={notificationPolicy.filter_private_mentions} onChange={this.handleFilterPrivateMentions}>
|
||||
<strong><FormattedMessage id='notifications.policy.filter_private_mentions_title' defaultMessage='Unsolicited private mentions' /></strong>
|
||||
<span className='hint'><FormattedMessage id='notifications.policy.filter_private_mentions_hint' defaultMessage="Filtered unless it's in reply to your own mention or if you follow the sender" /></span>
|
||||
</CheckboxWithLabel>
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
|
||||
import { fetchNotificationPolicy } from 'mastodon/actions/notifications';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { toCappedNumber } from 'mastodon/utils/numbers';
|
||||
|
||||
export const FilteredNotificationsBanner = () => {
|
||||
const dispatch = useDispatch();
|
||||
const policy = useSelector(state => state.get('notificationPolicy'));
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchNotificationPolicy());
|
||||
|
||||
const interval = setInterval(() => {
|
||||
dispatch(fetchNotificationPolicy());
|
||||
}, 120000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
if (policy === null || policy.getIn(['summary', 'pending_notifications_count']) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link className='filtered-notifications-banner' to='/notifications/requests'>
|
||||
<Icon icon={InventoryIcon} />
|
||||
|
||||
<div className='filtered-notifications-banner__text'>
|
||||
<strong><FormattedMessage id='filtered_notifications_banner.title' defaultMessage='Filtered notifications' /></strong>
|
||||
<span><FormattedMessage id='filtered_notifications_banner.pending_requests' defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know' values={{ count: policy.getIn(['summary', 'pending_requests_count']) }} /></span>
|
||||
</div>
|
||||
|
||||
<div className='filtered-notifications-banner__badge'>
|
||||
<div className='filtered-notifications-banner__badge__badge'>{toCappedNumber(policy.getIn(['summary', 'pending_notifications_count']))}</div>
|
||||
<FormattedMessage id='filtered_notifications_banner.mentions' defaultMessage='{count, plural, one {mention} other {mentions}}' values={{ count: policy.getIn(['summary', 'pending_notifications_count']) }} />
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,68 @@
|
|||
import { useEffect } from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import InventoryIcon from '@/material-icons/400-24px/inventory_2.svg?react';
|
||||
import { fetchNotificationPolicy } from 'mastodon/actions/notification_policies';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||
import { toCappedNumber } from 'mastodon/utils/numbers';
|
||||
|
||||
export const FilteredNotificationsBanner: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const policy = useAppSelector((state) => state.notificationPolicy);
|
||||
|
||||
useEffect(() => {
|
||||
void dispatch(fetchNotificationPolicy());
|
||||
|
||||
const interval = setInterval(() => {
|
||||
void dispatch(fetchNotificationPolicy());
|
||||
}, 120000);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
if (policy === null || policy.summary.pending_notifications_count === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
className='filtered-notifications-banner'
|
||||
to='/notifications/requests'
|
||||
>
|
||||
<Icon icon={InventoryIcon} id='filtered-notifications' />
|
||||
|
||||
<div className='filtered-notifications-banner__text'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='filtered_notifications_banner.title'
|
||||
defaultMessage='Filtered notifications'
|
||||
/>
|
||||
</strong>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='filtered_notifications_banner.pending_requests'
|
||||
defaultMessage='Notifications from {count, plural, =0 {no one} one {one person} other {# people}} you may know'
|
||||
values={{ count: policy.summary.pending_requests_count }}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className='filtered-notifications-banner__badge'>
|
||||
<div className='filtered-notifications-banner__badge__badge'>
|
||||
{toCappedNumber(policy.summary.pending_notifications_count)}
|
||||
</div>
|
||||
<FormattedMessage
|
||||
id='filtered_notifications_banner.mentions'
|
||||
defaultMessage='{count, plural, one {mention} other {mentions}}'
|
||||
values={{ count: policy.summary.pending_notifications_count }}
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
|
@ -4,7 +4,8 @@ import { connect } from 'react-redux';
|
|||
|
||||
import { showAlert } from '../../../actions/alerts';
|
||||
import { openModal } from '../../../actions/modal';
|
||||
import { setFilter, clearNotifications, requestBrowserPermission, updateNotificationsPolicy } from '../../../actions/notifications';
|
||||
import { updateNotificationsPolicy } from '../../../actions/notification_policies';
|
||||
import { setFilter, clearNotifications, requestBrowserPermission } from '../../../actions/notifications';
|
||||
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
|
||||
import { changeSetting } from '../../../actions/settings';
|
||||
import ColumnSettings from '../components/column_settings';
|
||||
|
@ -15,13 +16,16 @@ const messages = defineMessages({
|
|||
permissionDenied: { id: 'notifications.permission_denied_alert', defaultMessage: 'Desktop notifications can\'t be enabled, as browser permission has been denied before' },
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {import('mastodon/store').RootState} state
|
||||
*/
|
||||
const mapStateToProps = state => ({
|
||||
settings: state.getIn(['settings', 'notifications']),
|
||||
pushSettings: state.get('push_notifications'),
|
||||
alertsEnabled: state.getIn(['settings', 'notifications', 'alerts']).includes(true),
|
||||
browserSupport: state.getIn(['notifications', 'browserSupport']),
|
||||
browserPermission: state.getIn(['notifications', 'browserPermission']),
|
||||
notificationPolicy: state.get('notificationPolicy'),
|
||||
notificationPolicy: state.notificationPolicy,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
|
|
|
@ -147,14 +147,14 @@
|
|||
"compose.published.body": "Postitus avaldatud.",
|
||||
"compose.published.open": "Ava",
|
||||
"compose.saved.body": "Postitus salvestatud.",
|
||||
"compose_form.direct_message_warning_learn_more": "Vaata täpsemalt",
|
||||
"compose_form.direct_message_warning_learn_more": "Vaata lisa",
|
||||
"compose_form.encryption_warning": "Postitused Mastodonis ei ole otsast-otsani krüpteeritud. Ära jaga mingeid delikaatseid andmeid Mastodoni kaudu.",
|
||||
"compose_form.hashtag_warning": "See postitus ei ilmu ühegi märksõna all, kuna pole avalik. Vaid avalikud postitused on märksõnade kaudu leitavad.",
|
||||
"compose_form.lock_disclaimer": "Su konto ei ole {locked}. Igaüks saab sind jälgida, et näha su ainult-jälgijatele postitusi.",
|
||||
"compose_form.lock_disclaimer.lock": "lukus",
|
||||
"compose_form.placeholder": "Millest mõtled?",
|
||||
"compose_form.poll.duration": "Küsitluse kestus",
|
||||
"compose_form.poll.multiple": "Valikvastustega",
|
||||
"compose_form.poll.multiple": "Mitu vastust",
|
||||
"compose_form.poll.option_placeholder": "Valik {number}",
|
||||
"compose_form.poll.single": "Vali üks",
|
||||
"compose_form.poll.switch_to_multiple": "Muuda küsitlust mitmikvaliku lubamiseks",
|
||||
|
@ -297,6 +297,7 @@
|
|||
"filter_modal.select_filter.subtitle": "Kasuta olemasolevat kategooriat või loo uus",
|
||||
"filter_modal.select_filter.title": "Filtreeri seda postitust",
|
||||
"filter_modal.title.status": "Postituse filtreerimine",
|
||||
"filtered_notifications_banner.mentions": "{count, plural, one {mainimine} other {mainimist}}",
|
||||
"filtered_notifications_banner.pending_requests": "Teateid {count, plural, =0 {mitte üheltki} one {ühelt} other {#}} inimeselt, keda võid teada",
|
||||
"filtered_notifications_banner.title": "Filtreeritud teavitused",
|
||||
"firehose.all": "Kõik",
|
||||
|
@ -305,15 +306,19 @@
|
|||
"follow_request.authorize": "Autoriseeri",
|
||||
"follow_request.reject": "Hülga",
|
||||
"follow_requests.unlocked_explanation": "Kuigi su konto pole lukustatud, soovitab {domain} personal siiski nende kontode jälgimistaotlused käsitsi üle vaadata.",
|
||||
"follow_suggestions.curated_suggestion": "Teiste valitud",
|
||||
"follow_suggestions.curated_suggestion": "Meeskonna valitud",
|
||||
"follow_suggestions.dismiss": "Ära enam näita",
|
||||
"follow_suggestions.hints.featured": "Selle kasutajaprofiili on soovitanud {domain} kasutajad.",
|
||||
"follow_suggestions.hints.friends_of_friends": "See kasutajaprofiil on jälgitavate seas populaarne.",
|
||||
"follow_suggestions.featured_longer": "Käsitsi valitud {domain} meeskonna poolt",
|
||||
"follow_suggestions.friends_of_friends_longer": "Populaarne inimeste hulgas, keda jälgid",
|
||||
"follow_suggestions.hints.featured": "Selle kasutajaprofiili on soovitanud {domain} meeskond.",
|
||||
"follow_suggestions.hints.friends_of_friends": "See kasutajaprofiil on sinu jälgitavate seas populaarne.",
|
||||
"follow_suggestions.hints.most_followed": "See on {domain} enim jälgitud kasutajaprofiil.",
|
||||
"follow_suggestions.hints.most_interactions": "See on {domain} viimasel ajal enim tähelepanu saanud kasutajaprofiil.",
|
||||
"follow_suggestions.hints.most_interactions": "See kasutajaprofiil on viimasel ajal {domain} saanud palju tähelepanu.",
|
||||
"follow_suggestions.hints.similar_to_recently_followed": "See kasutajaprofiil sarnaneb neile, mida oled hiljuti jälgima asunud.",
|
||||
"follow_suggestions.personalized_suggestion": "Isikupärastatud soovitus",
|
||||
"follow_suggestions.popular_suggestion": "Popuplaarne soovitus",
|
||||
"follow_suggestions.popular_suggestion_longer": "Populaarne kohas {domain}",
|
||||
"follow_suggestions.similar_to_recently_followed_longer": "Sarnane profiilile, mida hiljuti jälgima hakkasid",
|
||||
"follow_suggestions.view_all": "Vaata kõiki",
|
||||
"follow_suggestions.who_to_follow": "Keda jälgida",
|
||||
"followed_tags": "Jälgitavad märksõnad",
|
||||
|
@ -409,6 +414,8 @@
|
|||
"limited_account_hint.action": "Näita profilli sellegipoolest",
|
||||
"limited_account_hint.title": "See profiil on peidetud {domain} moderaatorite poolt.",
|
||||
"link_preview.author": "{name} poolt",
|
||||
"link_preview.more_from_author": "Veel kasutajalt {name}",
|
||||
"link_preview.shares": "{count, plural, one {{counter} postitus} other {{counter} postitust}}",
|
||||
"lists.account.add": "Lisa nimekirja",
|
||||
"lists.account.remove": "Eemalda nimekirjast",
|
||||
"lists.delete": "Kustuta nimekiri",
|
||||
|
@ -468,13 +475,22 @@
|
|||
"notification.follow": "{name} alustas su jälgimist",
|
||||
"notification.follow_request": "{name} soovib sind jälgida",
|
||||
"notification.mention": "{name} mainis sind",
|
||||
"notification.moderation-warning.learn_more": "Vaata lisa",
|
||||
"notification.moderation_warning": "Said modereerimise hoiatuse",
|
||||
"notification.moderation_warning.action_delete_statuses": "Mõni su postitus on eemaldatud.",
|
||||
"notification.moderation_warning.action_disable": "Su konto on keelatud.",
|
||||
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Mõni su postitustest on märgitud kui tundlik.",
|
||||
"notification.moderation_warning.action_none": "Su konto on saanud modereerimise hoiatuse.",
|
||||
"notification.moderation_warning.action_sensitive": "Su postitused märgitakse nüüdsest tundlikuks.",
|
||||
"notification.moderation_warning.action_silence": "Su kontole pandi piirang.",
|
||||
"notification.moderation_warning.action_suspend": "Su konto on peatatud.",
|
||||
"notification.own_poll": "Su küsitlus on lõppenud",
|
||||
"notification.poll": "Küsitlus, milles osalesid, on lõppenud",
|
||||
"notification.reblog": "{name} jagas edasi postitust",
|
||||
"notification.relationships_severance_event": "Kadunud ühendus kasutajaga {name}",
|
||||
"notification.relationships_severance_event.account_suspension": "{from} admin on kustutanud {target}, mis tähendab, et sa ei saa enam neilt uuendusi või suhelda nendega.",
|
||||
"notification.relationships_severance_event.domain_block": "{from} admin on blokeerinud {target}, sealhulgas {followersCount} sinu jälgijat ja {followingCount, plural, one {# konto} other {# kontot}}, mida jälgid.",
|
||||
"notification.relationships_severance_event.learn_more": "Saa rohkem teada",
|
||||
"notification.relationships_severance_event.learn_more": "Vaata lisa",
|
||||
"notification.relationships_severance_event.user_domain_block": "Blokeerisid {target}, eemaldades oma jälgijate hulgast {followersCount} ja jälgitavate hulgast {followingCount, plural, one {# konto} other {# kontot}}.",
|
||||
"notification.status": "{name} just postitas",
|
||||
"notification.update": "{name} muutis postitust",
|
||||
|
@ -680,8 +696,11 @@
|
|||
"server_banner.about_active_users": "Inimesed, kes kasutavad seda serverit viimase 30 päeva jooksul (kuu aktiivsed kasutajad)",
|
||||
"server_banner.active_users": "aktiivsed kasutajad",
|
||||
"server_banner.administered_by": "Administraator:",
|
||||
"server_banner.is_one_of_many": "{domain} on üks paljudest sõltumatutest Mastodoni serveritest, mida saab fediversumis osalemiseks kasutada.",
|
||||
"server_banner.server_stats": "Serveri statistika:",
|
||||
"sign_in_banner.create_account": "Loo konto",
|
||||
"sign_in_banner.follow_anyone": "Jälgi ükskõik keda kogu fediversumist ja näe kõike ajalises järjestuses. Ei mingeid algoritme, reklaame või klikipüüdjaid segamas.",
|
||||
"sign_in_banner.mastodon_is": "Mastodon on parim viis olemaks kursis sellega, mis toimub.",
|
||||
"sign_in_banner.sign_in": "Logi sisse",
|
||||
"sign_in_banner.sso_redirect": "Sisene või registreeru",
|
||||
"status.admin_account": "Ava @{name} moderaatorivaates",
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
"admin.dashboard.retention.cohort_size": "नये उपयोगकर्ता",
|
||||
"admin.impact_report.instance_accounts": "ये अकाउंट प्रोफाइल मिटा देगा",
|
||||
"admin.impact_report.instance_followers": "हमारे यूजर्स इन फॉलोअर्स को खो देंगे",
|
||||
"admin.impact_report.instance_follows": "उनके उपयोगकर्ता इतने फ़ॉलोअर खो देंगे",
|
||||
"admin.impact_report.title": "प्रभावकां सारांश",
|
||||
"alert.rate_limited.message": "कृप्या {retry_time, time, medium} के बाद दुबारा कोशिश करें",
|
||||
"alert.rate_limited.title": "सीमित दर",
|
||||
|
@ -88,6 +89,7 @@
|
|||
"announcement.announcement": "घोषणा",
|
||||
"attachments_list.unprocessed": "(असंसाधित)",
|
||||
"audio.hide": "हाईड ऑडियो",
|
||||
"block_modal.remote_users_caveat": "हम {domain} को आपके निर्णय का सम्मान करने को कहेंगे।हालाकि इसकी आपूर्ति कि प्रत्याभूति नहीं हे। क्योंकि कुछ सर्वर ब्लॉक को अलग तरह से निभा सकते हे। अभी भी सार्वजानिक पोस्ट लोग- इन बगैर के उपयोगकर्ताओं को दिख सकती हैं।",
|
||||
"block_modal.show_less": "कम दिखाएं",
|
||||
"block_modal.show_more": "और दिखाएँ",
|
||||
"block_modal.they_cant_mention": "वे आपको मेंशन या फॉलो नहीं कर सकते",
|
||||
|
@ -205,7 +207,12 @@
|
|||
"dismissable_banner.dismiss": "डिसमिस",
|
||||
"dismissable_banner.explore_links": "इन समाचारों के बारे में लोगों द्वारा इस पर और डेसेंट्रलीसेड नेटवर्क के अन्य सर्वरों पर अभी बात की जा रही है।",
|
||||
"dismissable_banner.explore_tags": "ये हैशटैग अभी इस पर और डेसेंट्रलीसेड नेटवर्क के अन्य सर्वरों पर लोगों के बीच कर्षण प्राप्त कर रहे हैं।",
|
||||
"dismissable_banner.public_timeline": "यह ताजा सार्वजनिक पोस्ट है जिसका सामाजिक वेब {domain} के लोगो द्वारा अनुसरण हो रहा हैं।",
|
||||
"domain_block_modal.block": "सर्वर ब्लॉक करें",
|
||||
"domain_block_modal.block_account_instead": "इसकी जगह यह @{name} रखें",
|
||||
"domain_block_modal.they_can_interact_with_old_posts": "इस सर्वर की लोग आपकी पूरानी पोस्ट्स का अनुसरण किया जा sakta है।",
|
||||
"domain_block_modal.they_cant_follow": "इस सर्वर मेसे कोई भी आपका अनुसरण नहीं कर सकता।",
|
||||
"domain_block_modal.they_wont_know": "उनको पता नहीं चलेगा कि वे अवरोधित किए गए है।",
|
||||
"domain_block_modal.title": "डोमेन ब्लॉक करें",
|
||||
"domain_pill.server": "सर्वर",
|
||||
"domain_pill.username": "यूज़रनेम",
|
||||
|
|
|
@ -696,8 +696,10 @@
|
|||
"server_banner.about_active_users": "Personas que ha usate iste servitor in le ultime 30 dies (usatores active per mense)",
|
||||
"server_banner.active_users": "usatores active",
|
||||
"server_banner.administered_by": "Administrate per:",
|
||||
"server_banner.is_one_of_many": "{domain} es un de multe servitores independente de Mastodon que tu pote usar pro participar in le fediverso.",
|
||||
"server_banner.server_stats": "Statos del servitor:",
|
||||
"sign_in_banner.create_account": "Crear un conto",
|
||||
"sign_in_banner.mastodon_is": "Mastodon es le melior maniera de sequer lo que passa.",
|
||||
"sign_in_banner.sign_in": "Aperir session",
|
||||
"sign_in_banner.sso_redirect": "Aperir session o crear conto",
|
||||
"status.admin_account": "Aperir le interfacie de moderation pro @{name}",
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
"account.featured_tags.last_status_never": "게시물 없음",
|
||||
"account.featured_tags.title": "{name} 님의 추천 해시태그",
|
||||
"account.follow": "팔로우",
|
||||
"account.follow_back": "맞팔로우",
|
||||
"account.follow_back": "맞팔로우 하기",
|
||||
"account.followers": "팔로워",
|
||||
"account.followers.empty": "아직 아무도 이 사용자를 팔로우하고 있지 않습니다.",
|
||||
"account.followers_counter": "{counter} 팔로워",
|
||||
|
@ -53,7 +53,7 @@
|
|||
"account.mute_notifications_short": "알림 뮤트",
|
||||
"account.mute_short": "뮤트",
|
||||
"account.muted": "뮤트됨",
|
||||
"account.mutual": "상호 팔로우",
|
||||
"account.mutual": "맞팔로우 중",
|
||||
"account.no_bio": "제공된 설명이 없습니다.",
|
||||
"account.open_original_page": "원본 페이지 열기",
|
||||
"account.posts": "게시물",
|
||||
|
|
3
app/javascript/mastodon/models/notification_policy.ts
Normal file
3
app/javascript/mastodon/models/notification_policy.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import type { NotificationPolicyJSON } from 'mastodon/api_types/notification_policies';
|
||||
|
||||
export type NotificationPolicy = NotificationPolicyJSON; // No changes from the API type
|
|
@ -1,5 +1,7 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
COMPOSE_MOUNT,
|
||||
COMPOSE_UNMOUNT,
|
||||
|
@ -57,7 +59,6 @@ import {
|
|||
} from '../actions/compose';
|
||||
import { REDRAFT } from '../actions/statuses';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { enabledVisibilites, me } from '../initial_state';
|
||||
import { unescapeHTML } from '../utils/html';
|
||||
import { uuid } from '../uuid';
|
||||
|
@ -565,14 +566,14 @@ export default function compose(state = initialState, action) {
|
|||
return updateSuggestionTags(state, action.token);
|
||||
case COMPOSE_TAG_HISTORY_UPDATE:
|
||||
return state.set('tagHistory', fromJS(action.tags));
|
||||
case TIMELINE_DELETE:
|
||||
if (action.id === state.get('in_reply_to')) {
|
||||
case timelineDelete.type:
|
||||
if (action.payload.statusId === state.get('in_reply_to')) {
|
||||
if (state.get('privacy') === 'reply') {
|
||||
return state.set('in_reply_to', null).set('privacy', 'circle');
|
||||
} else {
|
||||
return state.set('in_reply_to', null);
|
||||
}
|
||||
} else if (action.id === state.get('id')) {
|
||||
} else if (action.payload.statusId === state.get('id')) {
|
||||
return state.set('id', null);
|
||||
} else {
|
||||
return state;
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
blockAccountSuccess,
|
||||
muteAccountSuccess,
|
||||
} from '../actions/accounts';
|
||||
import { CONTEXT_FETCH_SUCCESS } from '../actions/statuses';
|
||||
import { TIMELINE_DELETE, TIMELINE_UPDATE } from '../actions/timelines';
|
||||
import { TIMELINE_UPDATE } from '../actions/timelines';
|
||||
import { compareId } from '../compare_id';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -102,8 +104,8 @@ export default function replies(state = initialState, action) {
|
|||
return filterContexts(state, action.payload.relationship, action.payload.statuses);
|
||||
case CONTEXT_FETCH_SUCCESS:
|
||||
return normalizeContext(state, action.id, action.ancestors, action.descendants, action.references);
|
||||
case TIMELINE_DELETE:
|
||||
return deleteFromContexts(state, [action.id]);
|
||||
case timelineDelete.type:
|
||||
return deleteFromContexts(state, [action.payload.statusId]);
|
||||
case TIMELINE_UPDATE:
|
||||
return updateContext(state, action.status);
|
||||
default:
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import type { Reducer } from '@reduxjs/toolkit';
|
||||
import { Record as ImmutableRecord, Stack } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import { COMPOSE_UPLOAD_CHANGE_SUCCESS } from '../actions/compose';
|
||||
import type { ModalType } from '../actions/modal';
|
||||
import { openModal, closeModal } from '../actions/modal';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
|
||||
export type ModalProps = Record<string, unknown>;
|
||||
interface Modal {
|
||||
|
@ -72,10 +73,10 @@ export const modalReducer: Reducer<State> = (state = initialState, action) => {
|
|||
// TODO: type those actions
|
||||
else if (action.type === COMPOSE_UPLOAD_CHANGE_SUCCESS)
|
||||
return popModal(state, { modalType: 'FOCAL_POINT', ignoreFocus: false });
|
||||
else if (action.type === TIMELINE_DELETE)
|
||||
else if (timelineDelete.match(action))
|
||||
return state.update('stack', (stack) =>
|
||||
stack.filterNot(
|
||||
(modal) => modal.get('modalProps').statusId === action.id,
|
||||
(modal) => modal.get('modalProps').statusId === action.payload.statusId,
|
||||
),
|
||||
);
|
||||
else return state;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { fromJS } from 'immutable';
|
||||
|
||||
import { NOTIFICATION_POLICY_FETCH_SUCCESS } from 'mastodon/actions/notifications';
|
||||
|
||||
export const notificationPolicyReducer = (state = null, action) => {
|
||||
switch(action.type) {
|
||||
case NOTIFICATION_POLICY_FETCH_SUCCESS:
|
||||
return fromJS(action.policy);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
18
app/javascript/mastodon/reducers/notification_policy.ts
Normal file
18
app/javascript/mastodon/reducers/notification_policy.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { createReducer, isAnyOf } from '@reduxjs/toolkit';
|
||||
|
||||
import {
|
||||
fetchNotificationPolicy,
|
||||
updateNotificationsPolicy,
|
||||
} from 'mastodon/actions/notification_policies';
|
||||
import type { NotificationPolicy } from 'mastodon/models/notification_policy';
|
||||
|
||||
export const notificationPolicyReducer =
|
||||
createReducer<NotificationPolicy | null>(null, (builder) => {
|
||||
builder.addMatcher(
|
||||
isAnyOf(
|
||||
fetchNotificationPolicy.fulfilled,
|
||||
updateNotificationsPolicy.fulfilled,
|
||||
),
|
||||
(_state, action) => action.payload,
|
||||
);
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import { fromJS, Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { blockDomainSuccess } from 'mastodon/actions/domain_blocks';
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
authorizeFollowRequestSuccess,
|
||||
|
@ -30,7 +31,7 @@ import {
|
|||
NOTIFICATIONS_SET_BROWSER_SUPPORT,
|
||||
NOTIFICATIONS_SET_BROWSER_PERMISSION,
|
||||
} from '../actions/notifications';
|
||||
import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines';
|
||||
import { disconnectTimeline } from '../actions/timelines';
|
||||
import { compareId } from '../compare_id';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -293,11 +294,11 @@ export default function notifications(state = initialState, action) {
|
|||
return filterNotifications(state, [action.payload.id], 'follow_request');
|
||||
case NOTIFICATIONS_CLEAR:
|
||||
return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
|
||||
case TIMELINE_DELETE:
|
||||
return deleteByStatus(state, action.id);
|
||||
case TIMELINE_DISCONNECT:
|
||||
return action.timeline === 'home' ?
|
||||
state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
|
||||
case timelineDelete.type:
|
||||
return deleteByStatus(state, action.payload.statusId);
|
||||
case disconnectTimeline.type:
|
||||
return action.payload.timeline === 'home' ?
|
||||
state.update(action.payload.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
|
||||
state;
|
||||
case NOTIFICATIONS_MARK_AS_READ:
|
||||
const lastNotification = state.get('items').find(item => item !== null);
|
||||
|
|
|
@ -4,8 +4,7 @@ import {
|
|||
deployPictureInPictureAction,
|
||||
removePictureInPicture,
|
||||
} from 'mastodon/actions/picture_in_picture';
|
||||
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
export interface PIPMediaProps {
|
||||
src: string;
|
||||
|
@ -49,8 +48,9 @@ export const pictureInPictureReducer: Reducer<PIPState> = (
|
|||
...action.payload.props,
|
||||
};
|
||||
else if (removePictureInPicture.match(action)) return initialState;
|
||||
else if (action.type === TIMELINE_DELETE)
|
||||
if (state.type && state.statusId === action.id) return initialState;
|
||||
else if (timelineDelete.match(action))
|
||||
if (state.type && state.statusId === action.payload.statusId)
|
||||
return initialState;
|
||||
|
||||
return state;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
|
||||
import {
|
||||
|
@ -34,7 +35,6 @@ import {
|
|||
STATUS_FETCH_FAIL,
|
||||
STATUS_EMOJI_REACTION_UPDATE,
|
||||
} from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
|
@ -152,8 +152,8 @@ export default function statuses(state = initialState, action) {
|
|||
});
|
||||
case STATUS_COLLAPSE:
|
||||
return state.setIn([action.id, 'collapsed'], action.isCollapsed);
|
||||
case TIMELINE_DELETE:
|
||||
return deleteStatus(state, action.id, action.references);
|
||||
case timelineDelete.type:
|
||||
return deleteStatus(state, action.payload.statusId, action.payload.references);
|
||||
case STATUS_TRANSLATE_SUCCESS:
|
||||
return statusTranslateSuccess(state, action.id, action.translation);
|
||||
case STATUS_TRANSLATE_UNDO:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
blockAccountSuccess,
|
||||
muteAccountSuccess,
|
||||
|
@ -7,19 +9,18 @@ import {
|
|||
} from '../actions/accounts';
|
||||
import {
|
||||
TIMELINE_UPDATE,
|
||||
TIMELINE_DELETE,
|
||||
TIMELINE_CLEAR,
|
||||
TIMELINE_EXPAND_SUCCESS,
|
||||
TIMELINE_EXPAND_REQUEST,
|
||||
TIMELINE_EXPAND_FAIL,
|
||||
TIMELINE_SCROLL_TOP,
|
||||
TIMELINE_CONNECT,
|
||||
TIMELINE_DISCONNECT,
|
||||
TIMELINE_LOAD_PENDING,
|
||||
TIMELINE_MARK_AS_PARTIAL,
|
||||
TIMELINE_INSERT,
|
||||
TIMELINE_GAP,
|
||||
TIMELINE_SUGGESTIONS,
|
||||
disconnectTimeline,
|
||||
} from '../actions/timelines';
|
||||
import { compareId } from '../compare_id';
|
||||
|
||||
|
@ -158,7 +159,7 @@ const filterTimelines = (state, relationship, statuses) => {
|
|||
return;
|
||||
}
|
||||
|
||||
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id'));
|
||||
references = statuses.filter(item => item.get('reblog') === status.get('id')).map(item => item.get('id')).valueSeq().toJSON();
|
||||
state = deleteStatus(state, status.get('id'), references, relationship.id);
|
||||
});
|
||||
|
||||
|
@ -201,8 +202,8 @@ export default function timelines(state = initialState, action) {
|
|||
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent, action.usePendingItems);
|
||||
case TIMELINE_UPDATE:
|
||||
return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems);
|
||||
case TIMELINE_DELETE:
|
||||
return deleteStatus(state, action.id, action.references, action.reblogOf);
|
||||
case timelineDelete.type:
|
||||
return deleteStatus(state, action.payload.statusId, action.payload.references, action.payload.reblogOf);
|
||||
case TIMELINE_CLEAR:
|
||||
return clearTimeline(state, action.timeline);
|
||||
case blockAccountSuccess.type:
|
||||
|
@ -214,11 +215,11 @@ export default function timelines(state = initialState, action) {
|
|||
return updateTop(state, action.timeline, action.top);
|
||||
case TIMELINE_CONNECT:
|
||||
return state.update(action.timeline, initialTimeline, map => reconnectTimeline(map, action.usePendingItems));
|
||||
case TIMELINE_DISCONNECT:
|
||||
case disconnectTimeline.type:
|
||||
return state.update(
|
||||
action.timeline,
|
||||
action.payload.timeline,
|
||||
initialTimeline,
|
||||
map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(TIMELINE_GAP) : items),
|
||||
map => map.set('online', false).update(action.payload.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(TIMELINE_GAP) : items),
|
||||
);
|
||||
case TIMELINE_MARK_AS_PARTIAL:
|
||||
return state.update(
|
||||
|
|
|
@ -82,13 +82,19 @@ export function createThunk<Arg = void, Returned = void>(
|
|||
const discardLoadDataInPayload = Symbol('discardLoadDataInPayload');
|
||||
type DiscardLoadData = typeof discardLoadDataInPayload;
|
||||
|
||||
type OnData<LoadDataResult, ReturnedData> = (
|
||||
type OnData<ActionArg, LoadDataResult, ReturnedData> = (
|
||||
data: LoadDataResult,
|
||||
api: AppThunkApi & {
|
||||
actionArg: ActionArg;
|
||||
discardLoadData: DiscardLoadData;
|
||||
},
|
||||
) => ReturnedData | DiscardLoadData | Promise<ReturnedData | DiscardLoadData>;
|
||||
|
||||
type LoadData<Args, LoadDataResult> = (
|
||||
args: Args,
|
||||
api: AppThunkApi,
|
||||
) => Promise<LoadDataResult>;
|
||||
|
||||
type ArgsType = Record<string, unknown> | undefined;
|
||||
|
||||
// Overload when there is no `onData` method, the payload is the `onData` result
|
||||
|
@ -101,18 +107,18 @@ export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
|||
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
|
||||
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
||||
name: string,
|
||||
loadData: (args: Args) => Promise<LoadDataResult>,
|
||||
loadData: LoadData<Args, LoadDataResult>,
|
||||
onDataOrThunkOptions?:
|
||||
| AppThunkOptions
|
||||
| OnData<LoadDataResult, DiscardLoadData>,
|
||||
| OnData<Args, LoadDataResult, DiscardLoadData>,
|
||||
thunkOptions?: AppThunkOptions,
|
||||
): ReturnType<typeof createThunk<Args, void>>;
|
||||
|
||||
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
|
||||
export function createDataLoadingThunk<LoadDataResult, Args extends ArgsType>(
|
||||
name: string,
|
||||
loadData: (args: Args) => Promise<LoadDataResult>,
|
||||
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>,
|
||||
loadData: LoadData<Args, LoadDataResult>,
|
||||
onDataOrThunkOptions?: AppThunkOptions | OnData<Args, LoadDataResult, void>,
|
||||
thunkOptions?: AppThunkOptions,
|
||||
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
|
||||
|
||||
|
@ -123,8 +129,10 @@ export function createDataLoadingThunk<
|
|||
Returned,
|
||||
>(
|
||||
name: string,
|
||||
loadData: (args: Args) => Promise<LoadDataResult>,
|
||||
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
|
||||
loadData: LoadData<Args, LoadDataResult>,
|
||||
onDataOrThunkOptions?:
|
||||
| AppThunkOptions
|
||||
| OnData<Args, LoadDataResult, Returned>,
|
||||
thunkOptions?: AppThunkOptions,
|
||||
): ReturnType<typeof createThunk<Args, Returned>>;
|
||||
|
||||
|
@ -159,11 +167,13 @@ export function createDataLoadingThunk<
|
|||
Returned,
|
||||
>(
|
||||
name: string,
|
||||
loadData: (args: Args) => Promise<LoadDataResult>,
|
||||
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
|
||||
loadData: LoadData<Args, LoadDataResult>,
|
||||
onDataOrThunkOptions?:
|
||||
| AppThunkOptions
|
||||
| OnData<Args, LoadDataResult, Returned>,
|
||||
maybeThunkOptions?: AppThunkOptions,
|
||||
) {
|
||||
let onData: OnData<LoadDataResult, Returned> | undefined;
|
||||
let onData: OnData<Args, LoadDataResult, Returned> | undefined;
|
||||
let thunkOptions: AppThunkOptions | undefined;
|
||||
|
||||
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
|
||||
|
@ -177,7 +187,10 @@ export function createDataLoadingThunk<
|
|||
return createThunk<Args, Returned>(
|
||||
name,
|
||||
async (arg, { getState, dispatch }) => {
|
||||
const data = await loadData(arg);
|
||||
const data = await loadData(arg, {
|
||||
dispatch,
|
||||
getState,
|
||||
});
|
||||
|
||||
if (!onData) return data as Returned;
|
||||
|
||||
|
@ -185,6 +198,7 @@ export function createDataLoadingThunk<
|
|||
dispatch,
|
||||
getState,
|
||||
discardLoadData: discardLoadDataInPayload,
|
||||
actionArg: arg,
|
||||
});
|
||||
|
||||
// if there is no return in `onData`, we return the `onData` result
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue