From 621408489067a08c6eb518aa7a319500e016896a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 19 Jun 2022 20:38:51 +0200 Subject: [PATCH] Actions: TypeScript MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/actions/aliases.js | 196 --------------- app/soapbox/actions/aliases.ts | 234 ++++++++++++++++++ app/soapbox/actions/emoji_reacts.js | 182 -------------- app/soapbox/actions/emoji_reacts.ts | 190 ++++++++++++++ app/soapbox/actions/me.js | 123 --------- app/soapbox/actions/me.ts | 133 ++++++++++ app/soapbox/actions/mfa.js | 84 ------- app/soapbox/actions/mfa.ts | 105 ++++++++ .../actions/{moderation.js => moderation.tsx} | 63 ++--- app/soapbox/actions/mutes.js | 116 --------- app/soapbox/actions/mutes.ts | 125 ++++++++++ .../{notifications.js => notifications.ts} | 184 +++++++------- .../actions/{preload.js => preload.ts} | 51 ++-- .../actions/{security.js => security.ts} | 140 ++++++----- .../actions/{soapbox.js => soapbox.ts} | 95 +++---- app/soapbox/components/hashtag.tsx | 4 +- .../features/aliases/components/account.tsx | 2 +- app/soapbox/features/aliases/index.tsx | 2 +- .../components/password_reset_confirm.tsx | 2 +- app/soapbox/features/delete_account/index.tsx | 2 +- app/soapbox/features/status/index.tsx | 4 +- .../report-modal/steps/other-actions-step.tsx | 2 +- .../ui/components/profile_info_panel.tsx | 2 +- app/soapbox/utils/accounts.ts | 6 +- 24 files changed, 1099 insertions(+), 948 deletions(-) delete mode 100644 app/soapbox/actions/aliases.js create mode 100644 app/soapbox/actions/aliases.ts delete mode 100644 app/soapbox/actions/emoji_reacts.js create mode 100644 app/soapbox/actions/emoji_reacts.ts delete mode 100644 app/soapbox/actions/me.js create mode 100644 app/soapbox/actions/me.ts delete mode 100644 app/soapbox/actions/mfa.js create mode 100644 app/soapbox/actions/mfa.ts rename app/soapbox/actions/{moderation.js => moderation.tsx} (81%) delete mode 100644 app/soapbox/actions/mutes.js create mode 100644 app/soapbox/actions/mutes.ts rename app/soapbox/actions/{notifications.js => notifications.ts} (59%) rename app/soapbox/actions/{preload.js => preload.ts} (51%) rename app/soapbox/actions/{security.js => security.ts} (53%) rename app/soapbox/actions/{soapbox.js => soapbox.ts} (55%) diff --git a/app/soapbox/actions/aliases.js b/app/soapbox/actions/aliases.js deleted file mode 100644 index 3b898ae10..000000000 --- a/app/soapbox/actions/aliases.js +++ /dev/null @@ -1,196 +0,0 @@ -import { defineMessages } from 'react-intl'; - -import { isLoggedIn } from 'soapbox/utils/auth'; -import { getFeatures } from 'soapbox/utils/features'; - -import api from '../api'; - -import { showAlertForError } from './alerts'; -import { importFetchedAccounts } from './importer'; -import { patchMeSuccess } from './me'; -import snackbar from './snackbar'; - -export const ALIASES_FETCH_REQUEST = 'ALIASES_FETCH_REQUEST'; -export const ALIASES_FETCH_SUCCESS = 'ALIASES_FETCH_SUCCESS'; -export const ALIASES_FETCH_FAIL = 'ALIASES_FETCH_FAIL'; - -export const ALIASES_SUGGESTIONS_CHANGE = 'ALIASES_SUGGESTIONS_CHANGE'; -export const ALIASES_SUGGESTIONS_READY = 'ALIASES_SUGGESTIONS_READY'; -export const ALIASES_SUGGESTIONS_CLEAR = 'ALIASES_SUGGESTIONS_CLEAR'; - -export const ALIASES_ADD_REQUEST = 'ALIASES_ADD_REQUEST'; -export const ALIASES_ADD_SUCCESS = 'ALIASES_ADD_SUCCESS'; -export const ALIASES_ADD_FAIL = 'ALIASES_ADD_FAIL'; - -export const ALIASES_REMOVE_REQUEST = 'ALIASES_REMOVE_REQUEST'; -export const ALIASES_REMOVE_SUCCESS = 'ALIASES_REMOVE_SUCCESS'; -export const ALIASES_REMOVE_FAIL = 'ALIASES_REMOVE_FAIL'; - -const messages = defineMessages({ - createSuccess: { id: 'aliases.success.add', defaultMessage: 'Account alias created successfully' }, - removeSuccess: { id: 'aliases.success.remove', defaultMessage: 'Account alias removed successfully' }, -}); - -export const fetchAliases = (dispatch, getState) => { - if (!isLoggedIn(getState)) return; - const state = getState(); - - const instance = state.get('instance'); - const features = getFeatures(instance); - - if (!features.accountMoving) return; - - dispatch(fetchAliasesRequest()); - - api(getState).get('/api/pleroma/aliases') - .then(response => { - dispatch(fetchAliasesSuccess(response.data.aliases)); - }) - .catch(err => dispatch(fetchAliasesFail(err))); -}; - -export const fetchAliasesRequest = () => ({ - type: ALIASES_FETCH_REQUEST, -}); - -export const fetchAliasesSuccess = aliases => ({ - type: ALIASES_FETCH_SUCCESS, - value: aliases, -}); - -export const fetchAliasesFail = error => ({ - type: ALIASES_FETCH_FAIL, - error, -}); - -export const fetchAliasesSuggestions = q => (dispatch, getState) => { - if (!isLoggedIn(getState)) return; - - const params = { - q, - resolve: true, - limit: 4, - }; - - api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => { - dispatch(importFetchedAccounts(data)); - dispatch(fetchAliasesSuggestionsReady(q, data)); - }).catch(error => dispatch(showAlertForError(error))); -}; - -export const fetchAliasesSuggestionsReady = (query, accounts) => ({ - type: ALIASES_SUGGESTIONS_READY, - query, - accounts, -}); - -export const clearAliasesSuggestions = () => ({ - type: ALIASES_SUGGESTIONS_CLEAR, -}); - -export const changeAliasesSuggestions = value => ({ - type: ALIASES_SUGGESTIONS_CHANGE, - value, -}); - -export const addToAliases = (account) => (dispatch, getState) => { - if (!isLoggedIn(getState)) return; - const state = getState(); - - const instance = state.get('instance'); - const features = getFeatures(instance); - - if (!features.accountMoving) { - const me = state.get('me'); - const alsoKnownAs = state.getIn(['accounts_meta', me, 'pleroma', 'also_known_as']); - - dispatch(addToAliasesRequest()); - - api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: [...alsoKnownAs, account.getIn(['pleroma', 'ap_id'])] }) - .then((response => { - dispatch(snackbar.success(messages.createSuccess)); - dispatch(addToAliasesSuccess); - dispatch(patchMeSuccess(response.data)); - })) - .catch(err => dispatch(addToAliasesFail(err))); - - return; - } - - dispatch(addToAliasesRequest()); - - api(getState).put('/api/pleroma/aliases', { - alias: account.get('acct'), - }) - .then(response => { - dispatch(snackbar.success(messages.createSuccess)); - dispatch(addToAliasesSuccess); - dispatch(fetchAliases); - }) - .catch(err => dispatch(fetchAliasesFail(err))); -}; - -export const addToAliasesRequest = () => ({ - type: ALIASES_ADD_REQUEST, -}); - -export const addToAliasesSuccess = () => ({ - type: ALIASES_ADD_SUCCESS, -}); - -export const addToAliasesFail = error => ({ - type: ALIASES_ADD_FAIL, - error, -}); - -export const removeFromAliases = (account) => (dispatch, getState) => { - if (!isLoggedIn(getState)) return; - const state = getState(); - - const instance = state.get('instance'); - const features = getFeatures(instance); - - if (!features.accountMoving) { - const me = state.get('me'); - const alsoKnownAs = state.getIn(['accounts_meta', me, 'pleroma', 'also_known_as']); - - dispatch(removeFromAliasesRequest()); - - api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: alsoKnownAs.filter(id => id !== account) }) - .then(response => { - dispatch(snackbar.success(messages.removeSuccess)); - dispatch(removeFromAliasesSuccess); - dispatch(patchMeSuccess(response.data)); - }) - .catch(err => dispatch(removeFromAliasesFail(err))); - - return; - } - - dispatch(addToAliasesRequest()); - - api(getState).delete('/api/pleroma/aliases', { - data: { - alias: account, - }, - }) - .then(response => { - dispatch(snackbar.success(messages.removeSuccess)); - dispatch(removeFromAliasesSuccess); - dispatch(fetchAliases); - }) - .catch(err => dispatch(fetchAliasesFail(err))); -}; - -export const removeFromAliasesRequest = () => ({ - type: ALIASES_REMOVE_REQUEST, -}); - -export const removeFromAliasesSuccess = () => ({ - type: ALIASES_REMOVE_SUCCESS, -}); - -export const removeFromAliasesFail = error => ({ - type: ALIASES_REMOVE_FAIL, - error, -}); diff --git a/app/soapbox/actions/aliases.ts b/app/soapbox/actions/aliases.ts new file mode 100644 index 000000000..8361e31ad --- /dev/null +++ b/app/soapbox/actions/aliases.ts @@ -0,0 +1,234 @@ +import { defineMessages } from 'react-intl'; + +import { isLoggedIn } from 'soapbox/utils/auth'; +import { getFeatures } from 'soapbox/utils/features'; + +import api from '../api'; + +import { showAlertForError } from './alerts'; +import { importFetchedAccounts } from './importer'; +import { patchMeSuccess } from './me'; +import snackbar from './snackbar'; + +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { APIEntity, Account } from 'soapbox/types/entities'; + +const ALIASES_FETCH_REQUEST = 'ALIASES_FETCH_REQUEST'; +const ALIASES_FETCH_SUCCESS = 'ALIASES_FETCH_SUCCESS'; +const ALIASES_FETCH_FAIL = 'ALIASES_FETCH_FAIL'; + +const ALIASES_SUGGESTIONS_CHANGE = 'ALIASES_SUGGESTIONS_CHANGE'; +const ALIASES_SUGGESTIONS_READY = 'ALIASES_SUGGESTIONS_READY'; +const ALIASES_SUGGESTIONS_CLEAR = 'ALIASES_SUGGESTIONS_CLEAR'; + +const ALIASES_ADD_REQUEST = 'ALIASES_ADD_REQUEST'; +const ALIASES_ADD_SUCCESS = 'ALIASES_ADD_SUCCESS'; +const ALIASES_ADD_FAIL = 'ALIASES_ADD_FAIL'; + +const ALIASES_REMOVE_REQUEST = 'ALIASES_REMOVE_REQUEST'; +const ALIASES_REMOVE_SUCCESS = 'ALIASES_REMOVE_SUCCESS'; +const ALIASES_REMOVE_FAIL = 'ALIASES_REMOVE_FAIL'; + +const messages = defineMessages({ + createSuccess: { id: 'aliases.success.add', defaultMessage: 'Account alias created successfully' }, + removeSuccess: { id: 'aliases.success.remove', defaultMessage: 'Account alias removed successfully' }, +}); + +const fetchAliases = (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return; + const state = getState(); + + const instance = state.instance; + const features = getFeatures(instance); + + if (!features.accountMoving) return; + + dispatch(fetchAliasesRequest()); + + api(getState).get('/api/pleroma/aliases') + .then(response => { + dispatch(fetchAliasesSuccess(response.data.aliases)); + }) + .catch(err => dispatch(fetchAliasesFail(err))); +}; + +const fetchAliasesRequest = () => ({ + type: ALIASES_FETCH_REQUEST, +}); + +const fetchAliasesSuccess = (aliases: APIEntity[]) => ({ + type: ALIASES_FETCH_SUCCESS, + value: aliases, +}); + +const fetchAliasesFail = (error: AxiosError) => ({ + type: ALIASES_FETCH_FAIL, + error, +}); + +const fetchAliasesSuggestions = (q: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return; + + const params = { + q, + resolve: true, + limit: 4, + }; + + api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => { + dispatch(importFetchedAccounts(data)); + dispatch(fetchAliasesSuggestionsReady(q, data)); + }).catch(error => dispatch(showAlertForError(error))); + }; + +const fetchAliasesSuggestionsReady = (query: string, accounts: APIEntity[]) => ({ + type: ALIASES_SUGGESTIONS_READY, + query, + accounts, +}); + +const clearAliasesSuggestions = () => ({ + type: ALIASES_SUGGESTIONS_CLEAR, +}); + +const changeAliasesSuggestions = (value: string) => ({ + type: ALIASES_SUGGESTIONS_CHANGE, + value, +}); + +const addToAliases = (account: Account) => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return; + const state = getState(); + + const instance = state.instance; + const features = getFeatures(instance); + + if (!features.accountMoving) { + const me = state.me; + const alsoKnownAs = state.accounts_meta.get(me as string)!.pleroma.get('also_known_as'); + + dispatch(addToAliasesRequest()); + + api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: [...alsoKnownAs, account.pleroma.get('ap_id')] }) + .then((response => { + dispatch(snackbar.success(messages.createSuccess)); + dispatch(addToAliasesSuccess); + dispatch(patchMeSuccess(response.data)); + })) + .catch(err => dispatch(addToAliasesFail(err))); + + return; + } + + dispatch(addToAliasesRequest()); + + api(getState).put('/api/pleroma/aliases', { + alias: account.acct, + }) + .then(() => { + dispatch(snackbar.success(messages.createSuccess)); + dispatch(addToAliasesSuccess); + dispatch(fetchAliases); + }) + .catch(err => dispatch(fetchAliasesFail(err))); + }; + +const addToAliasesRequest = () => ({ + type: ALIASES_ADD_REQUEST, +}); + +const addToAliasesSuccess = () => ({ + type: ALIASES_ADD_SUCCESS, +}); + +const addToAliasesFail = (error: AxiosError) => ({ + type: ALIASES_ADD_FAIL, + error, +}); + +const removeFromAliases = (account: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return; + const state = getState(); + + const instance = state.instance; + const features = getFeatures(instance); + + if (!features.accountMoving) { + const me = state.me; + const alsoKnownAs = state.accounts_meta.get(me as string)!.pleroma.get('also_known_as'); + + dispatch(removeFromAliasesRequest()); + + api(getState).patch('/api/v1/accounts/update_credentials', { also_known_as: alsoKnownAs.filter((id: string) => id !== account) }) + .then(response => { + dispatch(snackbar.success(messages.removeSuccess)); + dispatch(removeFromAliasesSuccess); + dispatch(patchMeSuccess(response.data)); + }) + .catch(err => dispatch(removeFromAliasesFail(err))); + + return; + } + + dispatch(addToAliasesRequest()); + + api(getState).delete('/api/pleroma/aliases', { + data: { + alias: account, + }, + }) + .then(response => { + dispatch(snackbar.success(messages.removeSuccess)); + dispatch(removeFromAliasesSuccess); + dispatch(fetchAliases); + }) + .catch(err => dispatch(fetchAliasesFail(err))); + }; + +const removeFromAliasesRequest = () => ({ + type: ALIASES_REMOVE_REQUEST, +}); + +const removeFromAliasesSuccess = () => ({ + type: ALIASES_REMOVE_SUCCESS, +}); + +const removeFromAliasesFail = (error: AxiosError) => ({ + type: ALIASES_REMOVE_FAIL, + error, +}); + +export { + ALIASES_FETCH_REQUEST, + ALIASES_FETCH_SUCCESS, + ALIASES_FETCH_FAIL, + ALIASES_SUGGESTIONS_CHANGE, + ALIASES_SUGGESTIONS_READY, + ALIASES_SUGGESTIONS_CLEAR, + ALIASES_ADD_REQUEST, + ALIASES_ADD_SUCCESS, + ALIASES_ADD_FAIL, + ALIASES_REMOVE_REQUEST, + ALIASES_REMOVE_SUCCESS, + ALIASES_REMOVE_FAIL, + fetchAliases, + fetchAliasesRequest, + fetchAliasesSuccess, + fetchAliasesFail, + fetchAliasesSuggestions, + fetchAliasesSuggestionsReady, + clearAliasesSuggestions, + changeAliasesSuggestions, + addToAliases, + addToAliasesRequest, + addToAliasesSuccess, + addToAliasesFail, + removeFromAliases, + removeFromAliasesRequest, + removeFromAliasesSuccess, + removeFromAliasesFail, +}; diff --git a/app/soapbox/actions/emoji_reacts.js b/app/soapbox/actions/emoji_reacts.js deleted file mode 100644 index a057dd35e..000000000 --- a/app/soapbox/actions/emoji_reacts.js +++ /dev/null @@ -1,182 +0,0 @@ -import { List as ImmutableList } from 'immutable'; - -import { isLoggedIn } from 'soapbox/utils/auth'; - -import api from '../api'; - -import { importFetchedAccounts, importFetchedStatus } from './importer'; -import { favourite, unfavourite } from './interactions'; - -export const EMOJI_REACT_REQUEST = 'EMOJI_REACT_REQUEST'; -export const EMOJI_REACT_SUCCESS = 'EMOJI_REACT_SUCCESS'; -export const EMOJI_REACT_FAIL = 'EMOJI_REACT_FAIL'; - -export const UNEMOJI_REACT_REQUEST = 'UNEMOJI_REACT_REQUEST'; -export const UNEMOJI_REACT_SUCCESS = 'UNEMOJI_REACT_SUCCESS'; -export const UNEMOJI_REACT_FAIL = 'UNEMOJI_REACT_FAIL'; - -export const EMOJI_REACTS_FETCH_REQUEST = 'EMOJI_REACTS_FETCH_REQUEST'; -export const EMOJI_REACTS_FETCH_SUCCESS = 'EMOJI_REACTS_FETCH_SUCCESS'; -export const EMOJI_REACTS_FETCH_FAIL = 'EMOJI_REACTS_FETCH_FAIL'; - -const noOp = () => () => new Promise(f => f()); - -export const simpleEmojiReact = (status, emoji) => { - return (dispatch, getState) => { - const emojiReacts = status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()); - - if (emoji === '👍' && status.get('favourited')) return dispatch(unfavourite(status)); - - const undo = emojiReacts.filter(e => e.get('me') === true && e.get('name') === emoji).count() > 0; - if (undo) return dispatch(unEmojiReact(status, emoji)); - - return Promise.all( - emojiReacts - .filter(emojiReact => emojiReact.get('me') === true) - .map(emojiReact => dispatch(unEmojiReact(status, emojiReact.get('name')))), - status.get('favourited') && dispatch(unfavourite(status)), - ).then(() => { - if (emoji === '👍') { - dispatch(favourite(status)); - } else { - dispatch(emojiReact(status, emoji)); - } - }).catch(err => { - console.error(err); - }); - }; -}; - -export function fetchEmojiReacts(id, emoji) { - return (dispatch, getState) => { - if (!isLoggedIn(getState)) return dispatch(noOp()); - - dispatch(fetchEmojiReactsRequest(id, emoji)); - - const url = emoji - ? `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` - : `/api/v1/pleroma/statuses/${id}/reactions`; - - return api(getState).get(url).then(response => { - response.data.forEach(emojiReact => { - dispatch(importFetchedAccounts(emojiReact.accounts)); - }); - dispatch(fetchEmojiReactsSuccess(id, response.data)); - }).catch(error => { - dispatch(fetchEmojiReactsFail(id, error)); - }); - }; -} - -export function emojiReact(status, emoji) { - return function(dispatch, getState) { - if (!isLoggedIn(getState)) return dispatch(noOp()); - - dispatch(emojiReactRequest(status, emoji)); - - return api(getState) - .put(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) - .then(function(response) { - dispatch(importFetchedStatus(response.data)); - dispatch(emojiReactSuccess(status, emoji)); - }).catch(function(error) { - dispatch(emojiReactFail(status, emoji, error)); - }); - }; -} - -export function unEmojiReact(status, emoji) { - return (dispatch, getState) => { - if (!isLoggedIn(getState)) return dispatch(noOp()); - - dispatch(unEmojiReactRequest(status, emoji)); - - return api(getState) - .delete(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) - .then(response => { - dispatch(importFetchedStatus(response.data)); - dispatch(unEmojiReactSuccess(status, emoji)); - }).catch(error => { - dispatch(unEmojiReactFail(status, emoji, error)); - }); - }; -} - -export function fetchEmojiReactsRequest(id, emoji) { - return { - type: EMOJI_REACTS_FETCH_REQUEST, - id, - emoji, - }; -} - -export function fetchEmojiReactsSuccess(id, emojiReacts) { - return { - type: EMOJI_REACTS_FETCH_SUCCESS, - id, - emojiReacts, - }; -} - -export function fetchEmojiReactsFail(id, error) { - return { - type: EMOJI_REACTS_FETCH_FAIL, - error, - }; -} - -export function emojiReactRequest(status, emoji) { - return { - type: EMOJI_REACT_REQUEST, - status, - emoji, - skipLoading: true, - }; -} - -export function emojiReactSuccess(status, emoji) { - return { - type: EMOJI_REACT_SUCCESS, - status, - emoji, - skipLoading: true, - }; -} - -export function emojiReactFail(status, emoji, error) { - return { - type: EMOJI_REACT_FAIL, - status, - emoji, - error, - skipLoading: true, - }; -} - -export function unEmojiReactRequest(status, emoji) { - return { - type: UNEMOJI_REACT_REQUEST, - status, - emoji, - skipLoading: true, - }; -} - -export function unEmojiReactSuccess(status, emoji) { - return { - type: UNEMOJI_REACT_SUCCESS, - status, - emoji, - skipLoading: true, - }; -} - -export function unEmojiReactFail(status, emoji, error) { - return { - type: UNEMOJI_REACT_FAIL, - status, - emoji, - error, - skipLoading: true, - }; -} diff --git a/app/soapbox/actions/emoji_reacts.ts b/app/soapbox/actions/emoji_reacts.ts new file mode 100644 index 000000000..ac205d38d --- /dev/null +++ b/app/soapbox/actions/emoji_reacts.ts @@ -0,0 +1,190 @@ +import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; + +import { isLoggedIn } from 'soapbox/utils/auth'; + +import api from '../api'; + +import { importFetchedAccounts, importFetchedStatus } from './importer'; +import { favourite, unfavourite } from './interactions'; + +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { APIEntity, Status } from 'soapbox/types/entities'; + +const EMOJI_REACT_REQUEST = 'EMOJI_REACT_REQUEST'; +const EMOJI_REACT_SUCCESS = 'EMOJI_REACT_SUCCESS'; +const EMOJI_REACT_FAIL = 'EMOJI_REACT_FAIL'; + +const UNEMOJI_REACT_REQUEST = 'UNEMOJI_REACT_REQUEST'; +const UNEMOJI_REACT_SUCCESS = 'UNEMOJI_REACT_SUCCESS'; +const UNEMOJI_REACT_FAIL = 'UNEMOJI_REACT_FAIL'; + +const EMOJI_REACTS_FETCH_REQUEST = 'EMOJI_REACTS_FETCH_REQUEST'; +const EMOJI_REACTS_FETCH_SUCCESS = 'EMOJI_REACTS_FETCH_SUCCESS'; +const EMOJI_REACTS_FETCH_FAIL = 'EMOJI_REACTS_FETCH_FAIL'; + +const noOp = () => () => new Promise(f => f(undefined)); + +const simpleEmojiReact = (status: Status, emoji: string) => + (dispatch: AppDispatch) => { + const emojiReacts: ImmutableList> = status.pleroma.get('emoji_reactions') || ImmutableList(); + + if (emoji === '👍' && status.favourited) return dispatch(unfavourite(status)); + + const undo = emojiReacts.filter(e => e.get('me') === true && e.get('name') === emoji).count() > 0; + if (undo) return dispatch(unEmojiReact(status, emoji)); + + return Promise.all([ + ...emojiReacts + .filter((emojiReact) => emojiReact.get('me') === true) + .map(emojiReact => dispatch(unEmojiReact(status, emojiReact.get('name')))).toArray(), + status.favourited && dispatch(unfavourite(status)), + ]).then(() => { + if (emoji === '👍') { + dispatch(favourite(status)); + } else { + dispatch(emojiReact(status, emoji)); + } + }).catch(err => { + console.error(err); + }); + }; + +const fetchEmojiReacts = (id: string, emoji: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return dispatch(noOp()); + + dispatch(fetchEmojiReactsRequest(id, emoji)); + + const url = emoji + ? `/api/v1/pleroma/statuses/${id}/reactions/${emoji}` + : `/api/v1/pleroma/statuses/${id}/reactions`; + + return api(getState).get(url).then(response => { + response.data.forEach((emojiReact: APIEntity) => { + dispatch(importFetchedAccounts(emojiReact.accounts)); + }); + dispatch(fetchEmojiReactsSuccess(id, response.data)); + }).catch(error => { + dispatch(fetchEmojiReactsFail(id, error)); + }); + }; + +const emojiReact = (status: Status, emoji: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return dispatch(noOp()); + + dispatch(emojiReactRequest(status, emoji)); + + return api(getState) + .put(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) + .then(function(response) { + dispatch(importFetchedStatus(response.data)); + dispatch(emojiReactSuccess(status, emoji)); + }).catch(function(error) { + dispatch(emojiReactFail(status, emoji, error)); + }); + }; + +const unEmojiReact = (status: Status, emoji: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return dispatch(noOp()); + + dispatch(unEmojiReactRequest(status, emoji)); + + return api(getState) + .delete(`/api/v1/pleroma/statuses/${status.get('id')}/reactions/${emoji}`) + .then(response => { + dispatch(importFetchedStatus(response.data)); + dispatch(unEmojiReactSuccess(status, emoji)); + }).catch(error => { + dispatch(unEmojiReactFail(status, emoji, error)); + }); + }; + +const fetchEmojiReactsRequest = (id: string, emoji: string) => ({ + type: EMOJI_REACTS_FETCH_REQUEST, + id, + emoji, +}); + +const fetchEmojiReactsSuccess = (id: string, emojiReacts: APIEntity[]) => ({ + type: EMOJI_REACTS_FETCH_SUCCESS, + id, + emojiReacts, +}); + +const fetchEmojiReactsFail = (id: string, error: AxiosError) => ({ + type: EMOJI_REACTS_FETCH_FAIL, + id, + error, +}); + +const emojiReactRequest = (status: Status, emoji: string) => ({ + type: EMOJI_REACT_REQUEST, + status, + emoji, + skipLoading: true, +}); + +const emojiReactSuccess = (status: Status, emoji: string) => ({ + type: EMOJI_REACT_SUCCESS, + status, + emoji, + skipLoading: true, +}); + +const emojiReactFail = (status: Status, emoji: string, error: AxiosError) => ({ + type: EMOJI_REACT_FAIL, + status, + emoji, + error, + skipLoading: true, +}); + +const unEmojiReactRequest = (status: Status, emoji: string) => ({ + type: UNEMOJI_REACT_REQUEST, + status, + emoji, + skipLoading: true, +}); + +const unEmojiReactSuccess = (status: Status, emoji: string) => ({ + type: UNEMOJI_REACT_SUCCESS, + status, + emoji, + skipLoading: true, +}); + +const unEmojiReactFail = (status: Status, emoji: string, error: AxiosError) => ({ + type: UNEMOJI_REACT_FAIL, + status, + emoji, + error, + skipLoading: true, +}); + +export { + EMOJI_REACT_REQUEST, + EMOJI_REACT_SUCCESS, + EMOJI_REACT_FAIL, + UNEMOJI_REACT_REQUEST, + UNEMOJI_REACT_SUCCESS, + UNEMOJI_REACT_FAIL, + EMOJI_REACTS_FETCH_REQUEST, + EMOJI_REACTS_FETCH_SUCCESS, + EMOJI_REACTS_FETCH_FAIL, + simpleEmojiReact, + fetchEmojiReacts, + emojiReact, + unEmojiReact, + fetchEmojiReactsRequest, + fetchEmojiReactsSuccess, + fetchEmojiReactsFail, + emojiReactRequest, + emojiReactSuccess, + emojiReactFail, + unEmojiReactRequest, + unEmojiReactSuccess, + unEmojiReactFail, +}; diff --git a/app/soapbox/actions/me.js b/app/soapbox/actions/me.js deleted file mode 100644 index 96afcab7d..000000000 --- a/app/soapbox/actions/me.js +++ /dev/null @@ -1,123 +0,0 @@ -import KVStore from 'soapbox/storage/kv_store'; -import { getAuthUserId, getAuthUserUrl } from 'soapbox/utils/auth'; - -import api from '../api'; - -import { loadCredentials } from './auth'; -import { importFetchedAccount } from './importer'; - -export const ME_FETCH_REQUEST = 'ME_FETCH_REQUEST'; -export const ME_FETCH_SUCCESS = 'ME_FETCH_SUCCESS'; -export const ME_FETCH_FAIL = 'ME_FETCH_FAIL'; -export const ME_FETCH_SKIP = 'ME_FETCH_SKIP'; - -export const ME_PATCH_REQUEST = 'ME_PATCH_REQUEST'; -export const ME_PATCH_SUCCESS = 'ME_PATCH_SUCCESS'; -export const ME_PATCH_FAIL = 'ME_PATCH_FAIL'; - -const noOp = () => new Promise(f => f()); - -const getMeId = state => state.get('me') || getAuthUserId(state); - -const getMeUrl = state => { - const accountId = getMeId(state); - return state.getIn(['accounts', accountId, 'url']) || getAuthUserUrl(state); -}; - -const getMeToken = state => { - // Fallback for upgrading IDs to URLs - const accountUrl = getMeUrl(state) || state.getIn(['auth', 'me']); - return state.getIn(['auth', 'users', accountUrl, 'access_token']); -}; - -export function fetchMe() { - return (dispatch, getState) => { - const state = getState(); - const token = getMeToken(state); - const accountUrl = getMeUrl(state); - - if (!token) { - dispatch({ type: ME_FETCH_SKIP }); return noOp(); - } - - dispatch(fetchMeRequest()); - return dispatch(loadCredentials(token, accountUrl)).catch(error => { - dispatch(fetchMeFail(error)); - }); - }; -} - -/** Update the auth account in IndexedDB for Mastodon, etc. */ -const persistAuthAccount = (account, params) => { - if (account && account.url) { - if (!account.pleroma) account.pleroma = {}; - - if (!account.pleroma.settings_store) { - account.pleroma.settings_store = params.pleroma_settings_store || {}; - } - KVStore.setItem(`authAccount:${account.url}`, account).catch(console.error); - } -}; - -export function patchMe(params) { - return (dispatch, getState) => { - dispatch(patchMeRequest()); - - return api(getState) - .patch('/api/v1/accounts/update_credentials', params) - .then(response => { - persistAuthAccount(response.data, params); - dispatch(patchMeSuccess(response.data)); - }).catch(error => { - dispatch(patchMeFail(error)); - throw error; - }); - }; -} - -export function fetchMeRequest() { - return { - type: ME_FETCH_REQUEST, - }; -} - -export function fetchMeSuccess(me) { - return (dispatch, getState) => { - dispatch({ - type: ME_FETCH_SUCCESS, - me, - }); - }; -} - -export function fetchMeFail(error) { - return { - type: ME_FETCH_FAIL, - error, - skipAlert: true, - }; -} - -export function patchMeRequest() { - return { - type: ME_PATCH_REQUEST, - }; -} - -export function patchMeSuccess(me) { - return (dispatch, getState) => { - dispatch(importFetchedAccount(me)); - dispatch({ - type: ME_PATCH_SUCCESS, - me, - }); - }; -} - -export function patchMeFail(error) { - return { - type: ME_PATCH_FAIL, - error, - skipAlert: true, - }; -} diff --git a/app/soapbox/actions/me.ts b/app/soapbox/actions/me.ts new file mode 100644 index 000000000..acd845f8b --- /dev/null +++ b/app/soapbox/actions/me.ts @@ -0,0 +1,133 @@ +import KVStore from 'soapbox/storage/kv_store'; +import { getAuthUserId, getAuthUserUrl } from 'soapbox/utils/auth'; + +import api from '../api'; + +import { loadCredentials } from './auth'; +import { importFetchedAccount } from './importer'; + +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { APIEntity } from 'soapbox/types/entities'; + +const ME_FETCH_REQUEST = 'ME_FETCH_REQUEST'; +const ME_FETCH_SUCCESS = 'ME_FETCH_SUCCESS'; +const ME_FETCH_FAIL = 'ME_FETCH_FAIL'; +const ME_FETCH_SKIP = 'ME_FETCH_SKIP'; + +const ME_PATCH_REQUEST = 'ME_PATCH_REQUEST'; +const ME_PATCH_SUCCESS = 'ME_PATCH_SUCCESS'; +const ME_PATCH_FAIL = 'ME_PATCH_FAIL'; + +const noOp = () => new Promise(f => f(undefined)); + +const getMeId = (state: RootState) => state.me || getAuthUserId(state); + +const getMeUrl = (state: RootState) => { + const accountId = getMeId(state); + return state.accounts.get(accountId)!.url || getAuthUserUrl(state); +}; + +const getMeToken = (state: RootState) => { + // Fallback for upgrading IDs to URLs + const accountUrl = getMeUrl(state) || state.auth.get('me'); + return state.auth.getIn(['users', accountUrl, 'access_token']); +}; + +const fetchMe = () => + (dispatch: AppDispatch, getState: () => RootState) => { + const state = getState(); + const token = getMeToken(state); + const accountUrl = getMeUrl(state); + + if (!token) { + dispatch({ type: ME_FETCH_SKIP }); return noOp(); + } + + dispatch(fetchMeRequest()); + return dispatch(loadCredentials(token, accountUrl)).catch(error => { + dispatch(fetchMeFail(error)); + }); + }; + +/** Update the auth account in IndexedDB for Mastodon, etc. */ +const persistAuthAccount = (account: APIEntity, params: Record) => { + if (account && account.url) { + if (!account.pleroma) account.pleroma = {}; + + if (!account.pleroma.settings_store) { + account.pleroma.settings_store = params.pleroma_settings_store || {}; + } + KVStore.setItem(`authAccount:${account.url}`, account).catch(console.error); + } +}; + +const patchMe = (params: Record) => + (dispatch: AppDispatch, getState: () => RootState) => { + dispatch(patchMeRequest()); + + return api(getState) + .patch('/api/v1/accounts/update_credentials', params) + .then(response => { + persistAuthAccount(response.data, params); + dispatch(patchMeSuccess(response.data)); + }).catch(error => { + dispatch(patchMeFail(error)); + throw error; + }); + }; + +const fetchMeRequest = () => ({ + type: ME_FETCH_REQUEST, +}); + +const fetchMeSuccess = (me: APIEntity) => + (dispatch: AppDispatch) => { + dispatch({ + type: ME_FETCH_SUCCESS, + me, + }); + }; + +const fetchMeFail = (error: APIEntity) => ({ + type: ME_FETCH_FAIL, + error, + skipAlert: true, +}); + +const patchMeRequest = () => ({ + type: ME_PATCH_REQUEST, +}); + +const patchMeSuccess = (me: APIEntity) => + (dispatch: AppDispatch) => { + dispatch(importFetchedAccount(me)); + dispatch({ + type: ME_PATCH_SUCCESS, + me, + }); + }; + +const patchMeFail = (error: AxiosError) => ({ + type: ME_PATCH_FAIL, + error, + skipAlert: true, +}); + +export { + ME_FETCH_REQUEST, + ME_FETCH_SUCCESS, + ME_FETCH_FAIL, + ME_FETCH_SKIP, + ME_PATCH_REQUEST, + ME_PATCH_SUCCESS, + ME_PATCH_FAIL, + fetchMe, + patchMe, + fetchMeRequest, + fetchMeSuccess, + fetchMeFail, + patchMeRequest, + patchMeSuccess, + patchMeFail, +}; diff --git a/app/soapbox/actions/mfa.js b/app/soapbox/actions/mfa.js deleted file mode 100644 index d41c60f52..000000000 --- a/app/soapbox/actions/mfa.js +++ /dev/null @@ -1,84 +0,0 @@ -import api from '../api'; - -export const MFA_FETCH_REQUEST = 'MFA_FETCH_REQUEST'; -export const MFA_FETCH_SUCCESS = 'MFA_FETCH_SUCCESS'; -export const MFA_FETCH_FAIL = 'MFA_FETCH_FAIL'; - -export const MFA_BACKUP_CODES_FETCH_REQUEST = 'MFA_BACKUP_CODES_FETCH_REQUEST'; -export const MFA_BACKUP_CODES_FETCH_SUCCESS = 'MFA_BACKUP_CODES_FETCH_SUCCESS'; -export const MFA_BACKUP_CODES_FETCH_FAIL = 'MFA_BACKUP_CODES_FETCH_FAIL'; - -export const MFA_SETUP_REQUEST = 'MFA_SETUP_REQUEST'; -export const MFA_SETUP_SUCCESS = 'MFA_SETUP_SUCCESS'; -export const MFA_SETUP_FAIL = 'MFA_SETUP_FAIL'; - -export const MFA_CONFIRM_REQUEST = 'MFA_CONFIRM_REQUEST'; -export const MFA_CONFIRM_SUCCESS = 'MFA_CONFIRM_SUCCESS'; -export const MFA_CONFIRM_FAIL = 'MFA_CONFIRM_FAIL'; - -export const MFA_DISABLE_REQUEST = 'MFA_DISABLE_REQUEST'; -export const MFA_DISABLE_SUCCESS = 'MFA_DISABLE_SUCCESS'; -export const MFA_DISABLE_FAIL = 'MFA_DISABLE_FAIL'; - -export function fetchMfa() { - return (dispatch, getState) => { - dispatch({ type: MFA_FETCH_REQUEST }); - return api(getState).get('/api/pleroma/accounts/mfa').then(({ data }) => { - dispatch({ type: MFA_FETCH_SUCCESS, data }); - }).catch(error => { - dispatch({ type: MFA_FETCH_FAIL }); - }); - }; -} - -export function fetchBackupCodes() { - return (dispatch, getState) => { - dispatch({ type: MFA_BACKUP_CODES_FETCH_REQUEST }); - return api(getState).get('/api/pleroma/accounts/mfa/backup_codes').then(({ data }) => { - dispatch({ type: MFA_BACKUP_CODES_FETCH_SUCCESS, data }); - return data; - }).catch(error => { - dispatch({ type: MFA_BACKUP_CODES_FETCH_FAIL }); - }); - }; -} - -export function setupMfa(method) { - return (dispatch, getState) => { - dispatch({ type: MFA_SETUP_REQUEST, method }); - return api(getState).get(`/api/pleroma/accounts/mfa/setup/${method}`).then(({ data }) => { - dispatch({ type: MFA_SETUP_SUCCESS, data }); - return data; - }).catch(error => { - dispatch({ type: MFA_SETUP_FAIL }); - throw error; - }); - }; -} - -export function confirmMfa(method, code, password) { - return (dispatch, getState) => { - const params = { code, password }; - dispatch({ type: MFA_CONFIRM_REQUEST, method, code }); - return api(getState).post(`/api/pleroma/accounts/mfa/confirm/${method}`, params).then(({ data }) => { - dispatch({ type: MFA_CONFIRM_SUCCESS, method, code }); - return data; - }).catch(error => { - dispatch({ type: MFA_CONFIRM_FAIL, method, code, error, skipAlert: true }); - throw error; - }); - }; -} - -export function disableMfa(method, password) { - return (dispatch, getState) => { - dispatch({ type: MFA_DISABLE_REQUEST, method }); - return api(getState).delete(`/api/pleroma/accounts/mfa/${method}`, { data: { password } }).then(({ data }) => { - dispatch({ type: MFA_DISABLE_SUCCESS, method }); - return data; - }).catch(error => { - dispatch({ type: MFA_DISABLE_FAIL, method, skipAlert: true }); - throw error; - }); - }; -} diff --git a/app/soapbox/actions/mfa.ts b/app/soapbox/actions/mfa.ts new file mode 100644 index 000000000..4c1e85832 --- /dev/null +++ b/app/soapbox/actions/mfa.ts @@ -0,0 +1,105 @@ +import api from '../api'; + +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; + +const MFA_FETCH_REQUEST = 'MFA_FETCH_REQUEST'; +const MFA_FETCH_SUCCESS = 'MFA_FETCH_SUCCESS'; +const MFA_FETCH_FAIL = 'MFA_FETCH_FAIL'; + +const MFA_BACKUP_CODES_FETCH_REQUEST = 'MFA_BACKUP_CODES_FETCH_REQUEST'; +const MFA_BACKUP_CODES_FETCH_SUCCESS = 'MFA_BACKUP_CODES_FETCH_SUCCESS'; +const MFA_BACKUP_CODES_FETCH_FAIL = 'MFA_BACKUP_CODES_FETCH_FAIL'; + +const MFA_SETUP_REQUEST = 'MFA_SETUP_REQUEST'; +const MFA_SETUP_SUCCESS = 'MFA_SETUP_SUCCESS'; +const MFA_SETUP_FAIL = 'MFA_SETUP_FAIL'; + +const MFA_CONFIRM_REQUEST = 'MFA_CONFIRM_REQUEST'; +const MFA_CONFIRM_SUCCESS = 'MFA_CONFIRM_SUCCESS'; +const MFA_CONFIRM_FAIL = 'MFA_CONFIRM_FAIL'; + +const MFA_DISABLE_REQUEST = 'MFA_DISABLE_REQUEST'; +const MFA_DISABLE_SUCCESS = 'MFA_DISABLE_SUCCESS'; +const MFA_DISABLE_FAIL = 'MFA_DISABLE_FAIL'; + +const fetchMfa = () => + (dispatch: AppDispatch, getState: () => RootState) => { + dispatch({ type: MFA_FETCH_REQUEST }); + return api(getState).get('/api/pleroma/accounts/mfa').then(({ data }) => { + dispatch({ type: MFA_FETCH_SUCCESS, data }); + }).catch(() => { + dispatch({ type: MFA_FETCH_FAIL }); + }); + }; + +const fetchBackupCodes = () => + (dispatch: AppDispatch, getState: () => RootState) => { + dispatch({ type: MFA_BACKUP_CODES_FETCH_REQUEST }); + return api(getState).get('/api/pleroma/accounts/mfa/backup_codes').then(({ data }) => { + dispatch({ type: MFA_BACKUP_CODES_FETCH_SUCCESS, data }); + return data; + }).catch(() => { + dispatch({ type: MFA_BACKUP_CODES_FETCH_FAIL }); + }); + }; + +const setupMfa = (method: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + dispatch({ type: MFA_SETUP_REQUEST, method }); + return api(getState).get(`/api/pleroma/accounts/mfa/setup/${method}`).then(({ data }) => { + dispatch({ type: MFA_SETUP_SUCCESS, data }); + return data; + }).catch((error: AxiosError) => { + dispatch({ type: MFA_SETUP_FAIL }); + throw error; + }); + }; + +const confirmMfa = (method: string, code: string, password: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + const params = { code, password }; + dispatch({ type: MFA_CONFIRM_REQUEST, method, code }); + return api(getState).post(`/api/pleroma/accounts/mfa/confirm/${method}`, params).then(({ data }) => { + dispatch({ type: MFA_CONFIRM_SUCCESS, method, code }); + return data; + }).catch((error: AxiosError) => { + dispatch({ type: MFA_CONFIRM_FAIL, method, code, error, skipAlert: true }); + throw error; + }); + }; + +const disableMfa = (method: string, password: string) => + (dispatch: AppDispatch, getState: () => RootState) => { + dispatch({ type: MFA_DISABLE_REQUEST, method }); + return api(getState).delete(`/api/pleroma/accounts/mfa/${method}`, { data: { password } }).then(({ data }) => { + dispatch({ type: MFA_DISABLE_SUCCESS, method }); + return data; + }).catch((error: AxiosError) => { + dispatch({ type: MFA_DISABLE_FAIL, method, skipAlert: true }); + throw error; + }); + }; + +export { + MFA_FETCH_REQUEST, + MFA_FETCH_SUCCESS, + MFA_FETCH_FAIL, + MFA_BACKUP_CODES_FETCH_REQUEST, + MFA_BACKUP_CODES_FETCH_SUCCESS, + MFA_BACKUP_CODES_FETCH_FAIL, + MFA_SETUP_REQUEST, + MFA_SETUP_SUCCESS, + MFA_SETUP_FAIL, + MFA_CONFIRM_REQUEST, + MFA_CONFIRM_SUCCESS, + MFA_CONFIRM_FAIL, + MFA_DISABLE_REQUEST, + MFA_DISABLE_SUCCESS, + MFA_DISABLE_FAIL, + fetchMfa, + fetchBackupCodes, + setupMfa, + confirmMfa, + disableMfa, +}; diff --git a/app/soapbox/actions/moderation.js b/app/soapbox/actions/moderation.tsx similarity index 81% rename from app/soapbox/actions/moderation.js rename to app/soapbox/actions/moderation.tsx index d84242d66..946157071 100644 --- a/app/soapbox/actions/moderation.js +++ b/app/soapbox/actions/moderation.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { defineMessages } from 'react-intl'; +import { defineMessages, IntlShape } from 'react-intl'; import { fetchAccountByUsername } from 'soapbox/actions/accounts'; import { deactivateUsers, deleteUsers, deleteStatus, toggleStatusSensitivity } from 'soapbox/actions/admin'; @@ -8,6 +8,8 @@ import snackbar from 'soapbox/actions/snackbar'; import AccountContainer from 'soapbox/containers/account_container'; import { isLocal } from 'soapbox/utils/accounts'; +import type { AppDispatch, RootState } from 'soapbox/store'; + const messages = defineMessages({ deactivateUserHeading: { id: 'confirmations.admin.deactivate_user.heading', defaultMessage: 'Deactivate @{acct}' }, deactivateUserPrompt: { id: 'confirmations.admin.deactivate_user.message', defaultMessage: 'You are about to deactivate @{acct}. Deactivating a user is a reversible action.' }, @@ -35,11 +37,11 @@ const messages = defineMessages({ statusMarkedNotSensitive: { id: 'admin.statuses.status_marked_message_not_sensitive', defaultMessage: 'Post by @{acct} was marked not sensitive' }, }); -export function deactivateUserModal(intl, accountId, afterConfirm = () => {}) { - return function(dispatch, getState) { +const deactivateUserModal = (intl: IntlShape, accountId: string, afterConfirm = () => {}) => + (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); - const acct = state.getIn(['accounts', accountId, 'acct']); - const name = state.getIn(['accounts', accountId, 'username']); + const acct = state.accounts.get(accountId)!.acct; + const name = state.accounts.get(accountId)!.username; dispatch(openModal('CONFIRM', { icon: require('@tabler/icons/icons/user-off.svg'), @@ -55,15 +57,15 @@ export function deactivateUserModal(intl, accountId, afterConfirm = () => {}) { }, })); }; -} -export function deleteUserModal(intl, accountId, afterConfirm = () => {}) { - return function(dispatch, getState) { +const deleteUserModal = (intl: IntlShape, accountId: string, afterConfirm = () => {}) => + (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); - const acct = state.getIn(['accounts', accountId, 'acct']); - const name = state.getIn(['accounts', accountId, 'username']); - const favicon = state.getIn(['accounts', accountId, 'pleroma', 'favicon']); - const local = isLocal(state.getIn(['accounts', accountId])); + const account = state.accounts.get(accountId)!; + const acct = account.acct; + const name = account.username; + const favicon = account.pleroma.get('favicon'); + const local = isLocal(account); const message = (<> @@ -96,13 +98,12 @@ export function deleteUserModal(intl, accountId, afterConfirm = () => {}) { }, })); }; -} -export function rejectUserModal(intl, accountId, afterConfirm = () => {}) { - return function(dispatch, getState) { +const rejectUserModal = (intl: IntlShape, accountId: string, afterConfirm = () => {}) => + (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); - const acct = state.getIn(['accounts', accountId, 'acct']); - const name = state.getIn(['accounts', accountId, 'username']); + const acct = state.accounts.get(accountId)!.acct; + const name = state.accounts.get(accountId)!.username; dispatch(openModal('CONFIRM', { icon: require('@tabler/icons/icons/user-off.svg'), @@ -118,13 +119,12 @@ export function rejectUserModal(intl, accountId, afterConfirm = () => {}) { }, })); }; -} -export function toggleStatusSensitivityModal(intl, statusId, sensitive, afterConfirm = () => {}) { - return function(dispatch, getState) { +const toggleStatusSensitivityModal = (intl: IntlShape, statusId: string, sensitive: boolean, afterConfirm = () => {}) => + (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); - const accountId = state.getIn(['statuses', statusId, 'account']); - const acct = state.getIn(['accounts', accountId, 'acct']); + const accountId = state.statuses.get(statusId)!.account; + const acct = state.accounts.get(accountId)!.acct; dispatch(openModal('CONFIRM', { icon: require('@tabler/icons/icons/alert-triangle.svg'), @@ -140,13 +140,12 @@ export function toggleStatusSensitivityModal(intl, statusId, sensitive, afterCon }, })); }; -} -export function deleteStatusModal(intl, statusId, afterConfirm = () => {}) { - return function(dispatch, getState) { +const deleteStatusModal = (intl: IntlShape, statusId: string, afterConfirm = () => {}) => + (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); - const accountId = state.getIn(['statuses', statusId, 'account']); - const acct = state.getIn(['accounts', accountId, 'acct']); + const accountId = state.statuses.get(statusId)!.account; + const acct = state.accounts.get(accountId)!.acct; dispatch(openModal('CONFIRM', { icon: require('@tabler/icons/icons/trash.svg'), @@ -162,4 +161,12 @@ export function deleteStatusModal(intl, statusId, afterConfirm = () => {}) { }, })); }; -} + + +export { + deactivateUserModal, + deleteUserModal, + rejectUserModal, + toggleStatusSensitivityModal, + deleteStatusModal, +}; diff --git a/app/soapbox/actions/mutes.js b/app/soapbox/actions/mutes.js deleted file mode 100644 index f204ea9b8..000000000 --- a/app/soapbox/actions/mutes.js +++ /dev/null @@ -1,116 +0,0 @@ -import { isLoggedIn } from 'soapbox/utils/auth'; -import { getNextLinkName } from 'soapbox/utils/quirks'; - -import api, { getLinks } from '../api'; - -import { fetchRelationships } from './accounts'; -import { importFetchedAccounts } from './importer'; -import { openModal } from './modals'; - -export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST'; -export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS'; -export const MUTES_FETCH_FAIL = 'MUTES_FETCH_FAIL'; - -export const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST'; -export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS'; -export const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL'; - -export const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL'; -export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS'; - -export function fetchMutes() { - return (dispatch, getState) => { - if (!isLoggedIn(getState)) return; - const nextLinkName = getNextLinkName(getState); - - dispatch(fetchMutesRequest()); - - api(getState).get('/api/v1/mutes').then(response => { - const next = getLinks(response).refs.find(link => link.rel === nextLinkName); - dispatch(importFetchedAccounts(response.data)); - dispatch(fetchMutesSuccess(response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map(item => item.id))); - }).catch(error => dispatch(fetchMutesFail(error))); - }; -} - -export function fetchMutesRequest() { - return { - type: MUTES_FETCH_REQUEST, - }; -} - -export function fetchMutesSuccess(accounts, next) { - return { - type: MUTES_FETCH_SUCCESS, - accounts, - next, - }; -} - -export function fetchMutesFail(error) { - return { - type: MUTES_FETCH_FAIL, - error, - }; -} - -export function expandMutes() { - return (dispatch, getState) => { - if (!isLoggedIn(getState)) return; - const nextLinkName = getNextLinkName(getState); - - const url = getState().getIn(['user_lists', 'mutes', 'next']); - - if (url === null) { - return; - } - - dispatch(expandMutesRequest()); - - api(getState).get(url).then(response => { - const next = getLinks(response).refs.find(link => link.rel === nextLinkName); - dispatch(importFetchedAccounts(response.data)); - dispatch(expandMutesSuccess(response.data, next ? next.uri : null)); - dispatch(fetchRelationships(response.data.map(item => item.id))); - }).catch(error => dispatch(expandMutesFail(error))); - }; -} - -export function expandMutesRequest() { - return { - type: MUTES_EXPAND_REQUEST, - }; -} - -export function expandMutesSuccess(accounts, next) { - return { - type: MUTES_EXPAND_SUCCESS, - accounts, - next, - }; -} - -export function expandMutesFail(error) { - return { - type: MUTES_EXPAND_FAIL, - error, - }; -} - -export function initMuteModal(account) { - return dispatch => { - dispatch({ - type: MUTES_INIT_MODAL, - account, - }); - - dispatch(openModal('MUTE')); - }; -} - -export function toggleHideNotifications() { - return dispatch => { - dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS }); - }; -} diff --git a/app/soapbox/actions/mutes.ts b/app/soapbox/actions/mutes.ts new file mode 100644 index 000000000..0ec7d147b --- /dev/null +++ b/app/soapbox/actions/mutes.ts @@ -0,0 +1,125 @@ +import { isLoggedIn } from 'soapbox/utils/auth'; +import { getNextLinkName } from 'soapbox/utils/quirks'; + +import api, { getLinks } from '../api'; + +import { fetchRelationships } from './accounts'; +import { importFetchedAccounts } from './importer'; +import { openModal } from './modals'; + +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { APIEntity, Account as AccountEntity } from 'soapbox/types/entities'; + +const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST'; +const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS'; +const MUTES_FETCH_FAIL = 'MUTES_FETCH_FAIL'; + +const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST'; +const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS'; +const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL'; + +const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL'; +const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS'; + +const fetchMutes = () => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return; + const nextLinkName = getNextLinkName(getState); + + dispatch(fetchMutesRequest()); + + api(getState).get('/api/v1/mutes').then(response => { + const next = getLinks(response).refs.find(link => link.rel === nextLinkName); + dispatch(importFetchedAccounts(response.data)); + dispatch(fetchMutesSuccess(response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); + }).catch(error => dispatch(fetchMutesFail(error))); + }; + +const fetchMutesRequest = () => ({ + type: MUTES_FETCH_REQUEST, +}); + +const fetchMutesSuccess = (accounts: APIEntity[], next: string | null) => ({ + type: MUTES_FETCH_SUCCESS, + accounts, + next, +}); + +const fetchMutesFail = (error: AxiosError) => ({ + type: MUTES_FETCH_FAIL, + error, +}); + +const expandMutes = () => + (dispatch: AppDispatch, getState: () => RootState) => { + if (!isLoggedIn(getState)) return; + const nextLinkName = getNextLinkName(getState); + + const url = getState().user_lists.getIn(['mutes', 'next']); + + if (url === null) { + return; + } + + dispatch(expandMutesRequest()); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === nextLinkName); + dispatch(importFetchedAccounts(response.data)); + dispatch(expandMutesSuccess(response.data, next ? next.uri : null)); + dispatch(fetchRelationships(response.data.map((item: APIEntity) => item.id))); + }).catch(error => dispatch(expandMutesFail(error))); + }; + +const expandMutesRequest = () => ({ + type: MUTES_EXPAND_REQUEST, +}); + +const expandMutesSuccess = (accounts: APIEntity[], next: string | null) => ({ + type: MUTES_EXPAND_SUCCESS, + accounts, + next, +}); + +const expandMutesFail = (error: AxiosError) => ({ + type: MUTES_EXPAND_FAIL, + error, +}); + +const initMuteModal = (account: AccountEntity) => + (dispatch: AppDispatch) => { + dispatch({ + type: MUTES_INIT_MODAL, + account, + }); + + dispatch(openModal('MUTE')); + }; + +const toggleHideNotifications = () => + (dispatch: AppDispatch) => { + dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS }); + }; + +export { + MUTES_FETCH_REQUEST, + MUTES_FETCH_SUCCESS, + MUTES_FETCH_FAIL, + MUTES_EXPAND_REQUEST, + MUTES_EXPAND_SUCCESS, + MUTES_EXPAND_FAIL, + MUTES_INIT_MODAL, + MUTES_TOGGLE_HIDE_NOTIFICATIONS, + fetchMutes, + fetchMutesRequest, + fetchMutesSuccess, + fetchMutesFail, + expandMutes, + expandMutesRequest, + expandMutesSuccess, + expandMutesFail, + initMuteModal, + toggleHideNotifications, +}; diff --git a/app/soapbox/actions/notifications.js b/app/soapbox/actions/notifications.ts similarity index 59% rename from app/soapbox/actions/notifications.js rename to app/soapbox/actions/notifications.ts index 66918c698..0e396b89a 100644 --- a/app/soapbox/actions/notifications.js +++ b/app/soapbox/actions/notifications.ts @@ -1,20 +1,18 @@ import { List as ImmutableList, Map as ImmutableMap, - OrderedMap as ImmutableOrderedMap, } from 'immutable'; import IntlMessageFormat from 'intl-messageformat'; import 'intl-pluralrules'; import { defineMessages } from 'react-intl'; +import api, { getLinks } from 'soapbox/api'; +import { getFilters, regexFromFilters } from 'soapbox/selectors'; import { isLoggedIn } from 'soapbox/utils/auth'; import { parseVersion, PLEROMA } from 'soapbox/utils/features'; +import { unescapeHTML } from 'soapbox/utils/html'; import { joinPublicPath } from 'soapbox/utils/static'; -import api, { getLinks } from '../api'; -import { getFilters, regexFromFilters } from '../selectors'; -import { unescapeHTML } from '../utils/html'; - import { fetchRelationships } from './accounts'; import { importFetchedAccount, @@ -25,32 +23,36 @@ import { import { saveMarker } from './markers'; import { getSettings, saveSettings } from './settings'; -export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; -export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; -export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE'; -export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE'; +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { APIEntity } from 'soapbox/types/entities'; -export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST'; -export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS'; -export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL'; +const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; +const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; +const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE'; +const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE'; -export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; +const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST'; +const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS'; +const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL'; -export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; -export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; +const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET'; -export const NOTIFICATIONS_MARK_READ_REQUEST = 'NOTIFICATIONS_MARK_READ_REQUEST'; -export const NOTIFICATIONS_MARK_READ_SUCCESS = 'NOTIFICATIONS_MARK_READ_SUCCESS'; -export const NOTIFICATIONS_MARK_READ_FAIL = 'NOTIFICATIONS_MARK_READ_FAIL'; +const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR'; +const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP'; -export const MAX_QUEUED_NOTIFICATIONS = 40; +const NOTIFICATIONS_MARK_READ_REQUEST = 'NOTIFICATIONS_MARK_READ_REQUEST'; +const NOTIFICATIONS_MARK_READ_SUCCESS = 'NOTIFICATIONS_MARK_READ_SUCCESS'; +const NOTIFICATIONS_MARK_READ_FAIL = 'NOTIFICATIONS_MARK_READ_FAIL'; + +const MAX_QUEUED_NOTIFICATIONS = 40; defineMessages({ mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' }, group: { id: 'notifications.group', defaultMessage: '{count} notifications' }, }); -const fetchRelatedRelationships = (dispatch, notifications) => { +const fetchRelatedRelationships = (dispatch: AppDispatch, notifications: APIEntity[]) => { const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id); if (accountIds.length > 0) { @@ -58,8 +60,8 @@ const fetchRelatedRelationships = (dispatch, notifications) => { } }; -export function updateNotifications(notification, intlMessages, intlLocale) { - return (dispatch, getState) => { +const updateNotifications = (notification: APIEntity) => + (dispatch: AppDispatch, getState: () => RootState) => { const showInColumn = getSettings(getState()).getIn(['notifications', 'shows', notification.type], true); if (notification.account) { @@ -84,17 +86,16 @@ export function updateNotifications(notification, intlMessages, intlLocale) { fetchRelatedRelationships(dispatch, [notification]); } }; -} -export function updateNotificationsQueue(notification, intlMessages, intlLocale, curPath) { - return (dispatch, getState) => { +const updateNotificationsQueue = (notification: APIEntity, intlMessages: Record, intlLocale: string, curPath: string) => + (dispatch: AppDispatch, getState: () => RootState) => { if (notification.type === 'pleroma:chat_mention') return; // Drop chat notifications, handle them per-chat const showAlert = getSettings(getState()).getIn(['notifications', 'alerts', notification.type]); const filters = getFilters(getState(), { contextType: 'notifications' }); const playSound = getSettings(getState()).getIn(['notifications', 'sounds', notification.type]); - let filtered = false; + let filtered: boolean | null = false; const isOnNotificationsPage = curPath === '/notifications'; @@ -140,21 +141,20 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale, intlLocale, }); } else { - dispatch(updateNotifications(notification, intlMessages, intlLocale)); + dispatch(updateNotifications(notification)); } }; -} -export function dequeueNotifications() { - return (dispatch, getState) => { - const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableOrderedMap()); - const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0); +const dequeueNotifications = () => + (dispatch: AppDispatch, getState: () => RootState) => { + const queuedNotifications = getState().notifications.get('queuedNotifications'); + const totalQueuedNotificationsCount = getState().notifications.get('totalQueuedNotificationsCount'); if (totalQueuedNotificationsCount === 0) { return; } else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) { - queuedNotifications.forEach(block => { - dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale)); + queuedNotifications.forEach((block: APIEntity) => { + dispatch(updateNotifications(block.notification)); }); } else { dispatch(expandNotifications()); @@ -165,23 +165,22 @@ export function dequeueNotifications() { }); dispatch(markReadNotifications()); }; -} -const excludeTypesFromSettings = getState => getSettings(getState()).getIn(['notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS(); +const excludeTypesFromSettings = (getState: () => RootState) => (getSettings(getState()).getIn(['notifications', 'shows']) as ImmutableMap).filter(enabled => !enabled).keySeq().toJS(); -const excludeTypesFromFilter = filter => { +const excludeTypesFromFilter = (filter: string) => { const allTypes = ImmutableList(['follow', 'follow_request', 'favourite', 'reblog', 'mention', 'status', 'poll', 'move', 'pleroma:emoji_reaction']); return allTypes.filterNot(item => item === filter).toJS(); }; const noOp = () => {}; -export function expandNotifications({ maxId } = {}, done = noOp) { - return (dispatch, getState) => { +const expandNotifications = ({ maxId }: Record = {}, done = noOp) => + (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return dispatch(noOp); - const activeFilter = getSettings(getState()).getIn(['notifications', 'quickFilter', 'active']); - const notifications = getState().get('notifications'); + const activeFilter = getSettings(getState()).getIn(['notifications', 'quickFilter', 'active']) as string; + const notifications = getState().notifications; const isLoadingMore = !!maxId; if (notifications.get('isLoading')) { @@ -189,7 +188,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) { return dispatch(noOp); } - const params = { + const params: Record = { max_id: maxId, exclude_types: activeFilter === 'all' ? excludeTypesFromSettings(getState) @@ -205,7 +204,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) { return api(getState).get('/api/v1/notifications', { params }).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - const entries = response.data.reduce((acc, item) => { + const entries = (response.data as APIEntity[]).reduce((acc, item) => { if (item.account?.id) { acc.accounts[item.account.id] = item.account; } @@ -233,34 +232,27 @@ export function expandNotifications({ maxId } = {}, done = noOp) { done(); }); }; -} -export function expandNotificationsRequest(isLoadingMore) { - return { - type: NOTIFICATIONS_EXPAND_REQUEST, - skipLoading: !isLoadingMore, - }; -} +const expandNotificationsRequest = (isLoadingMore: boolean) => ({ + type: NOTIFICATIONS_EXPAND_REQUEST, + skipLoading: !isLoadingMore, +}); -export function expandNotificationsSuccess(notifications, next, isLoadingMore) { - return { - type: NOTIFICATIONS_EXPAND_SUCCESS, - notifications, - next, - skipLoading: !isLoadingMore, - }; -} +const expandNotificationsSuccess = (notifications: APIEntity[], next: string | null, isLoadingMore: boolean) => ({ + type: NOTIFICATIONS_EXPAND_SUCCESS, + notifications, + next, + skipLoading: !isLoadingMore, +}); -export function expandNotificationsFail(error, isLoadingMore) { - return { - type: NOTIFICATIONS_EXPAND_FAIL, - error, - skipLoading: !isLoadingMore, - }; -} +const expandNotificationsFail = (error: AxiosError, isLoadingMore: boolean) => ({ + type: NOTIFICATIONS_EXPAND_FAIL, + error, + skipLoading: !isLoadingMore, +}); -export function clearNotifications() { - return (dispatch, getState) => { +const clearNotifications = () => + (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; dispatch({ @@ -269,20 +261,18 @@ export function clearNotifications() { api(getState).post('/api/v1/notifications/clear'); }; -} -export function scrollTopNotifications(top) { - return (dispatch, getState) => { +const scrollTopNotifications = (top: boolean) => + (dispatch: AppDispatch) => { dispatch({ type: NOTIFICATIONS_SCROLL_TOP, top, }); dispatch(markReadNotifications()); }; -} -export function setFilter(filterType) { - return dispatch => { +const setFilter = (filterType: string) => + (dispatch: AppDispatch) => { dispatch({ type: NOTIFICATIONS_FILTER_SET, path: ['notifications', 'quickFilter', 'active'], @@ -291,25 +281,23 @@ export function setFilter(filterType) { dispatch(expandNotifications()); dispatch(saveSettings()); }; -} // Of course Markers don't work properly in Pleroma. // https://git.pleroma.social/pleroma/pleroma/-/issues/2769 -export function markReadPleroma(max_id) { - return (dispatch, getState) => { +const markReadPleroma = (max_id: string | number) => + (dispatch: AppDispatch, getState: () => RootState) => { return api(getState).post('/api/v1/pleroma/notifications/read', { max_id }); }; -} -export function markReadNotifications() { - return (dispatch, getState) => { +const markReadNotifications = () => + (dispatch: AppDispatch, getState: () => RootState) => { if (!isLoggedIn(getState)) return; const state = getState(); - const instance = state.get('instance'); - const topNotificationId = state.getIn(['notifications', 'items'], ImmutableOrderedMap()).first(ImmutableMap()).get('id'); - const lastReadId = state.getIn(['notifications', 'lastRead']); - const v = parseVersion(instance.get('version')); + const instance = state.instance; + const topNotificationId = state.notifications.get('items').first(ImmutableMap()).get('id'); + const lastReadId = state.notifications.get('lastRead'); + const v = parseVersion(instance.version); if (!(topNotificationId && topNotificationId > lastReadId)) return; @@ -325,4 +313,32 @@ export function markReadNotifications() { dispatch(markReadPleroma(topNotificationId)); } }; -} + +export { + NOTIFICATIONS_UPDATE, + NOTIFICATIONS_UPDATE_NOOP, + NOTIFICATIONS_UPDATE_QUEUE, + NOTIFICATIONS_DEQUEUE, + NOTIFICATIONS_EXPAND_REQUEST, + NOTIFICATIONS_EXPAND_SUCCESS, + NOTIFICATIONS_EXPAND_FAIL, + NOTIFICATIONS_FILTER_SET, + NOTIFICATIONS_CLEAR, + NOTIFICATIONS_SCROLL_TOP, + NOTIFICATIONS_MARK_READ_REQUEST, + NOTIFICATIONS_MARK_READ_SUCCESS, + NOTIFICATIONS_MARK_READ_FAIL, + MAX_QUEUED_NOTIFICATIONS, + updateNotifications, + updateNotificationsQueue, + dequeueNotifications, + expandNotifications, + expandNotificationsRequest, + expandNotificationsSuccess, + expandNotificationsFail, + clearNotifications, + scrollTopNotifications, + setFilter, + markReadPleroma, + markReadNotifications, +}; diff --git a/app/soapbox/actions/preload.js b/app/soapbox/actions/preload.ts similarity index 51% rename from app/soapbox/actions/preload.js rename to app/soapbox/actions/preload.ts index abde36da6..07b0aa0bb 100644 --- a/app/soapbox/actions/preload.js +++ b/app/soapbox/actions/preload.ts @@ -3,32 +3,34 @@ import mapValues from 'lodash/mapValues'; import { verifyCredentials } from './auth'; import { importFetchedAccounts } from './importer'; -export const PLEROMA_PRELOAD_IMPORT = 'PLEROMA_PRELOAD_IMPORT'; -export const MASTODON_PRELOAD_IMPORT = 'MASTODON_PRELOAD_IMPORT'; +import type { AppDispatch } from 'soapbox/store'; + +const PLEROMA_PRELOAD_IMPORT = 'PLEROMA_PRELOAD_IMPORT'; +const MASTODON_PRELOAD_IMPORT = 'MASTODON_PRELOAD_IMPORT'; // https://git.pleroma.social/pleroma/pleroma-fe/-/merge_requests/1176/diffs -const decodeUTF8Base64 = data => { +const decodeUTF8Base64 = (data: string) => { const rawData = atob(data); const array = Uint8Array.from(rawData.split('').map((char) => char.charCodeAt(0))); const text = new TextDecoder().decode(array); return text; }; -const decodePleromaData = data => { +const decodePleromaData = (data: Record) => { return mapValues(data, base64string => JSON.parse(decodeUTF8Base64(base64string))); }; -const pleromaDecoder = json => decodePleromaData(JSON.parse(json)); +const pleromaDecoder = (json: string) => decodePleromaData(JSON.parse(json)); // This will throw if it fails. // Should be called inside a try-catch. -const decodeFromMarkup = (elementId, decoder) => { - const { textContent } = document.getElementById(elementId); - return decoder(textContent); +const decodeFromMarkup = (elementId: string, decoder: (json: string) => Record) => { + const { textContent } = document.getElementById(elementId)!; + return decoder(textContent as string); }; -function preloadFromMarkup(elementId, decoder, action) { - return (dispatch, getState) => { +const preloadFromMarkup = (elementId: string, decoder: (json: string) => Record, action: (data: Record) => any) => + (dispatch: AppDispatch) => { try { const data = decodeFromMarkup(elementId, decoder); dispatch(action(data)); @@ -36,24 +38,20 @@ function preloadFromMarkup(elementId, decoder, action) { // Do nothing } }; -} -export function preload() { - return (dispatch, getState) => { +const preload = () => + (dispatch: AppDispatch) => { dispatch(preloadFromMarkup('initial-results', pleromaDecoder, preloadPleroma)); dispatch(preloadFromMarkup('initial-state', JSON.parse, preloadMastodon)); }; -} -export function preloadPleroma(data) { - return { - type: PLEROMA_PRELOAD_IMPORT, - data, - }; -} +const preloadPleroma = (data: Record) => ({ + type: PLEROMA_PRELOAD_IMPORT, + data, +}); -export function preloadMastodon(data) { - return (dispatch, getState) => { +const preloadMastodon = (data: Record) => + (dispatch: AppDispatch) => { const { me, access_token } = data.meta; const { url } = data.accounts[me]; @@ -61,4 +59,11 @@ export function preloadMastodon(data) { dispatch(verifyCredentials(access_token, url)); dispatch({ type: MASTODON_PRELOAD_IMPORT, data }); }; -} + +export { + PLEROMA_PRELOAD_IMPORT, + MASTODON_PRELOAD_IMPORT, + preload, + preloadPleroma, + preloadMastodon, +}; diff --git a/app/soapbox/actions/security.js b/app/soapbox/actions/security.ts similarity index 53% rename from app/soapbox/actions/security.js rename to app/soapbox/actions/security.ts index 454742c96..430691a06 100644 --- a/app/soapbox/actions/security.js +++ b/app/soapbox/actions/security.ts @@ -12,62 +12,62 @@ import api from '../api'; import { AUTH_LOGGED_OUT, messages } from './auth'; -export const FETCH_TOKENS_REQUEST = 'FETCH_TOKENS_REQUEST'; -export const FETCH_TOKENS_SUCCESS = 'FETCH_TOKENS_SUCCESS'; -export const FETCH_TOKENS_FAIL = 'FETCH_TOKENS_FAIL'; +import type { AppDispatch, RootState } from 'soapbox/store'; -export const REVOKE_TOKEN_REQUEST = 'REVOKE_TOKEN_REQUEST'; -export const REVOKE_TOKEN_SUCCESS = 'REVOKE_TOKEN_SUCCESS'; -export const REVOKE_TOKEN_FAIL = 'REVOKE_TOKEN_FAIL'; +const FETCH_TOKENS_REQUEST = 'FETCH_TOKENS_REQUEST'; +const FETCH_TOKENS_SUCCESS = 'FETCH_TOKENS_SUCCESS'; +const FETCH_TOKENS_FAIL = 'FETCH_TOKENS_FAIL'; -export const RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST'; -export const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; -export const RESET_PASSWORD_FAIL = 'RESET_PASSWORD_FAIL'; +const REVOKE_TOKEN_REQUEST = 'REVOKE_TOKEN_REQUEST'; +const REVOKE_TOKEN_SUCCESS = 'REVOKE_TOKEN_SUCCESS'; +const REVOKE_TOKEN_FAIL = 'REVOKE_TOKEN_FAIL'; -export const RESET_PASSWORD_CONFIRM_REQUEST = 'RESET_PASSWORD_CONFIRM_REQUEST'; -export const RESET_PASSWORD_CONFIRM_SUCCESS = 'RESET_PASSWORD_CONFIRM_SUCCESS'; -export const RESET_PASSWORD_CONFIRM_FAIL = 'RESET_PASSWORD_CONFIRM_FAIL'; +const RESET_PASSWORD_REQUEST = 'RESET_PASSWORD_REQUEST'; +const RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS'; +const RESET_PASSWORD_FAIL = 'RESET_PASSWORD_FAIL'; -export const CHANGE_PASSWORD_REQUEST = 'CHANGE_PASSWORD_REQUEST'; -export const CHANGE_PASSWORD_SUCCESS = 'CHANGE_PASSWORD_SUCCESS'; -export const CHANGE_PASSWORD_FAIL = 'CHANGE_PASSWORD_FAIL'; +const RESET_PASSWORD_CONFIRM_REQUEST = 'RESET_PASSWORD_CONFIRM_REQUEST'; +const RESET_PASSWORD_CONFIRM_SUCCESS = 'RESET_PASSWORD_CONFIRM_SUCCESS'; +const RESET_PASSWORD_CONFIRM_FAIL = 'RESET_PASSWORD_CONFIRM_FAIL'; -export const CHANGE_EMAIL_REQUEST = 'CHANGE_EMAIL_REQUEST'; -export const CHANGE_EMAIL_SUCCESS = 'CHANGE_EMAIL_SUCCESS'; -export const CHANGE_EMAIL_FAIL = 'CHANGE_EMAIL_FAIL'; +const CHANGE_PASSWORD_REQUEST = 'CHANGE_PASSWORD_REQUEST'; +const CHANGE_PASSWORD_SUCCESS = 'CHANGE_PASSWORD_SUCCESS'; +const CHANGE_PASSWORD_FAIL = 'CHANGE_PASSWORD_FAIL'; -export const DELETE_ACCOUNT_REQUEST = 'DELETE_ACCOUNT_REQUEST'; -export const DELETE_ACCOUNT_SUCCESS = 'DELETE_ACCOUNT_SUCCESS'; -export const DELETE_ACCOUNT_FAIL = 'DELETE_ACCOUNT_FAIL'; +const CHANGE_EMAIL_REQUEST = 'CHANGE_EMAIL_REQUEST'; +const CHANGE_EMAIL_SUCCESS = 'CHANGE_EMAIL_SUCCESS'; +const CHANGE_EMAIL_FAIL = 'CHANGE_EMAIL_FAIL'; -export const MOVE_ACCOUNT_REQUEST = 'MOVE_ACCOUNT_REQUEST'; -export const MOVE_ACCOUNT_SUCCESS = 'MOVE_ACCOUNT_SUCCESS'; -export const MOVE_ACCOUNT_FAIL = 'MOVE_ACCOUNT_FAIL'; +const DELETE_ACCOUNT_REQUEST = 'DELETE_ACCOUNT_REQUEST'; +const DELETE_ACCOUNT_SUCCESS = 'DELETE_ACCOUNT_SUCCESS'; +const DELETE_ACCOUNT_FAIL = 'DELETE_ACCOUNT_FAIL'; -export function fetchOAuthTokens() { - return (dispatch, getState) => { +const MOVE_ACCOUNT_REQUEST = 'MOVE_ACCOUNT_REQUEST'; +const MOVE_ACCOUNT_SUCCESS = 'MOVE_ACCOUNT_SUCCESS'; +const MOVE_ACCOUNT_FAIL = 'MOVE_ACCOUNT_FAIL'; + +const fetchOAuthTokens = () => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: FETCH_TOKENS_REQUEST }); return api(getState).get('/api/oauth_tokens.json').then(({ data: tokens }) => { dispatch({ type: FETCH_TOKENS_SUCCESS, tokens }); - }).catch(error => { + }).catch(() => { dispatch({ type: FETCH_TOKENS_FAIL }); }); }; -} -export function revokeOAuthTokenById(id) { - return (dispatch, getState) => { +const revokeOAuthTokenById = (id: number) => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: REVOKE_TOKEN_REQUEST, id }); return api(getState).delete(`/api/oauth_tokens/${id}`).then(() => { dispatch({ type: REVOKE_TOKEN_SUCCESS, id }); - }).catch(error => { + }).catch(() => { dispatch({ type: REVOKE_TOKEN_FAIL, id }); }); }; -} -export function changePassword(oldPassword, newPassword, confirmation) { - return (dispatch, getState) => { +const changePassword = (oldPassword: string, newPassword: string, confirmation: string) => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: CHANGE_PASSWORD_REQUEST }); return api(getState).post('/api/pleroma/change_password', { password: oldPassword, @@ -81,10 +81,9 @@ export function changePassword(oldPassword, newPassword, confirmation) { throw error; }); }; -} -export function resetPassword(usernameOrEmail) { - return (dispatch, getState) => { +const resetPassword = (usernameOrEmail: string) => + (dispatch: AppDispatch, getState: () => RootState) => { const state = getState(); const v = parseVersion(state.instance.version); @@ -107,10 +106,9 @@ export function resetPassword(usernameOrEmail) { throw error; }); }; -} -export function resetPasswordConfirm(password, token) { - return (dispatch, getState) => { +const resetPasswordConfirm = (password: string, token: string) => + (dispatch: AppDispatch, getState: () => RootState) => { const params = { password, reset_password_token: token }; dispatch({ type: RESET_PASSWORD_CONFIRM_REQUEST }); @@ -121,10 +119,9 @@ export function resetPasswordConfirm(password, token) { throw error; }); }; -} -export function changeEmail(email, password) { - return (dispatch, getState) => { +const changeEmail = (email: string, password: string) => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: CHANGE_EMAIL_REQUEST, email }); return api(getState).post('/api/pleroma/change_email', { email, @@ -137,16 +134,13 @@ export function changeEmail(email, password) { throw error; }); }; -} -export function confirmChangedEmail(token) { - return (_dispatch, getState) => { - return api(getState).get(`/api/v1/truth/email/confirm?confirmation_token=${token}`); - }; -} +const confirmChangedEmail = (token: string) => + (_dispatch: AppDispatch, getState: () => RootState) => + api(getState).get(`/api/v1/truth/email/confirm?confirmation_token=${token}`); -export function deleteAccount(intl, password) { - return (dispatch, getState) => { +const deleteAccount = (password: string) => + (dispatch: AppDispatch, getState: () => RootState) => { const account = getLoggedInAccount(getState()); dispatch({ type: DELETE_ACCOUNT_REQUEST }); @@ -156,16 +150,15 @@ export function deleteAccount(intl, password) { if (response.data.error) throw response.data.error; // This endpoint returns HTTP 200 even on failure dispatch({ type: DELETE_ACCOUNT_SUCCESS, response }); dispatch({ type: AUTH_LOGGED_OUT, account }); - dispatch(snackbar.success(intl.formatMessage(messages.loggedOut))); + dispatch(snackbar.success(messages.loggedOut)); }).catch(error => { dispatch({ type: DELETE_ACCOUNT_FAIL, error, skipAlert: true }); throw error; }); }; -} -export function moveAccount(targetAccount, password) { - return (dispatch, getState) => { +const moveAccount = (targetAccount: string, password: string) => + (dispatch: AppDispatch, getState: () => RootState) => { dispatch({ type: MOVE_ACCOUNT_REQUEST }); return api(getState).post('/api/pleroma/move_account', { password, @@ -178,4 +171,39 @@ export function moveAccount(targetAccount, password) { throw error; }); }; -} + +export { + FETCH_TOKENS_REQUEST, + FETCH_TOKENS_SUCCESS, + FETCH_TOKENS_FAIL, + REVOKE_TOKEN_REQUEST, + REVOKE_TOKEN_SUCCESS, + REVOKE_TOKEN_FAIL, + RESET_PASSWORD_REQUEST, + RESET_PASSWORD_SUCCESS, + RESET_PASSWORD_FAIL, + RESET_PASSWORD_CONFIRM_REQUEST, + RESET_PASSWORD_CONFIRM_SUCCESS, + RESET_PASSWORD_CONFIRM_FAIL, + CHANGE_PASSWORD_REQUEST, + CHANGE_PASSWORD_SUCCESS, + CHANGE_PASSWORD_FAIL, + CHANGE_EMAIL_REQUEST, + CHANGE_EMAIL_SUCCESS, + CHANGE_EMAIL_FAIL, + DELETE_ACCOUNT_REQUEST, + DELETE_ACCOUNT_SUCCESS, + DELETE_ACCOUNT_FAIL, + MOVE_ACCOUNT_REQUEST, + MOVE_ACCOUNT_SUCCESS, + MOVE_ACCOUNT_FAIL, + fetchOAuthTokens, + revokeOAuthTokenById, + changePassword, + resetPassword, + resetPasswordConfirm, + changeEmail, + confirmChangedEmail, + deleteAccount, + moveAccount, +}; diff --git a/app/soapbox/actions/soapbox.js b/app/soapbox/actions/soapbox.ts similarity index 55% rename from app/soapbox/actions/soapbox.js rename to app/soapbox/actions/soapbox.ts index 869012527..89d3080d8 100644 --- a/app/soapbox/actions/soapbox.js +++ b/app/soapbox/actions/soapbox.ts @@ -8,16 +8,20 @@ import { getFeatures } from 'soapbox/utils/features'; import api, { staticClient } from '../api'; -export const SOAPBOX_CONFIG_REQUEST_SUCCESS = 'SOAPBOX_CONFIG_REQUEST_SUCCESS'; -export const SOAPBOX_CONFIG_REQUEST_FAIL = 'SOAPBOX_CONFIG_REQUEST_FAIL'; +import type { AxiosError } from 'axios'; +import type { AppDispatch, RootState } from 'soapbox/store'; +import type { APIEntity } from 'soapbox/types/entities'; -export const SOAPBOX_CONFIG_REMEMBER_REQUEST = 'SOAPBOX_CONFIG_REMEMBER_REQUEST'; -export const SOAPBOX_CONFIG_REMEMBER_SUCCESS = 'SOAPBOX_CONFIG_REMEMBER_SUCCESS'; -export const SOAPBOX_CONFIG_REMEMBER_FAIL = 'SOAPBOX_CONFIG_REMEMBER_FAIL'; +const SOAPBOX_CONFIG_REQUEST_SUCCESS = 'SOAPBOX_CONFIG_REQUEST_SUCCESS'; +const SOAPBOX_CONFIG_REQUEST_FAIL = 'SOAPBOX_CONFIG_REQUEST_FAIL'; -export const getSoapboxConfig = createSelector([ - state => state.soapbox, - state => getFeatures(state.instance), +const SOAPBOX_CONFIG_REMEMBER_REQUEST = 'SOAPBOX_CONFIG_REMEMBER_REQUEST'; +const SOAPBOX_CONFIG_REMEMBER_SUCCESS = 'SOAPBOX_CONFIG_REMEMBER_SUCCESS'; +const SOAPBOX_CONFIG_REMEMBER_FAIL = 'SOAPBOX_CONFIG_REMEMBER_FAIL'; + +const getSoapboxConfig = createSelector([ + (state: RootState) => state.soapbox, + (state: RootState) => getFeatures(state.instance), ], (soapbox, features) => { // Do some additional normalization with the state return normalizeSoapboxConfig(soapbox).withMutations(soapboxConfig => { @@ -35,8 +39,8 @@ export const getSoapboxConfig = createSelector([ }); }); -export function rememberSoapboxConfig(host) { - return (dispatch, getState) => { +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 }); @@ -45,19 +49,16 @@ export function rememberSoapboxConfig(host) { dispatch({ type: SOAPBOX_CONFIG_REMEMBER_FAIL, host, error, skipAlert: true }); }); }; -} -export function fetchFrontendConfigurations() { - return (dispatch, getState) => { - return api(getState) +const fetchFrontendConfigurations = () => + (dispatch: AppDispatch, getState: () => RootState) => + api(getState) .get('/api/pleroma/frontend_configurations') .then(({ data }) => data); - }; -} /** Conditionally fetches Soapbox config depending on backend features */ -export function fetchSoapboxConfig(host) { - return (dispatch, getState) => { +const fetchSoapboxConfig = (host: string | null) => + (dispatch: AppDispatch, getState: () => RootState) => { const features = getFeatures(getState().instance); if (features.frontendConfigurations) { @@ -73,32 +74,28 @@ export function fetchSoapboxConfig(host) { return dispatch(fetchSoapboxJson(host)); } }; -} /** Tries to remember the config from browser storage before fetching it */ -export function loadSoapboxConfig() { - return (dispatch, getState) => { +const loadSoapboxConfig = () => + (dispatch: AppDispatch, getState: () => RootState) => { const host = getHost(getState()); - return dispatch(rememberSoapboxConfig(host)).then(() => { - return dispatch(fetchSoapboxConfig(host)); - }); + return dispatch(rememberSoapboxConfig(host)).then(() => + dispatch(fetchSoapboxConfig(host)), + ); }; -} -export function fetchSoapboxJson(host) { - return (dispatch, getState) => { - return staticClient.get('/instance/soapbox.json').then(({ data }) => { +const fetchSoapboxJson = (host: string | null) => + (dispatch: AppDispatch) => + staticClient.get('/instance/soapbox.json').then(({ data }) => { if (!isObject(data)) throw 'soapbox.json failed'; dispatch(importSoapboxConfig(data, host)); return data; }).catch(error => { dispatch(soapboxConfigFail(error, host)); }); - }; -} -export function importSoapboxConfig(soapboxConfig, host) { +const importSoapboxConfig = (soapboxConfig: APIEntity, host: string | null) => { if (!soapboxConfig.brandColor) { soapboxConfig.brandColor = '#0482d8'; } @@ -107,18 +104,30 @@ export function importSoapboxConfig(soapboxConfig, host) { soapboxConfig, host, }; -} +}; -export function soapboxConfigFail(error, host) { - return { - type: SOAPBOX_CONFIG_REQUEST_FAIL, - error, - skipAlert: true, - host, - }; -} +const soapboxConfigFail = (error: AxiosError, host: string | null) => ({ + type: SOAPBOX_CONFIG_REQUEST_FAIL, + error, + skipAlert: true, + host, +}); // https://stackoverflow.com/a/46663081 -function isObject(o) { - return o instanceof Object && o.constructor === Object; -} +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, + fetchSoapboxConfig, + loadSoapboxConfig, + fetchSoapboxJson, + importSoapboxConfig, + soapboxConfigFail, +}; diff --git a/app/soapbox/components/hashtag.tsx b/app/soapbox/components/hashtag.tsx index ff2156a1f..6c1440c9b 100644 --- a/app/soapbox/components/hashtag.tsx +++ b/app/soapbox/components/hashtag.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { useSelector } from 'react-redux'; import { Sparklines, SparklinesCurve } from 'react-sparklines'; import { getSoapboxConfig } from 'soapbox/actions/soapbox'; +import { useAppSelector } from 'soapbox/hooks'; import { shortNumberFormat } from '../utils/numbers'; @@ -18,7 +18,7 @@ interface IHashtag { const Hashtag: React.FC = ({ hashtag }) => { const count = Number(hashtag.history?.get(0)?.accounts); - const brandColor = useSelector((state) => getSoapboxConfig(state).brandColor); + const brandColor = useAppSelector((state) => getSoapboxConfig(state).brandColor); return ( diff --git a/app/soapbox/features/aliases/components/account.tsx b/app/soapbox/features/aliases/components/account.tsx index 8fe0a64a7..adcd1b072 100644 --- a/app/soapbox/features/aliases/components/account.tsx +++ b/app/soapbox/features/aliases/components/account.tsx @@ -39,7 +39,7 @@ const Account: React.FC = ({ accountId, aliases }) => { }); const me = useAppSelector((state) => state.me); - const handleOnAdd = () => dispatch(addToAliases(account)); + const handleOnAdd = () => dispatch(addToAliases(account!)); if (!account) return null; diff --git a/app/soapbox/features/aliases/index.tsx b/app/soapbox/features/aliases/index.tsx index 20ced8951..0e765339e 100644 --- a/app/soapbox/features/aliases/index.tsx +++ b/app/soapbox/features/aliases/index.tsx @@ -46,7 +46,7 @@ const Aliases = () => { }, []); const handleFilterDelete: React.MouseEventHandler = e => { - dispatch(removeFromAliases(e.currentTarget.dataset.value)); + dispatch(removeFromAliases(e.currentTarget.dataset.value as string)); }; const emptyMessage = ; diff --git a/app/soapbox/features/auth_login/components/password_reset_confirm.tsx b/app/soapbox/features/auth_login/components/password_reset_confirm.tsx index a7709834f..b18fbc8e3 100644 --- a/app/soapbox/features/auth_login/components/password_reset_confirm.tsx +++ b/app/soapbox/features/auth_login/components/password_reset_confirm.tsx @@ -35,7 +35,7 @@ const PasswordResetConfirm = () => { event.preventDefault(); setStatus(Statuses.LOADING); - dispatch(resetPasswordConfirm(password, token)) + dispatch(resetPasswordConfirm(password, token as string)) .then(() => setStatus(Statuses.SUCCESS)) .catch(() => setStatus(Statuses.FAIL)); }, [password]); diff --git a/app/soapbox/features/delete_account/index.tsx b/app/soapbox/features/delete_account/index.tsx index af4282aef..1c614e440 100644 --- a/app/soapbox/features/delete_account/index.tsx +++ b/app/soapbox/features/delete_account/index.tsx @@ -32,7 +32,7 @@ const DeleteAccount = () => { const handleSubmit = React.useCallback(() => { setLoading(true); - dispatch(deleteAccount(intl, password)).then(() => { + dispatch(deleteAccount(password)).then(() => { setPassword(''); dispatch(snackbar.success(intl.formatMessage(messages.deleteAccountSuccess))); }).finally(() => { diff --git a/app/soapbox/features/status/index.tsx b/app/soapbox/features/status/index.tsx index 313bd3625..608278d23 100644 --- a/app/soapbox/features/status/index.tsx +++ b/app/soapbox/features/status/index.tsx @@ -432,12 +432,12 @@ class Status extends ImmutablePureComponent { handleDeactivateUser = (status: StatusEntity) => { const { dispatch, intl } = this.props; - dispatch(deactivateUserModal(intl, status.getIn(['account', 'id']))); + dispatch(deactivateUserModal(intl, status.getIn(['account', 'id']) as string)); } handleDeleteUser = (status: StatusEntity) => { const { dispatch, intl } = this.props; - dispatch(deleteUserModal(intl, status.getIn(['account', 'id']))); + dispatch(deleteUserModal(intl, status.getIn(['account', 'id']) as string)); } handleToggleStatusSensitivity = (status: StatusEntity) => { diff --git a/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx b/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx index 7c3499bd1..cf4748a63 100644 --- a/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx +++ b/app/soapbox/features/ui/components/modals/report-modal/steps/other-actions-step.tsx @@ -33,7 +33,7 @@ const OtherActionsStep = ({ account }: IOtherActionsStep) => { const statusIds = useAppSelector((state) => OrderedSet(state.timelines.getIn([`account:${account.id}:with_replies`, 'items'])).union(state.reports.new.status_ids) as OrderedSet); const isBlocked = useAppSelector((state) => state.reports.new.block); const isForward = useAppSelector((state) => state.reports.new.forward); - const canForward = isRemote(account as any) && features.federating; + const canForward = isRemote(account) && features.federating; const isSubmitting = useAppSelector((state) => state.reports.new.isSubmitting); const [showAdditionalStatuses, setShowAdditionalStatuses] = useState(false); diff --git a/app/soapbox/features/ui/components/profile_info_panel.tsx b/app/soapbox/features/ui/components/profile_info_panel.tsx index e3d9ac8f4..22eba415a 100644 --- a/app/soapbox/features/ui/components/profile_info_panel.tsx +++ b/app/soapbox/features/ui/components/profile_info_panel.tsx @@ -172,7 +172,7 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => )}
- {isLocal(account as any) ? ( + {isLocal(account) ? ( , accountId: return Math.max(counter - items.size, 0); }; -export const isLocal = (account: ImmutableMap): boolean => { - const domain: string = account.get('acct').split('@')[1]; +export const isLocal = (account: Account): boolean => { + const domain: string = account.acct.split('@')[1]; return domain === undefined ? true : false; }; -export const isRemote = (account: ImmutableMap): boolean => !isLocal(account); +export const isRemote = (account: Account): boolean => !isLocal(account);