Merge pull request #2751 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes up to bb2d77b4a0
This commit is contained in:
commit
1090611529
@ -1,6 +1,6 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.63.5.
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.64.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
@ -55,7 +55,6 @@ Style/FetchEnvVar:
|
||||
- 'config/initializers/vapid.rb'
|
||||
- 'lib/mastodon/redis_config.rb'
|
||||
- 'lib/tasks/repo.rake'
|
||||
- 'spec/system/profile_spec.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
|
||||
@ -66,40 +65,10 @@ Style/FormatStringToken:
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'lib/paperclip/color_extractor.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/GlobalStdStream:
|
||||
Exclude:
|
||||
- 'config/environments/development.rb'
|
||||
- 'config/environments/production.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
||||
Style/GuardClause:
|
||||
Exclude:
|
||||
- 'app/lib/activitypub/activity/block.rb'
|
||||
- 'app/lib/request.rb'
|
||||
- 'app/lib/request_pool.rb'
|
||||
- 'app/lib/webfinger.rb'
|
||||
- 'app/lib/webfinger_resource.rb'
|
||||
- 'app/models/concerns/account/counters.rb'
|
||||
- 'app/models/concerns/user/ldap_authenticable.rb'
|
||||
- 'app/models/tag.rb'
|
||||
- 'app/models/user.rb'
|
||||
- 'app/services/fan_out_on_write_service.rb'
|
||||
- 'app/services/post_status_service.rb'
|
||||
- 'app/services/process_hashtags_service.rb'
|
||||
- 'app/workers/move_worker.rb'
|
||||
- 'app/workers/redownload_avatar_worker.rb'
|
||||
- 'app/workers/redownload_header_worker.rb'
|
||||
- 'app/workers/redownload_media_worker.rb'
|
||||
- 'app/workers/remote_account_refresh_worker.rb'
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'lib/devise/strategies/two_factor_ldap_authenticatable.rb'
|
||||
- 'lib/devise/strategies/two_factor_pam_authenticatable.rb'
|
||||
- 'lib/mastodon/cli/accounts.rb'
|
||||
- 'lib/mastodon/cli/maintenance.rb'
|
||||
- 'lib/mastodon/cli/media.rb'
|
||||
- 'lib/tasks/repo.rake'
|
||||
Enabled: false
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Style/HashTransformValues:
|
||||
@ -121,16 +90,10 @@ Style/MutableConstant:
|
||||
- 'app/services/delete_account_service.rb'
|
||||
- 'lib/mastodon/migration_warning.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
Style/NilLambda:
|
||||
Exclude:
|
||||
- 'config/initializers/paperclip.rb'
|
||||
|
||||
# Configuration parameters: AllowedMethods.
|
||||
# AllowedMethods: respond_to_missing?
|
||||
Style/OptionalBooleanParameter:
|
||||
Exclude:
|
||||
- 'app/helpers/admin/account_moderation_notes_helper.rb'
|
||||
- 'app/helpers/jsonld_helper.rb'
|
||||
- 'app/lib/admin/system_check/message.rb'
|
||||
- 'app/lib/request.rb'
|
||||
@ -154,13 +117,6 @@ Style/RedundantConstantBase:
|
||||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/sidekiq.rb'
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
|
||||
# AllowedMethods: present?, blank?, presence, try, try!
|
||||
Style/SafeNavigation:
|
||||
Exclude:
|
||||
- 'app/models/concerns/account/finder_concern.rb'
|
||||
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: WordRegex.
|
||||
# SupportedStyles: percent, brackets
|
||||
|
@ -15,7 +15,7 @@ class Api::V2Alpha::NotificationsController < Api::BaseController
|
||||
@relationships = StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
|
||||
end
|
||||
|
||||
render json: @notifications.map { |notification| NotificationGroup.from_notification(notification) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
|
||||
render json: @notifications.map { |notification| NotificationGroup.from_notification(notification, max_id: @group_metadata.dig(notification.group_key, :max_id)) }, each_serializer: REST::NotificationGroupSerializer, relationships: @relationships, group_metadata: @group_metadata
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -77,7 +77,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
|
||||
},
|
||||
|
||||
onDisconnect() {
|
||||
dispatch(disconnectTimeline(timelineId));
|
||||
dispatch(disconnectTimeline({ timeline: timelineId }));
|
||||
|
||||
if (options.fallback) {
|
||||
// @ts-expect-error
|
||||
|
@ -7,9 +7,11 @@ import { toServerSideType } from 'flavours/glitch/utils/filters';
|
||||
|
||||
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';
|
||||
@ -18,7 +20,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';
|
||||
@ -73,16 +74,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 }));
|
||||
};
|
||||
}
|
||||
|
||||
@ -237,12 +232,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/flavours/glitch/actions/timelines_typed.ts
Normal file
20
app/javascript/flavours/glitch/actions/timelines_typed.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { usePendingItems as preferPendingItems } from 'flavours/glitch/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');
|
@ -1,5 +1,7 @@
|
||||
import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'flavours/glitch/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
COMPOSE_MOUNT,
|
||||
COMPOSE_UNMOUNT,
|
||||
@ -54,7 +56,6 @@ import {
|
||||
} from '../actions/compose';
|
||||
import { REDRAFT } from '../actions/statuses';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { me, defaultContentType } from '../initial_state';
|
||||
import { recoverHashtags } from '../utils/hashtag';
|
||||
import { unescapeHTML } from '../utils/html';
|
||||
@ -551,10 +552,10 @@ 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')) {
|
||||
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 'flavours/glitch/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({
|
||||
@ -97,8 +99,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);
|
||||
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 'flavours/glitch/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,6 +1,7 @@
|
||||
import { fromJS, Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
import { blockDomainSuccess } from 'flavours/glitch/actions/domain_blocks';
|
||||
import { timelineDelete } from 'flavours/glitch/actions/timelines_typed';
|
||||
|
||||
import {
|
||||
authorizeFollowRequestSuccess,
|
||||
@ -33,7 +34,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({
|
||||
@ -333,11 +334,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_SET_BROWSER_SUPPORT:
|
||||
return state.set('browserSupport', action.value);
|
||||
|
@ -4,8 +4,7 @@ import {
|
||||
deployPictureInPictureAction,
|
||||
removePictureInPicture,
|
||||
} from 'flavours/glitch/actions/picture_in_picture';
|
||||
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { timelineDelete } from 'flavours/glitch/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,7 @@
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'flavours/glitch/actions/timelines_typed';
|
||||
|
||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||
import { normalizeStatusTranslation } from '../actions/importer/normalizer';
|
||||
import {
|
||||
@ -27,7 +29,6 @@ import {
|
||||
STATUS_FETCH_REQUEST,
|
||||
STATUS_FETCH_FAIL,
|
||||
} from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
@ -114,8 +115,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 'flavours/glitch/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';
|
||||
|
||||
@ -164,7 +165,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);
|
||||
});
|
||||
|
||||
@ -207,8 +208,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, action.filtered);
|
||||
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:
|
||||
@ -220,11 +221,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(
|
||||
|
@ -77,7 +77,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 }));
|
||||
};
|
||||
}
|
||||
|
||||
@ -225,12 +220,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');
|
@ -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}` }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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": "यूज़रनेम",
|
||||
|
@ -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,
|
||||
@ -51,7 +53,6 @@ import {
|
||||
} from '../actions/compose';
|
||||
import { REDRAFT } from '../actions/statuses';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
import { me } from '../initial_state';
|
||||
import { unescapeHTML } from '../utils/html';
|
||||
import { uuid } from '../uuid';
|
||||
@ -446,10 +447,10 @@ 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')) {
|
||||
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({
|
||||
@ -97,8 +99,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);
|
||||
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,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({
|
||||
@ -291,11 +292,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,7 @@
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import { timelineDelete } from 'mastodon/actions/timelines_typed';
|
||||
|
||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||
import { normalizeStatusTranslation } from '../actions/importer/normalizer';
|
||||
import {
|
||||
@ -27,7 +29,6 @@ import {
|
||||
STATUS_FETCH_REQUEST,
|
||||
STATUS_FETCH_FAIL,
|
||||
} from '../actions/statuses';
|
||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||
|
||||
const importStatus = (state, status) => state.set(status.id, fromJS(status));
|
||||
|
||||
@ -114,8 +115,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(
|
||||
|
@ -3,13 +3,16 @@
|
||||
class NotificationGroup < ActiveModelSerializers::Model
|
||||
attributes :group_key, :sample_accounts, :notifications_count, :notification, :most_recent_notification_id
|
||||
|
||||
def self.from_notification(notification)
|
||||
def self.from_notification(notification, max_id: nil)
|
||||
if notification.group_key.present?
|
||||
# TODO: caching and preloading
|
||||
most_recent_notifications = notification.account.notifications.where(group_key: notification.group_key).order(id: :desc).take(3)
|
||||
scope = notification.account.notifications.where(group_key: notification.group_key)
|
||||
scope = scope.where(id: ..max_id) if max_id.present?
|
||||
|
||||
most_recent_notifications = scope.order(id: :desc).take(3)
|
||||
most_recent_id = most_recent_notifications.first.id
|
||||
sample_accounts = most_recent_notifications.map(&:from_account)
|
||||
notifications_count = notification.account.notifications.where(group_key: notification.group_key).count
|
||||
notifications_count = scope.count
|
||||
else
|
||||
most_recent_id = notification.id
|
||||
sample_accounts = [notification.from_account]
|
||||
@ -29,5 +32,6 @@ class NotificationGroup < ActiveModelSerializers::Model
|
||||
:target_status,
|
||||
:report,
|
||||
:account_relationship_severance_event,
|
||||
:account_warning,
|
||||
to: :notification, prefix: false
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user