diff --git a/app/javascript/flavours/glitch/actions/streaming.js b/app/javascript/flavours/glitch/actions/streaming.js index d8341a5c16..a55240646f 100644 --- a/app/javascript/flavours/glitch/actions/streaming.js +++ b/app/javascript/flavours/glitch/actions/streaming.js @@ -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 diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js index 1fa2c3cde4..c10f1790e5 100644 --- a/app/javascript/flavours/glitch/actions/timelines.js +++ b/app/javascript/flavours/glitch/actions/timelines.js @@ -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')).toJSON(); const reblogOf = getState().getIn(['statuses', id, 'reblog'], null); - dispatch({ - type: TIMELINE_DELETE, - id, - accountId, - references, - reblogOf, - }); + dispatch(timelineDelete(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, diff --git a/app/javascript/flavours/glitch/actions/timelines_typed.ts b/app/javascript/flavours/glitch/actions/timelines_typed.ts new file mode 100644 index 0000000000..485b94ed52 --- /dev/null +++ b/app/javascript/flavours/glitch/actions/timelines_typed.ts @@ -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'); diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js index 36430e8238..211402ab2a 100644 --- a/app/javascript/flavours/glitch/reducers/compose.js +++ b/app/javascript/flavours/glitch/reducers/compose.js @@ -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; diff --git a/app/javascript/flavours/glitch/reducers/contexts.js b/app/javascript/flavours/glitch/reducers/contexts.js index f7d7419a4e..07fe8ffb8a 100644 --- a/app/javascript/flavours/glitch/reducers/contexts.js +++ b/app/javascript/flavours/glitch/reducers/contexts.js @@ -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: diff --git a/app/javascript/flavours/glitch/reducers/modal.ts b/app/javascript/flavours/glitch/reducers/modal.ts index 368f26542c..c9ecdb7fdf 100644 --- a/app/javascript/flavours/glitch/reducers/modal.ts +++ b/app/javascript/flavours/glitch/reducers/modal.ts @@ -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; interface Modal { @@ -72,10 +73,10 @@ export const modalReducer: Reducer = (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; diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js index c5f36dd94a..226f2affe1 100644 --- a/app/javascript/flavours/glitch/reducers/notifications.js +++ b/app/javascript/flavours/glitch/reducers/notifications.js @@ -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); diff --git a/app/javascript/flavours/glitch/reducers/picture_in_picture.ts b/app/javascript/flavours/glitch/reducers/picture_in_picture.ts index 35eb10cf57..6ad31d42bf 100644 --- a/app/javascript/flavours/glitch/reducers/picture_in_picture.ts +++ b/app/javascript/flavours/glitch/reducers/picture_in_picture.ts @@ -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 = ( ...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; }; diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js index ca766f73a3..39df0e956d 100644 --- a/app/javascript/flavours/glitch/reducers/statuses.js +++ b/app/javascript/flavours/glitch/reducers/statuses.js @@ -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: diff --git a/app/javascript/flavours/glitch/reducers/timelines.js b/app/javascript/flavours/glitch/reducers/timelines.js index 08c7a48336..f1602e8537 100644 --- a/app/javascript/flavours/glitch/reducers/timelines.js +++ b/app/javascript/flavours/glitch/reducers/timelines.js @@ -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'; @@ -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(