diff --git a/src/actions/push-notifications/setter.ts b/src/actions/push-notifications/setter.ts index 739427e7e..fd81914b0 100644 --- a/src/actions/push-notifications/setter.ts +++ b/src/actions/push-notifications/setter.ts @@ -1,9 +1,7 @@ -import type { AnyAction } from 'redux'; - -const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT'; -const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION'; -const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION'; -const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS'; +const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT' as const; +const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION' as const; +const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION' as const; +const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS' as const; const setBrowserSupport = (value: boolean) => ({ type: SET_BROWSER_SUPPORT, @@ -19,13 +17,17 @@ const clearSubscription = () => ({ type: CLEAR_SUBSCRIPTION, }); -const setAlerts = (path: Array, value: any) => - (dispatch: React.Dispatch) => - dispatch({ - type: SET_ALERTS, - path, - value, - }); +const setAlerts = (path: Array, value: any) => ({ + type: SET_ALERTS, + path, + value, +}); + +type SetterAction = + | ReturnType + | ReturnType + | ReturnType + | ReturnType; export { SET_BROWSER_SUPPORT, @@ -36,4 +38,5 @@ export { setSubscription, clearSubscription, setAlerts, + type SetterAction, }; diff --git a/src/actions/soapbox.ts b/src/actions/soapbox.ts index dca508505..37862c324 100644 --- a/src/actions/soapbox.ts +++ b/src/actions/soapbox.ts @@ -12,32 +12,22 @@ import type { APIEntity } from 'soapbox/types/entities'; const SOAPBOX_CONFIG_REQUEST_SUCCESS = 'SOAPBOX_CONFIG_REQUEST_SUCCESS' as const; const SOAPBOX_CONFIG_REQUEST_FAIL = 'SOAPBOX_CONFIG_REQUEST_FAIL' as const; -const SOAPBOX_CONFIG_REMEMBER_REQUEST = 'SOAPBOX_CONFIG_REMEMBER_REQUEST' as const; const SOAPBOX_CONFIG_REMEMBER_SUCCESS = 'SOAPBOX_CONFIG_REMEMBER_SUCCESS' as const; -const SOAPBOX_CONFIG_REMEMBER_FAIL = 'SOAPBOX_CONFIG_REMEMBER_FAIL' as const; const getSoapboxConfig = createSelector([ (state: RootState) => state.soapbox, (state: RootState) => state.auth.client.features, ], (soapbox, features) => { // Do some additional normalization with the state - return normalizeSoapboxConfig(soapbox).withMutations(soapboxConfig => { - // If displayFqn isn't set, infer it from federation - if (soapbox.get('displayFqn') === undefined) { - soapboxConfig.set('displayFqn', features.federating); - } - }); + return normalizeSoapboxConfig(soapbox); }); const rememberSoapboxConfig = (host: string | null) => (dispatch: AppDispatch) => { - dispatch({ type: SOAPBOX_CONFIG_REMEMBER_REQUEST, host }); return KVStore.getItemOrError(`soapbox_config:${host}`).then(soapboxConfig => { dispatch({ type: SOAPBOX_CONFIG_REMEMBER_SUCCESS, host, soapboxConfig }); return soapboxConfig; - }).catch(error => { - dispatch({ type: SOAPBOX_CONFIG_REMEMBER_FAIL, host, error, skipAlert: true }); - }); + }).catch(() => {}); }; const fetchFrontendConfigurations = () => @@ -107,9 +97,7 @@ const isObject = (o: any) => o instanceof Object && o.constructor === Object; export { SOAPBOX_CONFIG_REQUEST_SUCCESS, SOAPBOX_CONFIG_REQUEST_FAIL, - SOAPBOX_CONFIG_REMEMBER_REQUEST, SOAPBOX_CONFIG_REMEMBER_SUCCESS, - SOAPBOX_CONFIG_REMEMBER_FAIL, getSoapboxConfig, rememberSoapboxConfig, fetchFrontendConfigurations, diff --git a/src/actions/status-quotes.ts b/src/actions/status-quotes.ts index 9d35ee5e0..d331e4fc9 100644 --- a/src/actions/status-quotes.ts +++ b/src/actions/status-quotes.ts @@ -2,6 +2,7 @@ import { getClient } from '../api'; import { importFetchedStatuses } from './importer'; +import type { Status as BaseStatus, PaginatedResponse } from 'pl-api'; import type { AppDispatch, RootState } from 'soapbox/store'; const STATUS_QUOTES_FETCH_REQUEST = 'STATUS_QUOTES_FETCH_REQUEST' as const; @@ -14,34 +15,70 @@ const STATUS_QUOTES_EXPAND_FAIL = 'STATUS_QUOTES_EXPAND_FAIL' as const; const noOp = () => new Promise(f => f(null)); +interface FetchStatusQuotesRequestAction { + type: typeof STATUS_QUOTES_FETCH_REQUEST; + statusId: string; +} + +interface FetchStatusQuotesSuccessAction { + type: typeof STATUS_QUOTES_FETCH_SUCCESS; + statusId: string; + statuses: Array; + next: (() => Promise>) | null; +} + +interface FetchStatusQuotesFailAction { + type: typeof STATUS_QUOTES_FETCH_FAIL; + statusId: string; + error: unknown; +} + const fetchStatusQuotes = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => { if (getState().status_lists.getIn([`quotes:${statusId}`, 'isLoading'])) { return dispatch(noOp); } - dispatch({ - statusId, - type: STATUS_QUOTES_FETCH_REQUEST, - }); + const action: FetchStatusQuotesRequestAction = { type: STATUS_QUOTES_FETCH_REQUEST, statusId }; + dispatch(action); return getClient(getState).statuses.getStatusQuotes(statusId).then(response => { dispatch(importFetchedStatuses(response.items)); - return dispatch({ + const action: FetchStatusQuotesSuccessAction = { type: STATUS_QUOTES_FETCH_SUCCESS, statusId, statuses: response.items, next: response.next, - }); + }; + return dispatch(action); }).catch(error => { - dispatch({ + const action: FetchStatusQuotesFailAction = { type: STATUS_QUOTES_FETCH_FAIL, statusId, error, - }); + }; + dispatch(action); }); }; +interface ExpandStatusQuotesRequestAction { + type: typeof STATUS_QUOTES_EXPAND_REQUEST; + statusId: string; +} + +interface ExpandStatusQuotesSuccessAction { + type: typeof STATUS_QUOTES_EXPAND_SUCCESS; + statusId: string; + statuses: Array; + next: (() => Promise>) | null; +} + +interface ExpandStatusQuotesFailAction { + type: typeof STATUS_QUOTES_EXPAND_FAIL; + statusId: string; + error: unknown; +} + const expandStatusQuotes = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => { const next = getState().status_lists.get(`quotes:${statusId}`)?.next || null; @@ -50,28 +87,39 @@ const expandStatusQuotes = (statusId: string) => return dispatch(noOp); } - dispatch({ + const action: ExpandStatusQuotesRequestAction = { type: STATUS_QUOTES_EXPAND_REQUEST, statusId, - }); + }; + dispatch(action); return next().then(response => { dispatch(importFetchedStatuses(response.items)); - dispatch({ + const action: ExpandStatusQuotesSuccessAction = { type: STATUS_QUOTES_EXPAND_SUCCESS, statusId, statuses: response.items, next: response.next, - }); + }; + dispatch(action); }).catch(error => { - dispatch({ + const action: ExpandStatusQuotesFailAction = { type: STATUS_QUOTES_EXPAND_FAIL, statusId, error, - }); + }; + dispatch(action); }); }; +type StatusQuotesAction = + | FetchStatusQuotesRequestAction + | FetchStatusQuotesSuccessAction + | FetchStatusQuotesFailAction + | ExpandStatusQuotesRequestAction + | ExpandStatusQuotesSuccessAction + | ExpandStatusQuotesFailAction; + export { STATUS_QUOTES_FETCH_REQUEST, STATUS_QUOTES_FETCH_SUCCESS, @@ -81,4 +129,5 @@ export { STATUS_QUOTES_EXPAND_FAIL, fetchStatusQuotes, expandStatusQuotes, + type StatusQuotesAction, }; diff --git a/src/actions/statuses.ts b/src/actions/statuses.ts index 7a46afc9d..2be508011 100644 --- a/src/actions/statuses.ts +++ b/src/actions/statuses.ts @@ -332,6 +332,11 @@ const changeStatusLanguage = (statusId: string, language: string) => ({ language, }); +type StatusesAction = + | ReturnType + | ReturnType + | ReturnType; + export { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, @@ -379,4 +384,5 @@ export { undoStatusTranslation, unfilterStatus, changeStatusLanguage, + type StatusesAction, }; diff --git a/src/actions/timelines.ts b/src/actions/timelines.ts index c58791b2f..ece5183f8 100644 --- a/src/actions/timelines.ts +++ b/src/actions/timelines.ts @@ -105,14 +105,14 @@ interface TimelineDeleteAction { statusId: string; accountId: string; references: ImmutableMap; - reblogOf: unknown; + reblogOf: string | null; } const deleteFromTimelines = (statusId: string) => (dispatch: AppDispatch, getState: () => RootState) => { const accountId = getState().statuses.get(statusId)?.account?.id!; const references = getState().statuses.filter(status => status.reblog_id === statusId).map(status => [status.id, status.account.id] as const); - const reblogOf = getState().statuses.getIn([statusId, 'reblog'], null); + const reblogOf = getState().statuses.get(statusId)?.reblog_id || null; const action: TimelineDeleteAction = { type: TIMELINE_DELETE, @@ -125,9 +125,7 @@ const deleteFromTimelines = (statusId: string) => dispatch(action); }; -const clearTimeline = (timeline: string) => - (dispatch: AppDispatch) => - dispatch({ type: TIMELINE_CLEAR, timeline }); +const clearTimeline = (timeline: string) => ({ type: TIMELINE_CLEAR, timeline }); const noOp = () => { }; diff --git a/src/components/status-language-picker.tsx b/src/components/status-language-picker.tsx index 505e931da..70efd0fbc 100644 --- a/src/components/status-language-picker.tsx +++ b/src/components/status-language-picker.tsx @@ -23,7 +23,7 @@ const StatusLanguagePicker: React.FC = ({ status, showLab const intl = useIntl(); const dispatch = useAppDispatch(); - if (!status.contentMapHtml || !Object.keys(status.contentMapHtml).length) return null; + if (!status.contentMapHtml || Object.keys(status.contentMapHtml).length < 2) return null; const icon = ; diff --git a/src/components/translate-button.tsx b/src/components/translate-button.tsx index d512443bf..d2c76da8a 100644 --- a/src/components/translate-button.tsx +++ b/src/components/translate-button.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; import { translateStatus, undoStatusTranslation } from 'soapbox/actions/statuses'; @@ -19,7 +19,6 @@ const TranslateButton: React.FC = ({ status }) => { const features = useFeatures(); const instance = useInstance(); const settings = useSettings(); - const [autoTranslating, setAutoTranslating] = useState(false); const autoTranslate = settings.autoTranslate; const knownLanguages = autoTranslate ? [...settings.knownLanguages, intl.locale] : [intl.locale]; @@ -46,16 +45,13 @@ const TranslateButton: React.FC = ({ status }) => { }; useEffect(() => { - if (!status.translation && settings.autoTranslate && features.translations && renderTranslate && supportsLanguages && status.translation !== false && status.language !== null && !knownLanguages.includes(status.language)) { - setAutoTranslating(true); + if (status.translation === null && settings.autoTranslate && features.translations && renderTranslate && supportsLanguages && status.translation !== false && status.language !== null && !knownLanguages.includes(status.language)) { dispatch(translateStatus(status.id, intl.locale, true)); } }, []); if (!features.translations || !renderTranslate || !supportsLanguages || status.translation === false) return null; - if (settings.autoTranslate && !status.translating) return null; - const button = ( - - {visible && ( -
- {menu.map((menuItem, i) => ( - - ))} -
- )} - + ); }; diff --git a/src/reducers/push-notifications.ts b/src/reducers/push-notifications.ts index 35e2511f7..ed12522af 100644 --- a/src/reducers/push-notifications.ts +++ b/src/reducers/push-notifications.ts @@ -2,7 +2,7 @@ import { Map as ImmutableMap, Record as ImmutableRecord } from 'immutable'; import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, SET_ALERTS } from '../actions/push-notifications'; -import type { AnyAction } from 'redux'; +import type { SetterAction } from 'soapbox/actions/push-notifications/setter'; const SubscriptionRecord = ImmutableRecord({ id: '', @@ -25,7 +25,7 @@ const ReducerRecord = ImmutableRecord({ type Subscription = ReturnType; -const push_subscriptions = (state = ReducerRecord(), action: AnyAction) => { +const push_subscriptions = (state = ReducerRecord(), action: SetterAction) => { switch (action.type) { case SET_SUBSCRIPTION: return state diff --git a/src/reducers/timelines.ts b/src/reducers/timelines.ts index 23d7a06b3..3fe91faba 100644 --- a/src/reducers/timelines.ts +++ b/src/reducers/timelines.ts @@ -162,7 +162,7 @@ const shouldDelete = (timelineId: string, excludeAccount?: string) => { return true; }; -const deleteStatus = (state: State, statusId: string, accountId: string, references: ImmutableMap | Array<[string, string]>, excludeAccount?: string) => +const deleteStatus = (state: State, statusId: string, references: ImmutableMap | Array<[string, string]>, excludeAccount?: string) => state.withMutations(state => { state.keySeq().forEach(timelineId => { if (shouldDelete(timelineId, excludeAccount)) { @@ -173,7 +173,7 @@ const deleteStatus = (state: State, statusId: string, accountId: string, referen // Remove reblogs of deleted status references.forEach(ref => { - deleteStatus(state, ref[0], ref[1], [], excludeAccount); + deleteStatus(state, ref[0], [], excludeAccount); }); }); @@ -208,7 +208,7 @@ const filterTimelines = (state: State, relationship: Relationship, statuses: Imm statuses.forEach(status => { if (status.account.id !== relationship.id) return; const references = buildReferencesTo(statuses, status); - deleteStatus(state, status.id, status.account.id, references, relationship.id); + deleteStatus(state, status.id, references, relationship.id); }); }); @@ -329,7 +329,7 @@ const timelines = (state: State = initialState, action: AnyAction) => { case TIMELINE_DEQUEUE: return timelineDequeue(state, action.timeline); case TIMELINE_DELETE: - return deleteStatus(state, action.statusId, action.accountId, action.references, action.reblogOf); + return deleteStatus(state, action.statusId, action.references, action.reblogOf); case TIMELINE_CLEAR: return clearTimeline(state, action.timeline); case ACCOUNT_BLOCK_SUCCESS: