diff --git a/packages/pl-fe/src/actions/apps.ts b/packages/pl-fe/src/actions/apps.ts index bbd5e4538..97a68ef63 100644 --- a/packages/pl-fe/src/actions/apps.ts +++ b/packages/pl-fe/src/actions/apps.ts @@ -24,7 +24,7 @@ const createApp = (params: CreateApplicationParams, baseURL?: string) => return client.apps.createApplication(params).then((app) => { dispatch({ type: APP_CREATE_SUCCESS, params, app }); - return app as Record; + return app; }).catch(error => { dispatch({ type: APP_CREATE_FAIL, params, error }); throw error; diff --git a/packages/pl-fe/src/actions/auth.ts b/packages/pl-fe/src/actions/auth.ts index 37241fc8f..b19b4f400 100644 --- a/packages/pl-fe/src/actions/auth.ts +++ b/packages/pl-fe/src/actions/auth.ts @@ -6,7 +6,14 @@ * @see module:pl-fe/actions/oauth * @see module:pl-fe/actions/security */ -import { credentialAccountSchema, PlApiClient, type CreateAccountParams, type Token } from 'pl-api'; +import { + credentialAccountSchema, + PlApiClient, + type Application, + type CreateAccountParams, + type CredentialAccount, + type Token, +} from 'pl-api'; import { defineMessages } from 'react-intl'; import * as v from 'valibot'; @@ -32,6 +39,7 @@ import { type PlfeResponse, getClient } from '../api'; import { importEntities } from './importer'; +import type { Account } from 'pl-fe/normalizers/account'; import type { AppDispatch, RootState } from 'pl-fe/store'; const SWITCH_ACCOUNT = 'SWITCH_ACCOUNT' as const; @@ -49,6 +57,69 @@ const AUTH_ACCOUNT_REMEMBER_REQUEST = 'AUTH_ACCOUNT_REMEMBER_REQUEST' as const; const AUTH_ACCOUNT_REMEMBER_SUCCESS = 'AUTH_ACCOUNT_REMEMBER_SUCCESS' as const; const AUTH_ACCOUNT_REMEMBER_FAIL = 'AUTH_ACCOUNT_REMEMBER_FAIL' as const; +interface SwitchAccountAction { + type: typeof SWITCH_ACCOUNT; + account?: Account; + background: boolean; +} + +interface AuthAppCreatedAction { + type: typeof AUTH_APP_CREATED; + app: Application; +} + +interface AuthAppAuthorizedAction { + type: typeof AUTH_APP_AUTHORIZED; + app: Application; + token: Token; +} + +interface AuthLoggedInAction { + type: typeof AUTH_LOGGED_IN; + token: Token; +} + +interface AuthLoggedOutAction { + type: typeof AUTH_LOGGED_OUT; + account: Account; + standalone: boolean; +} + +interface VerifyCredentialsRequestAction { + type: typeof VERIFY_CREDENTIALS_REQUEST; + token: string; +} + +interface VerifyCredentialsSuccessAction { + type: typeof VERIFY_CREDENTIALS_SUCCESS; + token: string; + account: CredentialAccount; +} + +interface VerifyCredentialsFailAction { + type: typeof VERIFY_CREDENTIALS_FAIL; + token: string; + error: any; +} + +interface AuthAccountRememberRequestAction { + type: typeof AUTH_ACCOUNT_REMEMBER_REQUEST; + accountUrl: string; +} + +interface AuthAccountRememberSuccessAction { + type: typeof AUTH_ACCOUNT_REMEMBER_SUCCESS; + accountUrl: string; + account: CredentialAccount; +} + +interface AuthAccountRememberFailAction { + type: typeof AUTH_ACCOUNT_REMEMBER_FAIL; + error: any; + accountUrl: string; + skipAlert: boolean; +} + const customApp = custom('app'); const messages = defineMessages({ @@ -69,7 +140,7 @@ const createAppAndToken = () => const getAuthApp = () => (dispatch: AppDispatch) => { if (customApp?.client_secret) { - return noOp().then(() => dispatch({ type: AUTH_APP_CREATED, app: customApp })); + return noOp().then(() => dispatch({ type: AUTH_APP_CREATED, app: customApp })); } else { return dispatch(createAuthApp()); } @@ -84,25 +155,25 @@ const createAuthApp = () => website: sourceCode.homepage, }; - return dispatch(createApp(params)).then((app: Record) => - dispatch({ type: AUTH_APP_CREATED, app }), + return dispatch(createApp(params)).then((app) => + dispatch({ type: AUTH_APP_CREATED, app }), ); }; const createAppToken = () => (dispatch: AppDispatch, getState: () => RootState) => { - const app = getState().auth.app; + const app = getState().auth.app!; const params = { - client_id: app?.client_id!, - client_secret: app?.client_secret!, + client_id: app.client_id!, + client_secret: app.client_secret!, redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', grant_type: 'client_credentials', scope: getScopes(getState()), }; - return dispatch(obtainOAuthToken(params)).then((token: Record) => - dispatch({ type: AUTH_APP_AUTHORIZED, app, token }), + return dispatch(obtainOAuthToken(params)).then((token) => + dispatch({ type: AUTH_APP_AUTHORIZED, app, token }), ); }; @@ -145,13 +216,13 @@ const verifyCredentials = (token: string, accountUrl?: string) => (dispatch: AppDispatch, getState: () => RootState) => { const baseURL = parseBaseURL(accountUrl) || BuildConfig.BACKEND_URL; - dispatch({ type: VERIFY_CREDENTIALS_REQUEST, token }); + dispatch({ type: VERIFY_CREDENTIALS_REQUEST, token }); const client = new PlApiClient(baseURL, token); return client.settings.verifyCredentials().then((account) => { dispatch(importEntities({ accounts: [account] })); - dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account }); + dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account }); if (account.id === getState().me) dispatch(fetchMeSuccess(account)); return account; }).catch(error => { @@ -160,12 +231,12 @@ const verifyCredentials = (token: string, accountUrl?: string) => const account = error.response.json; const parsedAccount = v.parse(credentialAccountSchema, error.response.json); dispatch(importEntities({ accounts: [parsedAccount] })); - dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount }); + dispatch({ type: VERIFY_CREDENTIALS_SUCCESS, token, account: parsedAccount }); if (account.id === getState().me) dispatch(fetchMeSuccess(parsedAccount)); return parsedAccount; } else { if (getState().me === null) dispatch(fetchMeFail(error)); - dispatch({ type: VERIFY_CREDENTIALS_FAIL, token, error }); + dispatch({ type: VERIFY_CREDENTIALS_FAIL, token, error }); throw error; } }); @@ -173,14 +244,14 @@ const verifyCredentials = (token: string, accountUrl?: string) => const rememberAuthAccount = (accountUrl: string) => (dispatch: AppDispatch, getState: () => RootState) => { - dispatch({ type: AUTH_ACCOUNT_REMEMBER_REQUEST, accountUrl }); + dispatch({ type: AUTH_ACCOUNT_REMEMBER_REQUEST, accountUrl }); return KVStore.getItemOrError(`authAccount:${accountUrl}`).then(account => { dispatch(importEntities({ accounts: [account] })); - dispatch({ type: AUTH_ACCOUNT_REMEMBER_SUCCESS, account, accountUrl }); + dispatch({ type: AUTH_ACCOUNT_REMEMBER_SUCCESS, account, accountUrl }); if (account.id === getState().me) dispatch(fetchMeSuccess(account)); return account; }).catch(error => { - dispatch({ type: AUTH_ACCOUNT_REMEMBER_FAIL, error, accountUrl, skipAlert: true }); + dispatch({ type: AUTH_ACCOUNT_REMEMBER_FAIL, error, accountUrl, skipAlert: true }); }); }; @@ -227,7 +298,7 @@ const logOut = () => // Clear the account from Sentry. unsetSentryAccount(); - dispatch({ type: AUTH_LOGGED_OUT, account, standalone }); + dispatch({ type: AUTH_LOGGED_OUT, account, standalone }); toast.success(messages.loggedOut); }); @@ -240,7 +311,7 @@ const switchAccount = (accountId: string, background = false) => queryClient.invalidateQueries(); queryClient.clear(); - return dispatch({ type: SWITCH_ACCOUNT, account, background }); + return dispatch({ type: SWITCH_ACCOUNT, account, background }); }; const fetchOwnAccounts = () => @@ -272,10 +343,23 @@ const fetchCaptcha = () => const authLoggedIn = (token: Token) => (dispatch: AppDispatch) => { - dispatch({ type: AUTH_LOGGED_IN, token }); + dispatch({ type: AUTH_LOGGED_IN, token }); return token; }; +type AuthAction = + | SwitchAccountAction + | AuthAppCreatedAction + | AuthAppAuthorizedAction + | AuthLoggedInAction + | AuthLoggedOutAction + | VerifyCredentialsRequestAction + | VerifyCredentialsSuccessAction + | VerifyCredentialsFailAction + | AuthAccountRememberRequestAction + | AuthAccountRememberSuccessAction + | AuthAccountRememberFailAction; + export { SWITCH_ACCOUNT, AUTH_APP_CREATED, @@ -300,4 +384,5 @@ export { register, fetchCaptcha, authLoggedIn, + type AuthAction, }; diff --git a/packages/pl-fe/src/actions/me.ts b/packages/pl-fe/src/actions/me.ts index 60535df7e..bbbf7549a 100644 --- a/packages/pl-fe/src/actions/me.ts +++ b/packages/pl-fe/src/actions/me.ts @@ -39,6 +39,10 @@ const getMeToken = (state: RootState) => { return state.auth.users.get(accountUrl!)?.access_token; }; +interface MeFetchSkipAction { + type: typeof ME_FETCH_SKIP; +} + const fetchMe = () => (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); @@ -46,7 +50,7 @@ const fetchMe = () => const accountUrl = getMeUrl(state); if (!token) { - dispatch({ type: ME_FETCH_SKIP }); + dispatch({ type: ME_FETCH_SKIP }); return noOp(); } @@ -122,13 +126,11 @@ interface MePatchSuccessAction { const patchMeSuccess = (me: CredentialAccount) => (dispatch: AppDispatch) => { - const action: MePatchSuccessAction = { + dispatch(importEntities({ accounts: [me] })); + dispatch({ type: ME_PATCH_SUCCESS, me, - }; - - dispatch(importEntities({ accounts: [me] })); - dispatch(action); + }); }; const patchMeFail = (error: unknown) => ({ @@ -141,6 +143,7 @@ type MeAction = | ReturnType | ReturnType | ReturnType + | MeFetchSkipAction | ReturnType | MePatchSuccessAction | ReturnType; diff --git a/packages/pl-fe/src/reducers/accounts-meta.ts b/packages/pl-fe/src/reducers/accounts-meta.ts index 1349f69ac..28ee85b21 100644 --- a/packages/pl-fe/src/reducers/accounts-meta.ts +++ b/packages/pl-fe/src/reducers/accounts-meta.ts @@ -5,11 +5,10 @@ import { produce } from 'immer'; -import { VERIFY_CREDENTIALS_SUCCESS, AUTH_ACCOUNT_REMEMBER_SUCCESS } from 'pl-fe/actions/auth'; -import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'pl-fe/actions/me'; +import { VERIFY_CREDENTIALS_SUCCESS, AUTH_ACCOUNT_REMEMBER_SUCCESS, type AuthAction } from 'pl-fe/actions/auth'; +import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, type MeAction } from 'pl-fe/actions/me'; import type { Account, CredentialAccount } from 'pl-api'; -import type { AnyAction } from 'redux'; interface AccountMeta { pleroma: Account['__meta']['pleroma']; @@ -28,7 +27,7 @@ const importAccount = (state: State, account: CredentialAccount): State => }; }); -const accounts_meta = (state: Readonly = {}, action: AnyAction): State => { +const accounts_meta = (state: Readonly = {}, action: AuthAction | MeAction): State => { switch (action.type) { case ME_FETCH_SUCCESS: case ME_PATCH_SUCCESS: diff --git a/packages/pl-fe/src/reducers/auth.ts b/packages/pl-fe/src/reducers/auth.ts index abbdd1ab4..7f097df66 100644 --- a/packages/pl-fe/src/reducers/auth.ts +++ b/packages/pl-fe/src/reducers/auth.ts @@ -3,7 +3,7 @@ import trim from 'lodash/trim'; import { applicationSchema, PlApiClient, tokenSchema, type Application, type CredentialAccount, type Token } from 'pl-api'; import * as v from 'valibot'; -import { MASTODON_PRELOAD_IMPORT } from 'pl-fe/actions/preload'; +import { MASTODON_PRELOAD_IMPORT, type PreloadAction } from 'pl-fe/actions/preload'; import * as BuildConfig from 'pl-fe/build-config'; import KVStore from 'pl-fe/storage/kv-store'; import { validId, isURL, parseBaseURL } from 'pl-fe/utils/auth'; @@ -16,8 +16,9 @@ import { SWITCH_ACCOUNT, VERIFY_CREDENTIALS_SUCCESS, VERIFY_CREDENTIALS_FAIL, + type AuthAction, } from '../actions/auth'; -import { ME_FETCH_SKIP } from '../actions/me'; +import { ME_FETCH_SKIP, type MeAction } from '../actions/me'; import type { PlfeResponse } from 'pl-fe/api'; import type { Account as AccountEntity } from 'pl-fe/normalizers/account'; @@ -278,7 +279,7 @@ const deleteForbiddenToken = (state: State, error: { response: PlfeResponse }, t } }; -const reducer = (state: State, action: AnyAction) => { +const reducer = (state: State, action: AnyAction | AuthAction | MeAction | PreloadAction) => { switch (action.type) { case AUTH_APP_CREATED: return state.set('app', action.app); diff --git a/packages/pl-fe/src/reducers/contexts.ts b/packages/pl-fe/src/reducers/contexts.ts index 6a6fe4d00..104421868 100644 --- a/packages/pl-fe/src/reducers/contexts.ts +++ b/packages/pl-fe/src/reducers/contexts.ts @@ -4,7 +4,7 @@ import { OrderedSet as ImmutableOrderedSet, } from 'immutable'; -import { STATUS_IMPORT, STATUSES_IMPORT } from 'pl-fe/actions/importer'; +import { STATUS_IMPORT, STATUSES_IMPORT, type ImporterAction } from 'pl-fe/actions/importer'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from '../actions/accounts'; import { @@ -186,7 +186,7 @@ const deletePendingStatus = (state: State, params: Pick { +const replies = (state = ReducerRecord(), action: AnyAction | ImporterAction | StatusesAction | TimelineAction) => { switch (action.type) { case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_MUTE_SUCCESS: diff --git a/packages/pl-fe/src/reducers/me.ts b/packages/pl-fe/src/reducers/me.ts index cbf69e8aa..97686ce74 100644 --- a/packages/pl-fe/src/reducers/me.ts +++ b/packages/pl-fe/src/reducers/me.ts @@ -2,17 +2,18 @@ import { AUTH_LOGGED_OUT, AUTH_ACCOUNT_REMEMBER_SUCCESS, VERIFY_CREDENTIALS_SUCCESS, + type AuthAction, } from '../actions/auth'; import { ME_FETCH_SUCCESS, ME_FETCH_FAIL, ME_FETCH_SKIP, ME_PATCH_SUCCESS, + type MeAction, } from '../actions/me'; import type { PlfeResponse } from 'pl-fe/api'; import type { Me } from 'pl-fe/types/pl-fe'; -import type { AnyAction } from 'redux'; const initialState: Me = null; @@ -24,7 +25,7 @@ const handleForbidden = (state: Me, error: { response: PlfeResponse }) => { } }; -const me = (state: Me = initialState, action: AnyAction): Me => { +const me = (state: Me = initialState, action: AuthAction | MeAction): Me => { switch (action.type) { case ME_FETCH_SUCCESS: case ME_PATCH_SUCCESS: @@ -36,7 +37,7 @@ const me = (state: Me = initialState, action: AnyAction): Me => { case AUTH_LOGGED_OUT: return false; case ME_FETCH_FAIL: - return handleForbidden(state, action.error); + return handleForbidden(state, action.error as any); default: return state; } diff --git a/packages/pl-fe/src/reducers/notifications.ts b/packages/pl-fe/src/reducers/notifications.ts index 070d34af2..445e406b1 100644 --- a/packages/pl-fe/src/reducers/notifications.ts +++ b/packages/pl-fe/src/reducers/notifications.ts @@ -25,7 +25,7 @@ import { NOTIFICATIONS_MARK_READ_REQUEST, MAX_QUEUED_NOTIFICATIONS, } from '../actions/notifications'; -import { TIMELINE_DELETE } from '../actions/timelines'; +import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines'; import type { AccountWarning, Notification as BaseNotification, Markers, PaginatedResponse, Relationship, RelationshipSeveranceEvent, Report } from 'pl-api'; import type { Notification } from 'pl-fe/normalizers/notification'; @@ -215,7 +215,7 @@ const importMarker = (state: State, marker: Markers) => { }); }; -const notifications = (state: State = ReducerRecord(), action: AnyAction) => { +const notifications = (state: State = ReducerRecord(), action: AnyAction | TimelineAction) => { switch (action.type) { case NOTIFICATIONS_EXPAND_REQUEST: return state.set('isLoading', true); diff --git a/packages/pl-fe/src/reducers/polls.ts b/packages/pl-fe/src/reducers/polls.ts index f5015dc8d..3ad2489a9 100644 --- a/packages/pl-fe/src/reducers/polls.ts +++ b/packages/pl-fe/src/reducers/polls.ts @@ -1,9 +1,8 @@ import { Map as ImmutableMap } from 'immutable'; -import { POLLS_IMPORT } from 'pl-fe/actions/importer'; +import { POLLS_IMPORT, type ImporterAction } from 'pl-fe/actions/importer'; import type { Poll, Status } from 'pl-api'; -import type { AnyAction } from 'redux'; type State = ImmutableMap; @@ -14,7 +13,7 @@ const importPolls = (state: State, polls: Array>) const initialState: State = ImmutableMap(); -const polls = (state: State = initialState, action: AnyAction): State => { +const polls = (state: State = initialState, action: ImporterAction): State => { switch (action.type) { case POLLS_IMPORT: return importPolls(state, action.polls); diff --git a/packages/pl-fe/src/reducers/scheduled-statuses.ts b/packages/pl-fe/src/reducers/scheduled-statuses.ts index b7afb0c29..a61f5085f 100644 --- a/packages/pl-fe/src/reducers/scheduled-statuses.ts +++ b/packages/pl-fe/src/reducers/scheduled-statuses.ts @@ -1,6 +1,6 @@ import { Map as ImmutableMap } from 'immutable'; -import { STATUS_IMPORT, STATUSES_IMPORT } from 'pl-fe/actions/importer'; +import { STATUS_IMPORT, STATUSES_IMPORT, type ImporterAction } from 'pl-fe/actions/importer'; import { SCHEDULED_STATUSES_FETCH_SUCCESS, SCHEDULED_STATUS_CANCEL_REQUEST, @@ -25,7 +25,7 @@ const importStatuses = (state: State, statuses: Array) const deleteStatus = (state: State, statusId: string) => state.delete(statusId); -const scheduled_statuses = (state: State = initialState, action: AnyAction) => { +const scheduled_statuses = (state: State = initialState, action: AnyAction | ImporterAction) => { switch (action.type) { case STATUS_IMPORT: case STATUS_CREATE_SUCCESS: diff --git a/packages/pl-fe/src/reducers/status-lists.ts b/packages/pl-fe/src/reducers/status-lists.ts index 62a253676..032c4ec97 100644 --- a/packages/pl-fe/src/reducers/status-lists.ts +++ b/packages/pl-fe/src/reducers/status-lists.ts @@ -31,6 +31,7 @@ import { JOINED_EVENTS_FETCH_REQUEST, JOINED_EVENTS_FETCH_SUCCESS, JOINED_EVENTS_FETCH_FAIL, + type EventsAction, } from '../actions/events'; import { FAVOURITED_STATUSES_FETCH_REQUEST, @@ -152,7 +153,7 @@ const removeBookmarkFromLists = (state: State, status: Pick { +const statusLists = (state = initialState, action: AnyAction | BookmarksAction | EventsAction | FavouritesAction | InteractionsAction | PinStatusesAction | StatusQuotesAction) => { switch (action.type) { case FAVOURITED_STATUSES_FETCH_REQUEST: case FAVOURITED_STATUSES_EXPAND_REQUEST: diff --git a/packages/pl-fe/src/reducers/suggestions.ts b/packages/pl-fe/src/reducers/suggestions.ts index 9c6cad054..e829dead5 100644 --- a/packages/pl-fe/src/reducers/suggestions.ts +++ b/packages/pl-fe/src/reducers/suggestions.ts @@ -1,7 +1,7 @@ import { Record as ImmutableRecord } from 'immutable'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'pl-fe/actions/accounts'; -import { DOMAIN_BLOCK_SUCCESS } from 'pl-fe/actions/domain-blocks'; +import { DOMAIN_BLOCK_SUCCESS, type DomainBlocksAction } from 'pl-fe/actions/domain-blocks'; import { SUGGESTIONS_FETCH_REQUEST, SUGGESTIONS_FETCH_SUCCESS, @@ -37,7 +37,7 @@ const dismissAccount = (state: State, accountId: string) => const dismissAccounts = (state: State, accountIds: string[]) => state.update('items', items => items.filter(item => !accountIds.includes(item.account_id))); -const suggestionsReducer = (state: State = ReducerRecord(), action: AnyAction) => { +const suggestionsReducer = (state: State = ReducerRecord(), action: AnyAction | DomainBlocksAction) => { switch (action.type) { case SUGGESTIONS_FETCH_REQUEST: return state.set('isLoading', true); diff --git a/packages/pl-fe/src/reducers/timelines.ts b/packages/pl-fe/src/reducers/timelines.ts index 9e2d4a0ed..763341c47 100644 --- a/packages/pl-fe/src/reducers/timelines.ts +++ b/packages/pl-fe/src/reducers/timelines.ts @@ -7,8 +7,8 @@ import { import sample from 'lodash/sample'; import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from '../actions/accounts'; -import { PIN_SUCCESS, UNPIN_SUCCESS } from '../actions/interactions'; -import { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS } from '../actions/statuses'; +import { PIN_SUCCESS, UNPIN_SUCCESS, type InteractionsAction } from '../actions/interactions'; +import { STATUS_CREATE_REQUEST, STATUS_CREATE_SUCCESS, type StatusesAction } from '../actions/statuses'; import { TIMELINE_UPDATE, TIMELINE_DELETE, @@ -21,6 +21,7 @@ import { MAX_QUEUED_ITEMS, TIMELINE_SCROLL_TOP, TIMELINE_INSERT, + type TimelineAction, } from '../actions/timelines'; import type { PaginatedResponse, Status as BaseStatus, Relationship } from 'pl-api'; @@ -294,7 +295,7 @@ const handleExpandFail = (state: State, timelineId: string) => state.withMutatio setFailed(state, timelineId, true); }); -const timelines = (state: State = initialState, action: AnyAction) => { +const timelines = (state: State = initialState, action: AnyAction | InteractionsAction | StatusesAction | TimelineAction) => { switch (action.type) { case STATUS_CREATE_REQUEST: if (action.params.scheduled_at) return state;