/g, '\n\n');
@@ -140,38 +137,6 @@ export function normalizeStatusTranslation(translation, status) {
return normalTranslation;
}
-export function normalizePoll(poll, normalOldPoll) {
- const normalPoll = { ...poll };
- const emojiMap = makeEmojiMap(poll.emojis);
-
- normalPoll.options = poll.options.map((option, index) => {
- const normalOption = {
- ...option,
- voted: poll.own_votes && poll.own_votes.includes(index),
- titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap),
- };
-
- if (normalOldPoll && normalOldPoll.getIn(['options', index, 'title']) === option.title) {
- normalOption.translation = normalOldPoll.getIn(['options', index, 'translation']);
- }
-
- return normalOption;
- });
-
- return normalPoll;
-}
-
-export function normalizePollOptionTranslation(translation, poll) {
- const emojiMap = makeEmojiMap(poll.get('emojis').toJS());
-
- const normalTranslation = {
- ...translation,
- titleHtml: emojify(escapeTextContentForBrowser(translation.title), emojiMap),
- };
-
- return normalTranslation;
-}
-
export function normalizeAnnouncement(announcement) {
const normalAnnouncement = { ...announcement };
const emojiMap = makeEmojiMap(normalAnnouncement.emojis);
diff --git a/app/javascript/mastodon/actions/importer/polls.ts b/app/javascript/mastodon/actions/importer/polls.ts
new file mode 100644
index 0000000000..5bbe7d57d6
--- /dev/null
+++ b/app/javascript/mastodon/actions/importer/polls.ts
@@ -0,0 +1,7 @@
+import { createAction } from '@reduxjs/toolkit';
+
+import type { Poll } from 'mastodon/models/poll';
+
+export const importPolls = createAction<{ polls: Poll[] }>(
+ 'poll/importMultiple',
+);
diff --git a/app/javascript/mastodon/actions/lists.js b/app/javascript/mastodon/actions/lists.js
index a7cc1a9329..7b6dd22041 100644
--- a/app/javascript/mastodon/actions/lists.js
+++ b/app/javascript/mastodon/actions/lists.js
@@ -2,9 +2,6 @@
import api from '../api';
-import { showAlertForError } from './alerts';
-import { importFetchedAccounts } from './importer';
-
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
export const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL';
@@ -13,45 +10,10 @@ export const LISTS_FETCH_REQUEST = 'LISTS_FETCH_REQUEST';
export const LISTS_FETCH_SUCCESS = 'LISTS_FETCH_SUCCESS';
export const LISTS_FETCH_FAIL = 'LISTS_FETCH_FAIL';
-export const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE';
-export const LIST_EDITOR_RESET = 'LIST_EDITOR_RESET';
-export const LIST_EDITOR_SETUP = 'LIST_EDITOR_SETUP';
-
-export const LIST_CREATE_REQUEST = 'LIST_CREATE_REQUEST';
-export const LIST_CREATE_SUCCESS = 'LIST_CREATE_SUCCESS';
-export const LIST_CREATE_FAIL = 'LIST_CREATE_FAIL';
-
-export const LIST_UPDATE_REQUEST = 'LIST_UPDATE_REQUEST';
-export const LIST_UPDATE_SUCCESS = 'LIST_UPDATE_SUCCESS';
-export const LIST_UPDATE_FAIL = 'LIST_UPDATE_FAIL';
-
export const LIST_DELETE_REQUEST = 'LIST_DELETE_REQUEST';
export const LIST_DELETE_SUCCESS = 'LIST_DELETE_SUCCESS';
export const LIST_DELETE_FAIL = 'LIST_DELETE_FAIL';
-export const LIST_ACCOUNTS_FETCH_REQUEST = 'LIST_ACCOUNTS_FETCH_REQUEST';
-export const LIST_ACCOUNTS_FETCH_SUCCESS = 'LIST_ACCOUNTS_FETCH_SUCCESS';
-export const LIST_ACCOUNTS_FETCH_FAIL = 'LIST_ACCOUNTS_FETCH_FAIL';
-
-export const LIST_EDITOR_SUGGESTIONS_CHANGE = 'LIST_EDITOR_SUGGESTIONS_CHANGE';
-export const LIST_EDITOR_SUGGESTIONS_READY = 'LIST_EDITOR_SUGGESTIONS_READY';
-export const LIST_EDITOR_SUGGESTIONS_CLEAR = 'LIST_EDITOR_SUGGESTIONS_CLEAR';
-
-export const LIST_EDITOR_ADD_REQUEST = 'LIST_EDITOR_ADD_REQUEST';
-export const LIST_EDITOR_ADD_SUCCESS = 'LIST_EDITOR_ADD_SUCCESS';
-export const LIST_EDITOR_ADD_FAIL = 'LIST_EDITOR_ADD_FAIL';
-
-export const LIST_EDITOR_REMOVE_REQUEST = 'LIST_EDITOR_REMOVE_REQUEST';
-export const LIST_EDITOR_REMOVE_SUCCESS = 'LIST_EDITOR_REMOVE_SUCCESS';
-export const LIST_EDITOR_REMOVE_FAIL = 'LIST_EDITOR_REMOVE_FAIL';
-
-export const LIST_ADDER_RESET = 'LIST_ADDER_RESET';
-export const LIST_ADDER_SETUP = 'LIST_ADDER_SETUP';
-
-export const LIST_ADDER_LISTS_FETCH_REQUEST = 'LIST_ADDER_LISTS_FETCH_REQUEST';
-export const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS';
-export const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL';
-
export const fetchList = id => (dispatch, getState) => {
if (getState().getIn(['lists', id])) {
return;
@@ -102,94 +64,6 @@ export const fetchListsFail = error => ({
error,
});
-export const submitListEditor = shouldReset => (dispatch, getState) => {
- const listId = getState().getIn(['listEditor', 'listId']);
- const title = getState().getIn(['listEditor', 'title']);
-
- if (listId === null) {
- dispatch(createList(title, shouldReset));
- } else {
- dispatch(updateList(listId, title, shouldReset));
- }
-};
-
-export const setupListEditor = listId => (dispatch, getState) => {
- dispatch({
- type: LIST_EDITOR_SETUP,
- list: getState().getIn(['lists', listId]),
- });
-
- dispatch(fetchListAccounts(listId));
-};
-
-export const changeListEditorTitle = value => ({
- type: LIST_EDITOR_TITLE_CHANGE,
- value,
-});
-
-export const createList = (title, shouldReset) => (dispatch) => {
- dispatch(createListRequest());
-
- api().post('/api/v1/lists', { title }).then(({ data }) => {
- dispatch(createListSuccess(data));
-
- if (shouldReset) {
- dispatch(resetListEditor());
- }
- }).catch(err => dispatch(createListFail(err)));
-};
-
-export const createListRequest = () => ({
- type: LIST_CREATE_REQUEST,
-});
-
-export const createListSuccess = list => ({
- type: LIST_CREATE_SUCCESS,
- list,
-});
-
-export const createListFail = error => ({
- type: LIST_CREATE_FAIL,
- error,
-});
-
-export const updateList = (id, title, shouldReset, isExclusive, replies_policy, notify) => (dispatch) => {
- dispatch(updateListRequest(id));
-
- api().put(`/api/v1/lists/${id}`, {
- title,
- replies_policy,
- exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive,
- notify: typeof notify === 'undefined' ? undefined : !!notify,
- }).then(({ data }) => {
- dispatch(updateListSuccess(data));
-
- if (shouldReset) {
- dispatch(resetListEditor());
- }
- }).catch(err => dispatch(updateListFail(id, err)));
-};
-
-export const updateListRequest = id => ({
- type: LIST_UPDATE_REQUEST,
- id,
-});
-
-export const updateListSuccess = list => ({
- type: LIST_UPDATE_SUCCESS,
- list,
-});
-
-export const updateListFail = (id, error) => ({
- type: LIST_UPDATE_FAIL,
- id,
- error,
-});
-
-export const resetListEditor = () => ({
- type: LIST_EDITOR_RESET,
-});
-
export const deleteList = id => (dispatch) => {
dispatch(deleteListRequest(id));
@@ -213,166 +87,3 @@ export const deleteListFail = (id, error) => ({
id,
error,
});
-
-export const fetchListAccounts = listId => (dispatch) => {
- dispatch(fetchListAccountsRequest(listId));
-
- api().get(`/api/v1/lists/${listId}/accounts`, { params: { limit: 0 } }).then(({ data }) => {
- dispatch(importFetchedAccounts(data));
- dispatch(fetchListAccountsSuccess(listId, data));
- }).catch(err => dispatch(fetchListAccountsFail(listId, err)));
-};
-
-export const fetchListAccountsRequest = id => ({
- type: LIST_ACCOUNTS_FETCH_REQUEST,
- id,
-});
-
-export const fetchListAccountsSuccess = (id, accounts, next) => ({
- type: LIST_ACCOUNTS_FETCH_SUCCESS,
- id,
- accounts,
- next,
-});
-
-export const fetchListAccountsFail = (id, error) => ({
- type: LIST_ACCOUNTS_FETCH_FAIL,
- id,
- error,
-});
-
-export const fetchListSuggestions = q => (dispatch) => {
- const params = {
- q,
- resolve: false,
- following: true,
- };
-
- api().get('/api/v1/accounts/search', { params }).then(({ data }) => {
- dispatch(importFetchedAccounts(data));
- dispatch(fetchListSuggestionsReady(q, data));
- }).catch(error => dispatch(showAlertForError(error)));
-};
-
-export const fetchListSuggestionsReady = (query, accounts) => ({
- type: LIST_EDITOR_SUGGESTIONS_READY,
- query,
- accounts,
-});
-
-export const clearListSuggestions = () => ({
- type: LIST_EDITOR_SUGGESTIONS_CLEAR,
-});
-
-export const changeListSuggestions = value => ({
- type: LIST_EDITOR_SUGGESTIONS_CHANGE,
- value,
-});
-
-export const addToListEditor = accountId => (dispatch, getState) => {
- dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId));
-};
-
-export const addToList = (listId, accountId) => (dispatch) => {
- dispatch(addToListRequest(listId, accountId));
-
- api().post(`/api/v1/lists/${listId}/accounts`, { account_ids: [accountId] })
- .then(() => dispatch(addToListSuccess(listId, accountId)))
- .catch(err => dispatch(addToListFail(listId, accountId, err)));
-};
-
-export const addToListRequest = (listId, accountId) => ({
- type: LIST_EDITOR_ADD_REQUEST,
- listId,
- accountId,
-});
-
-export const addToListSuccess = (listId, accountId) => ({
- type: LIST_EDITOR_ADD_SUCCESS,
- listId,
- accountId,
-});
-
-export const addToListFail = (listId, accountId, error) => ({
- type: LIST_EDITOR_ADD_FAIL,
- listId,
- accountId,
- error,
-});
-
-export const removeFromListEditor = accountId => (dispatch, getState) => {
- dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId));
-};
-
-export const removeFromList = (listId, accountId) => (dispatch) => {
- dispatch(removeFromListRequest(listId, accountId));
-
- api().delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [accountId] } })
- .then(() => dispatch(removeFromListSuccess(listId, accountId)))
- .catch(err => dispatch(removeFromListFail(listId, accountId, err)));
-};
-
-export const removeFromListRequest = (listId, accountId) => ({
- type: LIST_EDITOR_REMOVE_REQUEST,
- listId,
- accountId,
-});
-
-export const removeFromListSuccess = (listId, accountId) => ({
- type: LIST_EDITOR_REMOVE_SUCCESS,
- listId,
- accountId,
-});
-
-export const removeFromListFail = (listId, accountId, error) => ({
- type: LIST_EDITOR_REMOVE_FAIL,
- listId,
- accountId,
- error,
-});
-
-export const resetListAdder = () => ({
- type: LIST_ADDER_RESET,
-});
-
-export const setupListAdder = accountId => (dispatch, getState) => {
- dispatch({
- type: LIST_ADDER_SETUP,
- account: getState().getIn(['accounts', accountId]),
- });
- dispatch(fetchLists());
- dispatch(fetchAccountLists(accountId));
-};
-
-export const fetchAccountLists = accountId => (dispatch) => {
- dispatch(fetchAccountListsRequest(accountId));
-
- api().get(`/api/v1/accounts/${accountId}/lists`)
- .then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data)))
- .catch(err => dispatch(fetchAccountListsFail(accountId, err)));
-};
-
-export const fetchAccountListsRequest = id => ({
- type:LIST_ADDER_LISTS_FETCH_REQUEST,
- id,
-});
-
-export const fetchAccountListsSuccess = (id, lists) => ({
- type: LIST_ADDER_LISTS_FETCH_SUCCESS,
- id,
- lists,
-});
-
-export const fetchAccountListsFail = (id, err) => ({
- type: LIST_ADDER_LISTS_FETCH_FAIL,
- id,
- err,
-});
-
-export const addToListAdder = listId => (dispatch, getState) => {
- dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])));
-};
-
-export const removeFromListAdder = listId => (dispatch, getState) => {
- dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
-};
diff --git a/app/javascript/mastodon/actions/lists_typed.ts b/app/javascript/mastodon/actions/lists_typed.ts
new file mode 100644
index 0000000000..eca051f52c
--- /dev/null
+++ b/app/javascript/mastodon/actions/lists_typed.ts
@@ -0,0 +1,17 @@
+import { apiCreate, apiUpdate } from 'mastodon/api/lists';
+import type { List } from 'mastodon/models/list';
+import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
+
+export const createList = createDataLoadingThunk(
+ 'list/create',
+ (list: Partial
) => apiCreate(list),
+);
+
+// Kmyblue tracking marker: copied antenna, circle, bookmark_category
+
+export const updateList = createDataLoadingThunk(
+ 'list/update',
+ (list: Partial
) => apiUpdate(list),
+);
+
+// Kmyblue tracking marker: copied antenna, circle, bookmark_category
diff --git a/app/javascript/mastodon/actions/modal.ts b/app/javascript/mastodon/actions/modal.ts
index ab03e46765..49af176a11 100644
--- a/app/javascript/mastodon/actions/modal.ts
+++ b/app/javascript/mastodon/actions/modal.ts
@@ -9,6 +9,7 @@ export type ModalType = keyof typeof MODAL_COMPONENTS;
interface OpenModalPayload {
modalType: ModalType;
modalProps: ModalProps;
+ previousModalProps?: ModalProps;
}
export const openModal = createAction
+ );
+};
diff --git a/app/javascript/mastodon/components/gifv.tsx b/app/javascript/mastodon/components/gifv.tsx
index c2be591128..8e3a434c14 100644
--- a/app/javascript/mastodon/components/gifv.tsx
+++ b/app/javascript/mastodon/components/gifv.tsx
@@ -1,70 +1,70 @@
-import { useCallback, useState } from 'react';
+import { useCallback, useState, forwardRef } from 'react';
interface Props {
src: string;
- key: string;
alt?: string;
lang?: string;
- width: number;
- height: number;
- onClick?: () => void;
+ width?: number;
+ height?: number;
+ onClick?: React.MouseEventHandler;
+ onMouseDown?: React.MouseEventHandler;
+ onTouchStart?: React.TouchEventHandler;
}
-export const GIFV: React.FC