diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 4558a5bdd..e29631a26 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -96,7 +96,7 @@ class Api::V1::AccountsController < Api::BaseController end def account_ids - Array(params[:id]).map(&:to_i) + Array(params[:id]).uniq.map(&:to_i) end def account_params diff --git a/app/controllers/api/v1/statuses/emoji_reactions_controller.rb b/app/controllers/api/v1/statuses/emoji_reactions_controller.rb index ddb18f7e3..be1a0867a 100644 --- a/app/controllers/api/v1/statuses/emoji_reactions_controller.rb +++ b/app/controllers/api/v1/statuses/emoji_reactions_controller.rb @@ -8,13 +8,18 @@ class Api::V1::Statuses::EmojiReactionsController < Api::BaseController before_action :set_status def update - EmojiReactionService.new.call(current_account, @status, params[:id]) + if EmojiReactionService.new.call(current_account, @status, params[:id]).present? + @status = Status.include_expired.find(params[:status_id]) + end + render json: @status, serializer: REST::StatusSerializer end def destroy #UnEmojiReactionWorker.perform_async(current_account.id, @status.id) - UnEmojiReactionService.new.call(current_account, @status) + if UnEmojiReactionService.new.call(current_account, @status).present? + @status = Status.include_expired.find(params[:status_id]) + end render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, emoji_reactions_map: { @status.id => false }) end diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 7bd7b116f..ebf3f672d 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -1,10 +1,15 @@ import api, { getLinks } from '../api'; import { importFetchedAccount, importFetchedAccounts } from './importer'; +import { uniq } from '../utils/uniq'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS'; export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL'; +export const ACCOUNTS_FETCH_REQUEST = 'ACCOUNTS_FETCH_REQUEST'; +export const ACCOUNTS_FETCH_SUCCESS = 'ACCOUNTS_FETCH_SUCCESS'; +export const ACCOUNTS_FETCH_FAIL = 'ACCOUNTS_FETCH_FAIL'; + export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST'; export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS'; export const ACCOUNT_FOLLOW_FAIL = 'ACCOUNT_FOLLOW_FAIL'; @@ -125,6 +130,65 @@ export function fetchAccountFail(id, error) { }; }; +export function fetchAccountsFromStatus(status) { + return fetchAccountsFromStatuses([status]); +}; + +export function fetchAccountsFromStatuses(statuses) { + return fetchAccounts( + uniq( + statuses + .flatMap(item => item.emoji_reactions) + .flatMap(emoji_reaction => emoji_reaction.account_ids) + ) + ); +}; + +export function fetchAccounts(accountIds) { + return (dispatch, getState) => { + dispatch(fetchRelationships(accountIds)); + + const loadedAccounts = getState().get('accounts', new Map()); + const newAccountIds = Array.from(new Set(accountIds)).filter(id => loadedAccounts.get(id, null) === null); + + if (newAccountIds.length === 0) { + return; + } + + dispatch(fetchAccountsRequest(accountIds)); + + api(getState).get(`/api/v1/accounts?${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => { + dispatch(importFetchedAccounts(response.data)); + dispatch(fetchAccountsSuccess()); + }).catch(error => { + dispatch(fetchAccountsFail(accountIds, error)); + }); + }; +}; + +export function fetchAccountsRequest(accountIds) { + return { + type: ACCOUNTS_FETCH_REQUEST, + accountIds, + }; +}; + +export function fetchAccountsSuccess() { + return { + type: ACCOUNTS_FETCH_SUCCESS, + }; +}; + +export function fetchAccountsFail(accountIds, error) { + return { + type: ACCOUNTS_FETCH_FAIL, + accountIds, + error, + skipAlert: true, + skipNotFound: true, + }; +}; + export function followAccount(id, options = { reblogs: true, delivery: true }) { return (dispatch, getState) => { const alreadyFollowing = getState().getIn(['relationships', id, 'following']); @@ -681,6 +745,24 @@ export function expandSubscribeFail(id, error) { }; }; +export function fetchRelationshipsFromStatus(status) { + return fetchRelationshipsFromStatuses([status]); +}; + +export function fetchRelationshipsFromStatuses(statuses) { + return fetchRelationships( + uniq( + statuses + .map(status => status.reblog ? status.reblog.account.id : status.account.id) + .concat( + statuses + .map(status => status.quote ? status.quote.account.id : null) + ) + .filter(e => !!e) + ) + ); +}; + export function fetchRelationships(accountIds) { return (dispatch, getState) => { const loadedRelationships = getState().get('relationships'); diff --git a/app/javascript/mastodon/actions/bookmarks.js b/app/javascript/mastodon/actions/bookmarks.js index b3e0a2ab4..db63a9789 100644 --- a/app/javascript/mastodon/actions/bookmarks.js +++ b/app/javascript/mastodon/actions/bookmarks.js @@ -1,4 +1,4 @@ -import { fetchRelationships } from './accounts'; +import { fetchRelationshipsFromStatuses, fetchAccountsFromStatuses } from './accounts'; import api, { getLinks } from '../api'; import { importFetchedStatuses } from './importer'; import { uniq } from '../utils/uniq'; @@ -21,9 +21,11 @@ export function fetchBookmarkedStatuses() { api(getState).get('/api/v1/bookmarks').then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id)))); - dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(fetchBookmarkedStatusesSuccess(statuses, next ? next.uri : null)); }).catch(error => { dispatch(fetchBookmarkedStatusesFail(error)); }); @@ -63,9 +65,11 @@ export function expandBookmarkedStatuses() { api(getState).get(url).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id)))); - dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(expandBookmarkedStatusesSuccess(statuses, next ? next.uri : null)); }).catch(error => { dispatch(expandBookmarkedStatusesFail(error)); }); diff --git a/app/javascript/mastodon/actions/emoji_reactions.js b/app/javascript/mastodon/actions/emoji_reactions.js index 8ad6f586d..ed2c54b3c 100644 --- a/app/javascript/mastodon/actions/emoji_reactions.js +++ b/app/javascript/mastodon/actions/emoji_reactions.js @@ -1,4 +1,4 @@ -import { fetchRelationships } from './accounts'; +import { fetchRelationshipsFromStatuses, fetchAccountsFromStatuses } from './accounts'; import api, { getLinks } from '../api'; import { importFetchedStatuses } from './importer'; import { uniq } from '../utils/uniq'; @@ -21,9 +21,11 @@ export function fetchEmojiReactionedStatuses() { api(getState).get('/api/v1/emoji_reactions').then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id)))); - dispatch(fetchEmojiReactionedStatusesSuccess(response.data, next ? next.uri : null)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(fetchEmojiReactionedStatusesSuccess(statuses, next ? next.uri : null)); }).catch(error => { dispatch(fetchEmojiReactionedStatusesFail(error)); }); @@ -63,9 +65,11 @@ export function expandEmojiReactionedStatuses() { api(getState).get(url).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id)))); - dispatch(expandEmojiReactionedStatusesSuccess(response.data, next ? next.uri : null)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(expandEmojiReactionedStatusesSuccess(statuses, next ? next.uri : null)); }).catch(error => { dispatch(expandEmojiReactionedStatusesFail(error)); }); diff --git a/app/javascript/mastodon/actions/favourites.js b/app/javascript/mastodon/actions/favourites.js index 9b28ac4c4..248283256 100644 --- a/app/javascript/mastodon/actions/favourites.js +++ b/app/javascript/mastodon/actions/favourites.js @@ -1,4 +1,4 @@ -import { fetchRelationships } from './accounts'; +import { fetchRelationshipsFromStatuses, fetchAccountsFromStatuses } from './accounts'; import api, { getLinks } from '../api'; import { importFetchedStatuses } from './importer'; import { uniq } from '../utils/uniq'; @@ -21,9 +21,11 @@ export function fetchFavouritedStatuses() { api(getState).get('/api/v1/favourites').then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id)))); - dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(fetchFavouritedStatusesSuccess(statuses, next ? next.uri : null)); }).catch(error => { dispatch(fetchFavouritedStatusesFail(error)); }); @@ -66,9 +68,11 @@ export function expandFavouritedStatuses() { api(getState).get(url).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id)))); - dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(expandFavouritedStatusesSuccess(statuses, next ? next.uri : null)); }).catch(error => { dispatch(expandFavouritedStatusesFail(error)); }); diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index a331525d8..a3ad440ad 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -1,6 +1,6 @@ import api, { getLinks } from '../api'; import IntlMessageFormat from 'intl-messageformat'; -import { fetchRelationships } from './accounts'; +import { fetchRelationships, fetchAccounts, fetchAccountsFromStatuses } from './accounts'; import { importFetchedAccount, importFetchedAccounts, @@ -85,6 +85,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) { if (notification.status) { dispatch(importFetchedStatus(notification.status)); + dispatch(fetchAccounts(notification.status.emoji_reactions.map(emoji_reaction => emoji_reaction.account_ids).flat())); } dispatch({ @@ -169,6 +170,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) { dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent, isLoadingRecent && preferPendingItems)); fetchRelatedRelationships(dispatch, response.data); + dispatch(fetchAccountsFromStatuses(response.data.map(item => item.status).filter(status => !!status))); dispatch(submitMarkers()); }).catch(error => { dispatch(expandNotificationsFail(error, isLoadingMore)); diff --git a/app/javascript/mastodon/actions/pin_statuses.js b/app/javascript/mastodon/actions/pin_statuses.js index 77abba7b5..2d696f153 100644 --- a/app/javascript/mastodon/actions/pin_statuses.js +++ b/app/javascript/mastodon/actions/pin_statuses.js @@ -1,4 +1,5 @@ import api from '../api'; +import { fetchAccountsFromStatuses } from './accounts'; import { importFetchedStatuses } from './importer'; export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST'; @@ -13,6 +14,7 @@ export function fetchPinnedStatuses() { api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => { dispatch(importFetchedStatuses(response.data)); + dispatch(fetchAccountsFromStatuses(response.data)); dispatch(fetchPinnedStatusesSuccess(response.data, null)); }).catch(error => { dispatch(fetchPinnedStatusesFail(error)); diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js index 37560a74c..d64ad99fd 100644 --- a/app/javascript/mastodon/actions/search.js +++ b/app/javascript/mastodon/actions/search.js @@ -1,5 +1,5 @@ import api from '../api'; -import { fetchRelationships } from './accounts'; +import { fetchRelationships, fetchAccountsFromStatuses } from './accounts'; import { importFetchedAccounts, importFetchedStatuses } from './importer'; export const SEARCH_CHANGE = 'SEARCH_CHANGE'; @@ -51,6 +51,7 @@ export function submitSearch() { if (response.data.statuses) { dispatch(importFetchedStatuses(response.data.statuses)); + dispatch(fetchAccountsFromStatuses(response.data.statuses)); } dispatch(fetchSearchSuccess(response.data, value)); @@ -101,6 +102,7 @@ export const expandSearch = type => (dispatch, getState) => { if (data.statuses) { dispatch(importFetchedStatuses(data.statuses)); + dispatch(fetchAccountsFromStatuses(data.statuses)); } dispatch(expandSearchSuccess(data, value, type)); diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index 5e6cab699..003c1cc74 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -1,6 +1,7 @@ import api from '../api'; import { deleteFromTimelines } from './timelines'; +import { fetchRelationshipsFromStatus, fetchAccountsFromStatus, fetchRelationshipsFromStatuses, fetchAccountsFromStatuses } from './accounts'; import { importFetchedStatus, importFetchedStatuses, importFetchedAccount } from './importer'; import { ensureComposeIsVisible } from './compose'; @@ -54,7 +55,10 @@ export function fetchStatus(id) { dispatch(fetchStatusRequest(id, skipLoading)); api(getState).get(`/api/v1/statuses/${id}`).then(response => { - dispatch(importFetchedStatus(response.data)); + const status = response.data; + dispatch(importFetchedStatus(status)); + dispatch(fetchRelationshipsFromStatus(status)); + dispatch(fetchAccountsFromStatus(status)); dispatch(fetchStatusSuccess(skipLoading)); }).catch(error => { dispatch(fetchStatusFail(id, error, skipLoading)); @@ -141,7 +145,10 @@ export function fetchContext(id) { dispatch(fetchContextRequest(id)); api(getState).get(`/api/v1/statuses/${id}/context`).then(response => { - dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants))); + const statuses = response.data.ancestors.concat(response.data.descendants); + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants)); }).catch(error => { diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 1177cdda9..494cdd4c3 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -1,4 +1,4 @@ -import { fetchRelationships } from './accounts'; +import { fetchRelationshipsFromStatus, fetchAccountsFromStatus, fetchRelationshipsFromStatuses, fetchAccountsFromStatuses } from './accounts'; import { importFetchedStatus, importFetchedStatuses } from './importer'; import { submitMarkers } from './markers'; import api, { getLinks } from 'mastodon/api'; @@ -43,7 +43,8 @@ export function updateTimeline(timeline, status, accept) { } dispatch(importFetchedStatus(status)); - dispatch(fetchRelationships([status.reblog ? status.reblog.account.id : status.account.id, status.quote ? status.quote.account.id : null].filter(function(e){return e}))); + dispatch(fetchRelationshipsFromStatus(status)); + dispatch(fetchAccountsFromStatus(status)); const insertTimeline = timeline => { dispatch({ @@ -146,9 +147,11 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) { api(getState).get(path, { params }).then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); - dispatch(importFetchedStatuses(response.data)); - dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id).concat(response.data.map(item => item.quote ? item.quote.account.id : null)).filter(function(e){return e})))); - dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems)); + const statuses = response.data; + dispatch(importFetchedStatuses(statuses)); + dispatch(fetchRelationshipsFromStatuses(statuses)); + dispatch(fetchAccountsFromStatuses(statuses)); + dispatch(expandTimelineSuccess(timelineId, statuses, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems)); if (timelineId === 'home') { dispatch(submitMarkers());