Merge remote-tracking branch 'parent/main' into upstream-20240524

This commit is contained in:
KMY 2024-05-24 08:33:49 +09:00
commit c546939a40
213 changed files with 2260 additions and 986 deletions

View file

@ -6,7 +6,8 @@ LOCAL_HTTPS=true
# Elasticsearch # Elasticsearch
ES_PREFIX=test ES_PREFIX=test
# Required by ActiveRecord encryption feature # Secret values required by ActiveRecord encryption feature
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=fkSxKD2bF396kdQbrP1EJ7WbU7ZgNokR # Use `bin/rails db:encryption:init` to generate fresh secrets
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=r0hvVmzBVsjxC7AMlwhOzmtc36ZCOS1E ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=test_determinist_key_DO_NOT_USE_IN_PRODUCTION
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=PhdFyyfy5xJ7WVd2lWBpcPScRQHzRTNr ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=test_salt_DO_NOT_USE_IN_PRODUCTION
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=test_primary_key_DO_NOT_USE_IN_PRODUCTION

View file

@ -10,35 +10,35 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.1.3.2) actioncable (7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
actionmailbox (7.1.3.2) actionmailbox (7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
activejob (= 7.1.3.2) activejob (= 7.1.3.3)
activerecord (= 7.1.3.2) activerecord (= 7.1.3.3)
activestorage (= 7.1.3.2) activestorage (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.1.3.2) actionmailer (7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
actionview (= 7.1.3.2) actionview (= 7.1.3.3)
activejob (= 7.1.3.2) activejob (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
actionpack (7.1.3.2) actionpack (7.1.3.3)
actionview (= 7.1.3.2) actionview (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
racc racc
rack (>= 2.2.4) rack (>= 2.2.4)
@ -46,15 +46,15 @@ GEM
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6) rails-html-sanitizer (~> 1.6)
actiontext (7.1.3.2) actiontext (7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
activerecord (= 7.1.3.2) activerecord (= 7.1.3.3)
activestorage (= 7.1.3.2) activestorage (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.1.3.2) actionview (7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.11) erubi (~> 1.11)
rails-dom-testing (~> 2.2) rails-dom-testing (~> 2.2)
@ -64,22 +64,22 @@ GEM
activemodel (>= 4.1) activemodel (>= 4.1)
case_transform (>= 0.2) case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3) jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.1.3.2) activejob (7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.1.3.2) activemodel (7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
activerecord (7.1.3.2) activerecord (7.1.3.3)
activemodel (= 7.1.3.2) activemodel (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
timeout (>= 0.4.0) timeout (>= 0.4.0)
activestorage (7.1.3.2) activestorage (7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
activejob (= 7.1.3.2) activejob (= 7.1.3.3)
activerecord (= 7.1.3.2) activerecord (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
marcel (~> 1.0) marcel (~> 1.0)
activesupport (7.1.3.2) activesupport (7.1.3.3)
base64 base64
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
@ -425,7 +425,7 @@ GEM
mime-types-data (3.2024.0507) mime-types-data (3.2024.0507)
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.6) mini_portile2 (2.8.6)
minitest (5.22.3) minitest (5.23.0)
msgpack (1.7.2) msgpack (1.7.2)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.4.0) multipart-post (2.4.0)
@ -444,7 +444,7 @@ GEM
timeout timeout
net-smtp (0.5.0) net-smtp (0.5.0)
net-protocol net-protocol
nio4r (2.7.1) nio4r (2.7.3)
nokogiri (1.16.5) nokogiri (1.16.5)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
@ -610,7 +610,7 @@ GEM
pundit (2.3.2) pundit (2.3.2)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
raabro (1.4.0) raabro (1.4.0)
racc (1.7.3) racc (1.8.0)
rack (2.2.9) rack (2.2.9)
rack-attack (6.7.0) rack-attack (6.7.0)
rack (>= 1.0, < 4) rack (>= 1.0, < 4)
@ -634,20 +634,20 @@ GEM
rackup (1.0.0) rackup (1.0.0)
rack (< 3) rack (< 3)
webrick webrick
rails (7.1.3.2) rails (7.1.3.3)
actioncable (= 7.1.3.2) actioncable (= 7.1.3.3)
actionmailbox (= 7.1.3.2) actionmailbox (= 7.1.3.3)
actionmailer (= 7.1.3.2) actionmailer (= 7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
actiontext (= 7.1.3.2) actiontext (= 7.1.3.3)
actionview (= 7.1.3.2) actionview (= 7.1.3.3)
activejob (= 7.1.3.2) activejob (= 7.1.3.3)
activemodel (= 7.1.3.2) activemodel (= 7.1.3.3)
activerecord (= 7.1.3.2) activerecord (= 7.1.3.3)
activestorage (= 7.1.3.2) activestorage (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.1.3.2) railties (= 7.1.3.3)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@ -662,9 +662,9 @@ GEM
rails-i18n (7.0.9) rails-i18n (7.0.9)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.1.3.2) railties (7.1.3.3)
actionpack (= 7.1.3.2) actionpack (= 7.1.3.3)
activesupport (= 7.1.3.2) activesupport (= 7.1.3.3)
irb irb
rackup (>= 1.0.0) rackup (>= 1.0.0)
rake (>= 12.2) rake (>= 12.2)
@ -685,7 +685,7 @@ GEM
redis (>= 4) redis (>= 4)
redlock (1.3.2) redlock (1.3.2)
redis (>= 3.0.0, < 6.0) redis (>= 3.0.0, < 6.0)
regexp_parser (2.9.0) regexp_parser (2.9.2)
reline (0.5.7) reline (0.5.7)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.6.0) request_store (1.6.0)
@ -746,7 +746,7 @@ GEM
rubocop-performance (1.21.0) rubocop-performance (1.21.0)
rubocop (>= 1.48.1, < 2.0) rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails (2.24.1) rubocop-rails (2.25.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
rack (>= 1.1) rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0) rubocop (>= 1.33.0, < 2.0)
@ -775,7 +775,7 @@ GEM
scenic (1.8.0) scenic (1.8.0)
activerecord (>= 4.0.0) activerecord (>= 4.0.0)
railties (>= 4.0.0) railties (>= 4.0.0)
selenium-webdriver (4.21.0) selenium-webdriver (4.21.1)
base64 (~> 0.2) base64 (~> 0.2)
rexml (~> 3.2, >= 3.2.5) rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0) rubyzip (>= 1.2.2, < 3.0)

View file

@ -142,7 +142,7 @@ module Admin
def unblock_email def unblock_email
authorize @account, :unblock_email? authorize @account, :unblock_email?
CanonicalEmailBlock.matching_account(@account).delete_all CanonicalEmailBlock.where(reference_account: @account).delete_all
log_action :unblock_email, @account log_action :unblock_email, @account

View file

@ -4,6 +4,6 @@ class Api::V1::Apps::CredentialsController < Api::BaseController
def show def show
return doorkeeper_render_error unless valid_doorkeeper_token? return doorkeeper_render_error unless valid_doorkeeper_token?
render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer, fields: %i(name website vapid_key client_id scopes) render json: doorkeeper_token.application, serializer: REST::ApplicationSerializer
end end
end end

View file

@ -5,7 +5,7 @@ class Api::V1::AppsController < Api::BaseController
def create def create
@app = Doorkeeper::Application.create!(application_options) @app = Doorkeeper::Application.create!(application_options)
render json: @app, serializer: REST::ApplicationSerializer render json: @app, serializer: REST::CredentialApplicationSerializer
end end
private private
@ -24,6 +24,6 @@ class Api::V1::AppsController < Api::BaseController
end end
def app_params def app_params
params.permit(:client_name, :redirect_uris, :scopes, :website) params.permit(:client_name, :scopes, :website, :redirect_uris, redirect_uris: [])
end end
end end

View file

@ -254,6 +254,10 @@ module ApplicationHelper
prerender_custom_emojis(html, JSON.parse([custom_emojis_hash].to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse prerender_custom_emojis(html, JSON.parse([custom_emojis_hash].to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
end end
def mascot_url
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
end
def instance_presenter def instance_presenter
@instance_presenter ||= InstancePresenter.new @instance_presenter ||= InstancePresenter.new
end end

View file

@ -1,11 +0,0 @@
# frozen_string_literal: true
module MascotHelper
def mascot_url
full_asset_url(instance_presenter.mascot&.file&.url || frontend_asset_path('images/elephant_ui_plane.svg'))
end
def instance_presenter
@instance_presenter ||= InstancePresenter.new
end
end

View file

@ -1,18 +1,10 @@
import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships'; import { apiSubmitAccountNote } from 'mastodon/api/accounts';
import { createAppAsyncThunk } from 'mastodon/store/typed_functions'; import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
import api from '../api'; export const submitAccountNote = createDataLoadingThunk(
export const submitAccountNote = createAppAsyncThunk(
'account_note/submit', 'account_note/submit',
async (args: { id: string; value: string }, { getState }) => { ({ accountId, note }: { accountId: string; note: string }) =>
const response = await api(getState).post<ApiRelationshipJSON>( apiSubmitAccountNote(accountId, note),
`/api/v1/accounts/${args.id}/note`, (relationship) => ({ relationship }),
{ { skipLoading: true },
comment: args.value,
},
);
return { relationship: response.data };
},
); );

View file

@ -76,11 +76,11 @@ export const ACCOUNT_REVEAL = 'ACCOUNT_REVEAL';
export * from './accounts_typed'; export * from './accounts_typed';
export function fetchAccount(id) { export function fetchAccount(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchRelationships([id])); dispatch(fetchRelationships([id]));
dispatch(fetchAccountRequest(id)); dispatch(fetchAccountRequest(id));
api(getState).get(`/api/v1/accounts/${id}`).then(response => { api().get(`/api/v1/accounts/${id}`).then(response => {
dispatch(importFetchedAccount(response.data)); dispatch(importFetchedAccount(response.data));
dispatch(fetchAccountSuccess()); dispatch(fetchAccountSuccess());
}).catch(error => { }).catch(error => {
@ -89,10 +89,10 @@ export function fetchAccount(id) {
}; };
} }
export const lookupAccount = acct => (dispatch, getState) => { export const lookupAccount = acct => (dispatch) => {
dispatch(lookupAccountRequest(acct)); dispatch(lookupAccountRequest(acct));
api(getState).get('/api/v1/accounts/lookup', { params: { acct } }).then(response => { api().get('/api/v1/accounts/lookup', { params: { acct } }).then(response => {
dispatch(fetchRelationships([response.data.id])); dispatch(fetchRelationships([response.data.id]));
dispatch(importFetchedAccount(response.data)); dispatch(importFetchedAccount(response.data));
dispatch(lookupAccountSuccess()); dispatch(lookupAccountSuccess());
@ -146,7 +146,7 @@ export function followAccount(id, options = { reblogs: true }) {
dispatch(followAccountRequest({ id, locked })); dispatch(followAccountRequest({ id, locked }));
api(getState).post(`/api/v1/accounts/${id}/follow`, options).then(response => { api().post(`/api/v1/accounts/${id}/follow`, options).then(response => {
dispatch(followAccountSuccess({relationship: response.data, alreadyFollowing})); dispatch(followAccountSuccess({relationship: response.data, alreadyFollowing}));
}).catch(error => { }).catch(error => {
dispatch(followAccountFail({ id, error, locked })); dispatch(followAccountFail({ id, error, locked }));
@ -158,7 +158,7 @@ export function unfollowAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(unfollowAccountRequest(id)); dispatch(unfollowAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => { api().post(`/api/v1/accounts/${id}/unfollow`).then(response => {
dispatch(unfollowAccountSuccess({relationship: response.data, statuses: getState().get('statuses')})); dispatch(unfollowAccountSuccess({relationship: response.data, statuses: getState().get('statuses')}));
}).catch(error => { }).catch(error => {
dispatch(unfollowAccountFail({ id, error })); dispatch(unfollowAccountFail({ id, error }));
@ -170,7 +170,7 @@ export function blockAccount(id) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(blockAccountRequest(id)); dispatch(blockAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/block`).then(response => { api().post(`/api/v1/accounts/${id}/block`).then(response => {
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
dispatch(blockAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') })); dispatch(blockAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') }));
}).catch(error => { }).catch(error => {
@ -180,10 +180,10 @@ export function blockAccount(id) {
} }
export function unblockAccount(id) { export function unblockAccount(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unblockAccountRequest(id)); dispatch(unblockAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => { api().post(`/api/v1/accounts/${id}/unblock`).then(response => {
dispatch(unblockAccountSuccess({ relationship: response.data })); dispatch(unblockAccountSuccess({ relationship: response.data }));
}).catch(error => { }).catch(error => {
dispatch(unblockAccountFail({ id, error })); dispatch(unblockAccountFail({ id, error }));
@ -223,7 +223,7 @@ export function muteAccount(id, notifications, duration=0) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(muteAccountRequest(id)); dispatch(muteAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications, duration }).then(response => { api().post(`/api/v1/accounts/${id}/mute`, { notifications, duration }).then(response => {
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers // Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
dispatch(muteAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') })); dispatch(muteAccountSuccess({ relationship: response.data, statuses: getState().get('statuses') }));
}).catch(error => { }).catch(error => {
@ -233,10 +233,10 @@ export function muteAccount(id, notifications, duration=0) {
} }
export function unmuteAccount(id) { export function unmuteAccount(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unmuteAccountRequest(id)); dispatch(unmuteAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/unmute`).then(response => { api().post(`/api/v1/accounts/${id}/unmute`).then(response => {
dispatch(unmuteAccountSuccess({ relationship: response.data })); dispatch(unmuteAccountSuccess({ relationship: response.data }));
}).catch(error => { }).catch(error => {
dispatch(unmuteAccountFail({ id, error })); dispatch(unmuteAccountFail({ id, error }));
@ -274,10 +274,10 @@ export function unmuteAccountFail(error) {
export function fetchFollowers(id) { export function fetchFollowers(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchFollowersRequest(id)); dispatch(fetchFollowersRequest(id));
api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => { api().get(`/api/v1/accounts/${id}/followers`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
@ -324,7 +324,7 @@ export function expandFollowers(id) {
dispatch(expandFollowersRequest(id)); dispatch(expandFollowersRequest(id));
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
@ -361,10 +361,10 @@ export function expandFollowersFail(id, error) {
} }
export function fetchFollowing(id) { export function fetchFollowing(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchFollowingRequest(id)); dispatch(fetchFollowingRequest(id));
api(getState).get(`/api/v1/accounts/${id}/following`).then(response => { api().get(`/api/v1/accounts/${id}/following`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
@ -411,7 +411,7 @@ export function expandFollowing(id) {
dispatch(expandFollowingRequest(id)); dispatch(expandFollowingRequest(id));
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
@ -460,7 +460,7 @@ export function fetchRelationships(accountIds) {
dispatch(fetchRelationshipsRequest(newAccountIds)); dispatch(fetchRelationshipsRequest(newAccountIds));
api(getState).get(`/api/v1/accounts/relationships?with_suspended=true&${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => { api().get(`/api/v1/accounts/relationships?with_suspended=true&${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
dispatch(fetchRelationshipsSuccess({ relationships: response.data })); dispatch(fetchRelationshipsSuccess({ relationships: response.data }));
}).catch(error => { }).catch(error => {
dispatch(fetchRelationshipsFail(error)); dispatch(fetchRelationshipsFail(error));
@ -486,10 +486,10 @@ export function fetchRelationshipsFail(error) {
} }
export function fetchFollowRequests() { export function fetchFollowRequests() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchFollowRequestsRequest()); dispatch(fetchFollowRequestsRequest());
api(getState).get('/api/v1/follow_requests').then(response => { api().get('/api/v1/follow_requests').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
@ -528,7 +528,7 @@ export function expandFollowRequests() {
dispatch(expandFollowRequestsRequest()); dispatch(expandFollowRequestsRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null)); dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
@ -558,10 +558,10 @@ export function expandFollowRequestsFail(error) {
} }
export function authorizeFollowRequest(id) { export function authorizeFollowRequest(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(authorizeFollowRequestRequest(id)); dispatch(authorizeFollowRequestRequest(id));
api(getState) api()
.post(`/api/v1/follow_requests/${id}/authorize`) .post(`/api/v1/follow_requests/${id}/authorize`)
.then(() => dispatch(authorizeFollowRequestSuccess({ id }))) .then(() => dispatch(authorizeFollowRequestSuccess({ id })))
.catch(error => dispatch(authorizeFollowRequestFail(id, error))); .catch(error => dispatch(authorizeFollowRequestFail(id, error)));
@ -585,10 +585,10 @@ export function authorizeFollowRequestFail(id, error) {
export function rejectFollowRequest(id) { export function rejectFollowRequest(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(rejectFollowRequestRequest(id)); dispatch(rejectFollowRequestRequest(id));
api(getState) api()
.post(`/api/v1/follow_requests/${id}/reject`) .post(`/api/v1/follow_requests/${id}/reject`)
.then(() => dispatch(rejectFollowRequestSuccess({ id }))) .then(() => dispatch(rejectFollowRequestSuccess({ id })))
.catch(error => dispatch(rejectFollowRequestFail(id, error))); .catch(error => dispatch(rejectFollowRequestFail(id, error)));
@ -611,10 +611,10 @@ export function rejectFollowRequestFail(id, error) {
} }
export function pinAccount(id) { export function pinAccount(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(pinAccountRequest(id)); dispatch(pinAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => { api().post(`/api/v1/accounts/${id}/pin`).then(response => {
dispatch(pinAccountSuccess({ relationship: response.data })); dispatch(pinAccountSuccess({ relationship: response.data }));
}).catch(error => { }).catch(error => {
dispatch(pinAccountFail(error)); dispatch(pinAccountFail(error));
@ -623,10 +623,10 @@ export function pinAccount(id) {
} }
export function unpinAccount(id) { export function unpinAccount(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unpinAccountRequest(id)); dispatch(unpinAccountRequest(id));
api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => { api().post(`/api/v1/accounts/${id}/unpin`).then(response => {
dispatch(unpinAccountSuccess({ relationship: response.data })); dispatch(unpinAccountSuccess({ relationship: response.data }));
}).catch(error => { }).catch(error => {
dispatch(unpinAccountFail(error)); dispatch(unpinAccountFail(error));
@ -662,7 +662,7 @@ export function unpinAccountFail(error) {
}; };
} }
export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch, getState) => { export const updateAccount = ({ displayName, note, avatar, header, discoverable, indexable }) => (dispatch) => {
const data = new FormData(); const data = new FormData();
data.append('display_name', displayName); data.append('display_name', displayName);
@ -672,7 +672,7 @@ export const updateAccount = ({ displayName, note, avatar, header, discoverable,
data.append('discoverable', discoverable); data.append('discoverable', discoverable);
data.append('indexable', indexable); data.append('indexable', indexable);
return api(getState).patch('/api/v1/accounts/update_credentials', data).then(response => { return api().patch('/api/v1/accounts/update_credentials', data).then(response => {
dispatch(importFetchedAccount(response.data)); dispatch(importFetchedAccount(response.data));
}); });
}; };

View file

@ -26,10 +26,10 @@ export const ANNOUNCEMENTS_TOGGLE_SHOW = 'ANNOUNCEMENTS_TOGGLE_SHOW';
const noOp = () => {}; const noOp = () => {};
export const fetchAnnouncements = (done = noOp) => (dispatch, getState) => { export const fetchAnnouncements = (done = noOp) => (dispatch) => {
dispatch(fetchAnnouncementsRequest()); dispatch(fetchAnnouncementsRequest());
api(getState).get('/api/v1/announcements').then(response => { api().get('/api/v1/announcements').then(response => {
dispatch(fetchAnnouncementsSuccess(response.data.map(x => normalizeAnnouncement(x)))); dispatch(fetchAnnouncementsSuccess(response.data.map(x => normalizeAnnouncement(x))));
}).catch(error => { }).catch(error => {
dispatch(fetchAnnouncementsFail(error)); dispatch(fetchAnnouncementsFail(error));
@ -61,10 +61,10 @@ export const updateAnnouncements = announcement => ({
announcement: normalizeAnnouncement(announcement), announcement: normalizeAnnouncement(announcement),
}); });
export const dismissAnnouncement = announcementId => (dispatch, getState) => { export const dismissAnnouncement = announcementId => (dispatch) => {
dispatch(dismissAnnouncementRequest(announcementId)); dispatch(dismissAnnouncementRequest(announcementId));
api(getState).post(`/api/v1/announcements/${announcementId}/dismiss`).then(() => { api().post(`/api/v1/announcements/${announcementId}/dismiss`).then(() => {
dispatch(dismissAnnouncementSuccess(announcementId)); dispatch(dismissAnnouncementSuccess(announcementId));
}).catch(error => { }).catch(error => {
dispatch(dismissAnnouncementFail(announcementId, error)); dispatch(dismissAnnouncementFail(announcementId, error));
@ -103,7 +103,7 @@ export const addReaction = (announcementId, name) => (dispatch, getState) => {
dispatch(addReactionRequest(announcementId, name, alreadyAdded)); dispatch(addReactionRequest(announcementId, name, alreadyAdded));
} }
api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => { api().put(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
dispatch(addReactionSuccess(announcementId, name, alreadyAdded)); dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
}).catch(err => { }).catch(err => {
if (!alreadyAdded) { if (!alreadyAdded) {
@ -134,10 +134,10 @@ export const addReactionFail = (announcementId, name, error) => ({
skipLoading: true, skipLoading: true,
}); });
export const removeReaction = (announcementId, name) => (dispatch, getState) => { export const removeReaction = (announcementId, name) => (dispatch) => {
dispatch(removeReactionRequest(announcementId, name)); dispatch(removeReactionRequest(announcementId, name));
api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => { api().delete(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
dispatch(removeReactionSuccess(announcementId, name)); dispatch(removeReactionSuccess(announcementId, name));
}).catch(err => { }).catch(err => {
dispatch(removeReactionFail(announcementId, name, err)); dispatch(removeReactionFail(announcementId, name, err));

View file

@ -13,10 +13,10 @@ export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS';
export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL'; export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
export function fetchBlocks() { export function fetchBlocks() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchBlocksRequest()); dispatch(fetchBlocksRequest());
api(getState).get('/api/v1/blocks').then(response => { api().get('/api/v1/blocks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null)); dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
@ -56,7 +56,7 @@ export function expandBlocks() {
dispatch(expandBlocksRequest()); dispatch(expandBlocksRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(expandBlocksSuccess(response.data, next ? next.uri : null)); dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));

View file

@ -20,7 +20,7 @@ export function fetchBookmarkedStatuses() {
dispatch(fetchBookmarkedStatusesRequest()); dispatch(fetchBookmarkedStatusesRequest());
api(getState).get('/api/v1/bookmarks').then(response => { api().get('/api/v1/bookmarks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
@ -61,7 +61,7 @@ export function expandBookmarkedStatuses() {
dispatch(expandBookmarkedStatusesRequest()); dispatch(expandBookmarkedStatusesRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null)); dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));

View file

@ -207,7 +207,7 @@ export function submitCompose(routerHistory) {
}); });
} }
api(getState).request({ api().request({
url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`, url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
method: statusId === null ? 'post' : 'put', method: statusId === null ? 'post' : 'put',
data: { data: {
@ -327,7 +327,7 @@ export function uploadCompose(files) {
const data = new FormData(); const data = new FormData();
data.append('file', file); data.append('file', file);
api(getState).post('/api/v2/media', data, { api().post('/api/v2/media', data, {
onUploadProgress: function({ loaded }){ onUploadProgress: function({ loaded }){
progress[i] = loaded; progress[i] = loaded;
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total)); dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
@ -348,7 +348,7 @@ export function uploadCompose(files) {
let tryCount = 1; let tryCount = 1;
const poll = () => { const poll = () => {
api(getState).get(`/api/v1/media/${data.id}`).then(response => { api().get(`/api/v1/media/${data.id}`).then(response => {
if (response.status === 200) { if (response.status === 200) {
dispatch(uploadComposeSuccess(response.data, file)); dispatch(uploadComposeSuccess(response.data, file));
} else if (response.status === 206) { } else if (response.status === 206) {
@ -370,7 +370,7 @@ export const uploadComposeProcessing = () => ({
type: COMPOSE_UPLOAD_PROCESSING, type: COMPOSE_UPLOAD_PROCESSING,
}); });
export const uploadThumbnail = (id, file) => (dispatch, getState) => { export const uploadThumbnail = (id, file) => (dispatch) => {
dispatch(uploadThumbnailRequest()); dispatch(uploadThumbnailRequest());
const total = file.size; const total = file.size;
@ -378,7 +378,7 @@ export const uploadThumbnail = (id, file) => (dispatch, getState) => {
data.append('thumbnail', file); data.append('thumbnail', file);
api(getState).put(`/api/v1/media/${id}`, data, { api().put(`/api/v1/media/${id}`, data, {
onUploadProgress: ({ loaded }) => { onUploadProgress: ({ loaded }) => {
dispatch(uploadThumbnailProgress(loaded, total)); dispatch(uploadThumbnailProgress(loaded, total));
}, },
@ -461,7 +461,7 @@ export function changeUploadCompose(id, params) {
dispatch(changeUploadComposeSuccess(data, true)); dispatch(changeUploadComposeSuccess(data, true));
} else { } else {
api(getState).put(`/api/v1/media/${id}`, params).then(response => { api().put(`/api/v1/media/${id}`, params).then(response => {
dispatch(changeUploadComposeSuccess(response.data, false)); dispatch(changeUploadComposeSuccess(response.data, false));
}).catch(error => { }).catch(error => {
dispatch(changeUploadComposeFail(id, error)); dispatch(changeUploadComposeFail(id, error));
@ -549,7 +549,7 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) =>
fetchComposeSuggestionsAccountsController = new AbortController(); fetchComposeSuggestionsAccountsController = new AbortController();
api(getState).get('/api/v1/accounts/search', { api().get('/api/v1/accounts/search', {
signal: fetchComposeSuggestionsAccountsController.signal, signal: fetchComposeSuggestionsAccountsController.signal,
params: { params: {
@ -583,7 +583,7 @@ const fetchComposeSuggestionsTags = throttle((dispatch, getState, token) => {
fetchComposeSuggestionsTagsController = new AbortController(); fetchComposeSuggestionsTagsController = new AbortController();
api(getState).get('/api/v2/search', { api().get('/api/v2/search', {
signal: fetchComposeSuggestionsTagsController.signal, signal: fetchComposeSuggestionsTagsController.signal,
params: { params: {

View file

@ -28,13 +28,13 @@ export const unmountConversations = () => ({
type: CONVERSATIONS_UNMOUNT, type: CONVERSATIONS_UNMOUNT,
}); });
export const markConversationRead = conversationId => (dispatch, getState) => { export const markConversationRead = conversationId => (dispatch) => {
dispatch({ dispatch({
type: CONVERSATIONS_READ, type: CONVERSATIONS_READ,
id: conversationId, id: conversationId,
}); });
api(getState).post(`/api/v1/conversations/${conversationId}/read`); api().post(`/api/v1/conversations/${conversationId}/read`);
}; };
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => { export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
@ -48,7 +48,7 @@ export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
const isLoadingRecent = !!params.since_id; const isLoadingRecent = !!params.since_id;
api(getState).get('/api/v1/conversations', { params }) api().get('/api/v1/conversations', { params })
.then(response => { .then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
@ -88,10 +88,10 @@ export const updateConversations = conversation => dispatch => {
}); });
}; };
export const deleteConversation = conversationId => (dispatch, getState) => { export const deleteConversation = conversationId => (dispatch) => {
dispatch(deleteConversationRequest(conversationId)); dispatch(deleteConversationRequest(conversationId));
api(getState).delete(`/api/v1/conversations/${conversationId}`) api().delete(`/api/v1/conversations/${conversationId}`)
.then(() => dispatch(deleteConversationSuccess(conversationId))) .then(() => dispatch(deleteConversationSuccess(conversationId)))
.catch(error => dispatch(deleteConversationFail(conversationId, error))); .catch(error => dispatch(deleteConversationFail(conversationId, error)));
}; };

View file

@ -5,10 +5,10 @@ export const CUSTOM_EMOJIS_FETCH_SUCCESS = 'CUSTOM_EMOJIS_FETCH_SUCCESS';
export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL'; export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL';
export function fetchCustomEmojis() { export function fetchCustomEmojis() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchCustomEmojisRequest()); dispatch(fetchCustomEmojisRequest());
api(getState).get('/api/v1/custom_emojis').then(response => { api().get('/api/v1/custom_emojis').then(response => {
dispatch(fetchCustomEmojisSuccess(response.data)); dispatch(fetchCustomEmojisSuccess(response.data));
}).catch(error => { }).catch(error => {
dispatch(fetchCustomEmojisFail(error)); dispatch(fetchCustomEmojisFail(error));

View file

@ -11,10 +11,10 @@ export const DIRECTORY_EXPAND_REQUEST = 'DIRECTORY_EXPAND_REQUEST';
export const DIRECTORY_EXPAND_SUCCESS = 'DIRECTORY_EXPAND_SUCCESS'; export const DIRECTORY_EXPAND_SUCCESS = 'DIRECTORY_EXPAND_SUCCESS';
export const DIRECTORY_EXPAND_FAIL = 'DIRECTORY_EXPAND_FAIL'; export const DIRECTORY_EXPAND_FAIL = 'DIRECTORY_EXPAND_FAIL';
export const fetchDirectory = params => (dispatch, getState) => { export const fetchDirectory = params => (dispatch) => {
dispatch(fetchDirectoryRequest()); dispatch(fetchDirectoryRequest());
api(getState).get('/api/v1/directory', { params: { ...params, limit: 20 } }).then(({ data }) => { api().get('/api/v1/directory', { params: { ...params, limit: 20 } }).then(({ data }) => {
dispatch(importFetchedAccounts(data)); dispatch(importFetchedAccounts(data));
dispatch(fetchDirectorySuccess(data)); dispatch(fetchDirectorySuccess(data));
dispatch(fetchRelationships(data.map(x => x.id))); dispatch(fetchRelationships(data.map(x => x.id)));
@ -40,7 +40,7 @@ export const expandDirectory = params => (dispatch, getState) => {
const loadedItems = getState().getIn(['user_lists', 'directory', 'items']).size; const loadedItems = getState().getIn(['user_lists', 'directory', 'items']).size;
api(getState).get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => { api().get('/api/v1/directory', { params: { ...params, offset: loadedItems, limit: 20 } }).then(({ data }) => {
dispatch(importFetchedAccounts(data)); dispatch(importFetchedAccounts(data));
dispatch(expandDirectorySuccess(data)); dispatch(expandDirectorySuccess(data));
dispatch(fetchRelationships(data.map(x => x.id))); dispatch(fetchRelationships(data.map(x => x.id)));

View file

@ -24,7 +24,7 @@ export function blockDomain(domain) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(blockDomainRequest(domain)); dispatch(blockDomainRequest(domain));
api(getState).post('/api/v1/domain_blocks', { domain }).then(() => { api().post('/api/v1/domain_blocks', { domain }).then(() => {
const at_domain = '@' + domain; const at_domain = '@' + domain;
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id')); const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
@ -54,7 +54,7 @@ export function unblockDomain(domain) {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(unblockDomainRequest(domain)); dispatch(unblockDomainRequest(domain));
api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(() => { api().delete('/api/v1/domain_blocks', { params: { domain } }).then(() => {
const at_domain = '@' + domain; const at_domain = '@' + domain;
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id')); const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
dispatch(unblockDomainSuccess({ domain, accounts })); dispatch(unblockDomainSuccess({ domain, accounts }));
@ -80,10 +80,10 @@ export function unblockDomainFail(domain, error) {
} }
export function fetchDomainBlocks() { export function fetchDomainBlocks() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchDomainBlocksRequest()); dispatch(fetchDomainBlocksRequest());
api(getState).get('/api/v1/domain_blocks').then(response => { api().get('/api/v1/domain_blocks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null)); dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null));
}).catch(err => { }).catch(err => {
@ -123,7 +123,7 @@ export function expandDomainBlocks() {
dispatch(expandDomainBlocksRequest()); dispatch(expandDomainBlocksRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null)); dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null));
}).catch(err => { }).catch(err => {

View file

@ -20,7 +20,7 @@ export function fetchFavouritedStatuses() {
dispatch(fetchFavouritedStatusesRequest()); dispatch(fetchFavouritedStatusesRequest());
api(getState).get('/api/v1/favourites').then(response => { api().get('/api/v1/favourites').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null)); dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
@ -64,7 +64,7 @@ export function expandFavouritedStatuses() {
dispatch(expandFavouritedStatusesRequest()); dispatch(expandFavouritedStatusesRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null)); dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));

View file

@ -11,7 +11,7 @@ export const fetchFeaturedTags = (id) => (dispatch, getState) => {
dispatch(fetchFeaturedTagsRequest(id)); dispatch(fetchFeaturedTagsRequest(id));
api(getState).get(`/api/v1/accounts/${id}/featured_tags`) api().get(`/api/v1/accounts/${id}/featured_tags`)
.then(({ data }) => dispatch(fetchFeaturedTagsSuccess(id, data))) .then(({ data }) => dispatch(fetchFeaturedTagsSuccess(id, data)))
.catch(err => dispatch(fetchFeaturedTagsFail(id, err))); .catch(err => dispatch(fetchFeaturedTagsFail(id, err)));
}; };

View file

@ -23,13 +23,13 @@ export const initAddFilter = (status, { contextType }) => dispatch =>
}, },
})); }));
export const fetchFilters = () => (dispatch, getState) => { export const fetchFilters = () => (dispatch) => {
dispatch({ dispatch({
type: FILTERS_FETCH_REQUEST, type: FILTERS_FETCH_REQUEST,
skipLoading: true, skipLoading: true,
}); });
api(getState) api()
.get('/api/v2/filters') .get('/api/v2/filters')
.then(({ data }) => dispatch({ .then(({ data }) => dispatch({
type: FILTERS_FETCH_SUCCESS, type: FILTERS_FETCH_SUCCESS,
@ -44,10 +44,10 @@ export const fetchFilters = () => (dispatch, getState) => {
})); }));
}; };
export const createFilterStatus = (params, onSuccess, onFail) => (dispatch, getState) => { export const createFilterStatus = (params, onSuccess, onFail) => (dispatch) => {
dispatch(createFilterStatusRequest()); dispatch(createFilterStatusRequest());
api(getState).post(`/api/v2/filters/${params.filter_id}/statuses`, params).then(response => { api().post(`/api/v2/filters/${params.filter_id}/statuses`, params).then(response => {
dispatch(createFilterStatusSuccess(response.data)); dispatch(createFilterStatusSuccess(response.data));
if (onSuccess) onSuccess(); if (onSuccess) onSuccess();
}).catch(error => { }).catch(error => {
@ -70,10 +70,10 @@ export const createFilterStatusFail = error => ({
error, error,
}); });
export const createFilter = (params, onSuccess, onFail) => (dispatch, getState) => { export const createFilter = (params, onSuccess, onFail) => (dispatch) => {
dispatch(createFilterRequest()); dispatch(createFilterRequest());
api(getState).post('/api/v2/filters', params).then(response => { api().post('/api/v2/filters', params).then(response => {
dispatch(createFilterSuccess(response.data)); dispatch(createFilterSuccess(response.data));
if (onSuccess) onSuccess(response.data); if (onSuccess) onSuccess(response.data);
}).catch(error => { }).catch(error => {

View file

@ -15,7 +15,7 @@ export const fetchHistory = statusId => (dispatch, getState) => {
dispatch(fetchHistoryRequest(statusId)); dispatch(fetchHistoryRequest(statusId));
api(getState).get(`/api/v1/statuses/${statusId}/history`).then(({ data }) => { api().get(`/api/v1/statuses/${statusId}/history`).then(({ data }) => {
dispatch(importFetchedAccounts(data.map(x => x.account))); dispatch(importFetchedAccounts(data.map(x => x.account)));
dispatch(fetchHistorySuccess(statusId, data)); dispatch(fetchHistorySuccess(statusId, data));
}).catch(error => dispatch(fetchHistoryFail(error))); }).catch(error => dispatch(fetchHistoryFail(error)));

View file

@ -3,10 +3,6 @@ import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts'; import { fetchRelationships } from './accounts';
import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer'; import { importFetchedAccounts, importFetchedStatus, importFetchedStatuses } from './importer';
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
export const REBLOG_FAIL = 'REBLOG_FAIL';
export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST'; export const REBLOGS_EXPAND_REQUEST = 'REBLOGS_EXPAND_REQUEST';
export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS'; export const REBLOGS_EXPAND_SUCCESS = 'REBLOGS_EXPAND_SUCCESS';
export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL'; export const REBLOGS_EXPAND_FAIL = 'REBLOGS_EXPAND_FAIL';
@ -19,10 +15,6 @@ export const EMOJIREACT_REQUEST = 'EMOJIREACT_REQUEST';
export const EMOJIREACT_SUCCESS = 'EMOJIREACT_SUCCESS'; export const EMOJIREACT_SUCCESS = 'EMOJIREACT_SUCCESS';
export const EMOJIREACT_FAIL = 'EMOJIREACT_FAIL'; export const EMOJIREACT_FAIL = 'EMOJIREACT_FAIL';
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST'; export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS'; export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL'; export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
@ -79,89 +71,13 @@ export const MENTIONED_USERS_EXPAND_REQUEST = 'MENTIONED_USERS_EXPAND_REQUEST';
export const MENTIONED_USERS_EXPAND_SUCCESS = 'MENTIONED_USERS_EXPAND_SUCCESS'; export const MENTIONED_USERS_EXPAND_SUCCESS = 'MENTIONED_USERS_EXPAND_SUCCESS';
export const MENTIONED_USERS_EXPAND_FAIL = 'MENTIONED_USERS_EXPAND_FAIL'; export const MENTIONED_USERS_EXPAND_FAIL = 'MENTIONED_USERS_EXPAND_FAIL';
export function reblog(status, visibility) { export * from "./interactions_typed";
return function (dispatch, getState) {
dispatch(reblogRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`, { visibility }).then(function (response) {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
dispatch(importFetchedStatus(response.data.reblog));
dispatch(reblogSuccess(status));
}).catch(function (error) {
dispatch(reblogFail(status, error));
});
};
}
export function unreblog(status) {
return (dispatch, getState) => {
dispatch(unreblogRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
dispatch(importFetchedStatus(response.data));
dispatch(unreblogSuccess(status));
}).catch(error => {
dispatch(unreblogFail(status, error));
});
};
}
export function reblogRequest(status) {
return {
type: REBLOG_REQUEST,
status: status,
skipLoading: true,
};
}
export function reblogSuccess(status) {
return {
type: REBLOG_SUCCESS,
status: status,
skipLoading: true,
};
}
export function reblogFail(status, error) {
return {
type: REBLOG_FAIL,
status: status,
error: error,
skipLoading: true,
};
}
export function unreblogRequest(status) {
return {
type: UNREBLOG_REQUEST,
status: status,
skipLoading: true,
};
}
export function unreblogSuccess(status) {
return {
type: UNREBLOG_SUCCESS,
status: status,
skipLoading: true,
};
}
export function unreblogFail(status, error) {
return {
type: UNREBLOG_FAIL,
status: status,
error: error,
skipLoading: true,
};
}
export function favourite(status) { export function favourite(status) {
return function (dispatch, getState) { return function (dispatch) {
dispatch(favouriteRequest(status)); dispatch(favouriteRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) { api().post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(favouriteSuccess(status)); dispatch(favouriteSuccess(status));
}).catch(function (error) { }).catch(function (error) {
@ -171,10 +87,10 @@ export function favourite(status) {
} }
export function unfavourite(status) { export function unfavourite(status) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unfavouriteRequest(status)); dispatch(unfavouriteRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => { api().post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(unfavouriteSuccess(status)); dispatch(unfavouriteSuccess(status));
}).catch(error => { }).catch(error => {
@ -319,10 +235,10 @@ export function unEmojiReactFail(status, emoji, error) {
} }
export function bookmark(status) { export function bookmark(status) {
return function (dispatch, getState) { return function (dispatch) {
dispatch(bookmarkRequest(status)); dispatch(bookmarkRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/bookmark`).then(function (response) { api().post(`/api/v1/statuses/${status.get('id')}/bookmark`).then(function (response) {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(bookmarkSuccess(status, response.data)); dispatch(bookmarkSuccess(status, response.data));
}).catch(function (error) { }).catch(function (error) {
@ -332,10 +248,10 @@ export function bookmark(status) {
} }
export function unbookmark(status) { export function unbookmark(status) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unbookmarkRequest(status)); dispatch(unbookmarkRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then(response => { api().post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then(response => {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(unbookmarkSuccess(status, response.data)); dispatch(unbookmarkSuccess(status, response.data));
}).catch(error => { }).catch(error => {
@ -391,10 +307,10 @@ export function unbookmarkFail(status, error) {
} }
export function fetchReblogs(id) { export function fetchReblogs(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchReblogsRequest(id)); dispatch(fetchReblogsRequest(id));
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => { api().get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null)); dispatch(fetchReblogsSuccess(id, response.data, next ? next.uri : null));
@ -438,7 +354,7 @@ export function expandReblogs(id) {
dispatch(expandReblogsRequest(id)); dispatch(expandReblogsRequest(id));
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
@ -473,10 +389,10 @@ export function expandReblogsFail(id, error) {
} }
export function fetchFavourites(id) { export function fetchFavourites(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchFavouritesRequest(id)); dispatch(fetchFavouritesRequest(id));
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => { api().get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null)); dispatch(fetchFavouritesSuccess(id, response.data, next ? next.uri : null));
@ -520,7 +436,7 @@ export function expandFavourites(id) {
dispatch(expandFavouritesRequest(id)); dispatch(expandFavouritesRequest(id));
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
@ -669,10 +585,10 @@ export function fetchStatusReferencesFail(id, error) {
} }
export function pin(status) { export function pin(status) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(pinRequest(status)); dispatch(pinRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => { api().post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(pinSuccess(status)); dispatch(pinSuccess(status));
}).catch(error => { }).catch(error => {
@ -707,10 +623,10 @@ export function pinFail(status, error) {
} }
export function unpin (status) { export function unpin (status) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unpinRequest(status)); dispatch(unpinRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => { api().post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(unpinSuccess(status)); dispatch(unpinSuccess(status));
}).catch(error => { }).catch(error => {

View file

@ -0,0 +1,35 @@
import { apiReblog, apiUnreblog } from 'mastodon/api/interactions';
import type { StatusVisibility } from 'mastodon/models/status';
import { createDataLoadingThunk } from 'mastodon/store/typed_functions';
import { importFetchedStatus } from './importer';
export const reblog = createDataLoadingThunk(
'status/reblog',
({
statusId,
visibility,
}: {
statusId: string;
visibility: StatusVisibility;
}) => apiReblog(statusId, visibility),
(data, { dispatch, discardLoadData }) => {
// The reblog API method returns a new status wrapped around the original. In this case we are only
// interested in how the original is modified, hence passing it skipping the wrapper
dispatch(importFetchedStatus(data.reblog));
// The payload is not used in any actions
return discardLoadData;
},
);
export const unreblog = createDataLoadingThunk(
'status/unreblog',
({ statusId }: { statusId: string }) => apiUnreblog(statusId),
(data, { dispatch, discardLoadData }) => {
dispatch(importFetchedStatus(data));
// The payload is not used in any actions
return discardLoadData;
},
);

View file

@ -59,7 +59,7 @@ export const fetchList = id => (dispatch, getState) => {
dispatch(fetchListRequest(id)); dispatch(fetchListRequest(id));
api(getState).get(`/api/v1/lists/${id}`) api().get(`/api/v1/lists/${id}`)
.then(({ data }) => dispatch(fetchListSuccess(data))) .then(({ data }) => dispatch(fetchListSuccess(data)))
.catch(err => dispatch(fetchListFail(id, err))); .catch(err => dispatch(fetchListFail(id, err)));
}; };
@ -80,10 +80,10 @@ export const fetchListFail = (id, error) => ({
error, error,
}); });
export const fetchLists = () => (dispatch, getState) => { export const fetchLists = () => (dispatch) => {
dispatch(fetchListsRequest()); dispatch(fetchListsRequest());
api(getState).get('/api/v1/lists') api().get('/api/v1/lists')
.then(({ data }) => dispatch(fetchListsSuccess(data))) .then(({ data }) => dispatch(fetchListsSuccess(data)))
.catch(err => dispatch(fetchListsFail(err))); .catch(err => dispatch(fetchListsFail(err)));
}; };
@ -127,10 +127,10 @@ export const changeListEditorTitle = value => ({
value, value,
}); });
export const createList = (title, shouldReset) => (dispatch, getState) => { export const createList = (title, shouldReset) => (dispatch) => {
dispatch(createListRequest()); dispatch(createListRequest());
api(getState).post('/api/v1/lists', { title }).then(({ data }) => { api().post('/api/v1/lists', { title }).then(({ data }) => {
dispatch(createListSuccess(data)); dispatch(createListSuccess(data));
if (shouldReset) { if (shouldReset) {
@ -153,10 +153,10 @@ export const createListFail = error => ({
error, error,
}); });
export const updateList = (id, title, shouldReset, isExclusive, replies_policy, notify) => (dispatch, getState) => { export const updateList = (id, title, shouldReset, isExclusive, replies_policy, notify) => (dispatch) => {
dispatch(updateListRequest(id)); dispatch(updateListRequest(id));
api(getState).put(`/api/v1/lists/${id}`, { api().put(`/api/v1/lists/${id}`, {
title, title,
replies_policy, replies_policy,
exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive, exclusive: typeof isExclusive === 'undefined' ? undefined : !!isExclusive,
@ -190,10 +190,10 @@ export const resetListEditor = () => ({
type: LIST_EDITOR_RESET, type: LIST_EDITOR_RESET,
}); });
export const deleteList = id => (dispatch, getState) => { export const deleteList = id => (dispatch) => {
dispatch(deleteListRequest(id)); dispatch(deleteListRequest(id));
api(getState).delete(`/api/v1/lists/${id}`) api().delete(`/api/v1/lists/${id}`)
.then(() => dispatch(deleteListSuccess(id))) .then(() => dispatch(deleteListSuccess(id)))
.catch(err => dispatch(deleteListFail(id, err))); .catch(err => dispatch(deleteListFail(id, err)));
}; };
@ -214,10 +214,10 @@ export const deleteListFail = (id, error) => ({
error, error,
}); });
export const fetchListAccounts = listId => (dispatch, getState) => { export const fetchListAccounts = listId => (dispatch) => {
dispatch(fetchListAccountsRequest(listId)); dispatch(fetchListAccountsRequest(listId));
api(getState).get(`/api/v1/lists/${listId}/accounts`, { params: { limit: 0 } }).then(({ data }) => { api().get(`/api/v1/lists/${listId}/accounts`, { params: { limit: 0 } }).then(({ data }) => {
dispatch(importFetchedAccounts(data)); dispatch(importFetchedAccounts(data));
dispatch(fetchListAccountsSuccess(listId, data)); dispatch(fetchListAccountsSuccess(listId, data));
}).catch(err => dispatch(fetchListAccountsFail(listId, err))); }).catch(err => dispatch(fetchListAccountsFail(listId, err)));
@ -241,14 +241,14 @@ export const fetchListAccountsFail = (id, error) => ({
error, error,
}); });
export const fetchListSuggestions = q => (dispatch, getState) => { export const fetchListSuggestions = q => (dispatch) => {
const params = { const params = {
q, q,
resolve: false, resolve: false,
following: true, following: true,
}; };
api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => { api().get('/api/v1/accounts/search', { params }).then(({ data }) => {
dispatch(importFetchedAccounts(data)); dispatch(importFetchedAccounts(data));
dispatch(fetchListSuggestionsReady(q, data)); dispatch(fetchListSuggestionsReady(q, data));
}).catch(error => dispatch(showAlertForError(error))); }).catch(error => dispatch(showAlertForError(error)));
@ -273,10 +273,10 @@ export const addToListEditor = accountId => (dispatch, getState) => {
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId)); dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId));
}; };
export const addToList = (listId, accountId) => (dispatch, getState) => { export const addToList = (listId, accountId) => (dispatch) => {
dispatch(addToListRequest(listId, accountId)); dispatch(addToListRequest(listId, accountId));
api(getState).post(`/api/v1/lists/${listId}/accounts`, { account_ids: [accountId] }) api().post(`/api/v1/lists/${listId}/accounts`, { account_ids: [accountId] })
.then(() => dispatch(addToListSuccess(listId, accountId))) .then(() => dispatch(addToListSuccess(listId, accountId)))
.catch(err => dispatch(addToListFail(listId, accountId, err))); .catch(err => dispatch(addToListFail(listId, accountId, err)));
}; };
@ -304,10 +304,10 @@ export const removeFromListEditor = accountId => (dispatch, getState) => {
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId)); dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId));
}; };
export const removeFromList = (listId, accountId) => (dispatch, getState) => { export const removeFromList = (listId, accountId) => (dispatch) => {
dispatch(removeFromListRequest(listId, accountId)); dispatch(removeFromListRequest(listId, accountId));
api(getState).delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [accountId] } }) api().delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [accountId] } })
.then(() => dispatch(removeFromListSuccess(listId, accountId))) .then(() => dispatch(removeFromListSuccess(listId, accountId)))
.catch(err => dispatch(removeFromListFail(listId, accountId, err))); .catch(err => dispatch(removeFromListFail(listId, accountId, err)));
}; };
@ -344,10 +344,10 @@ export const setupListAdder = accountId => (dispatch, getState) => {
dispatch(fetchAccountLists(accountId)); dispatch(fetchAccountLists(accountId));
}; };
export const fetchAccountLists = accountId => (dispatch, getState) => { export const fetchAccountLists = accountId => (dispatch) => {
dispatch(fetchAccountListsRequest(accountId)); dispatch(fetchAccountListsRequest(accountId));
api(getState).get(`/api/v1/accounts/${accountId}/lists`) api().get(`/api/v1/accounts/${accountId}/lists`)
.then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data))) .then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data)))
.catch(err => dispatch(fetchAccountListsFail(accountId, err))); .catch(err => dispatch(fetchAccountListsFail(accountId, err)));
}; };
@ -376,4 +376,3 @@ export const addToListAdder = listId => (dispatch, getState) => {
export const removeFromListAdder = listId => (dispatch, getState) => { export const removeFromListAdder = listId => (dispatch, getState) => {
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId']))); dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
}; };

View file

@ -1,19 +1,24 @@
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import type { MarkerJSON } from 'mastodon/api_types/markers'; import type { MarkerJSON } from 'mastodon/api_types/markers';
import { getAccessToken } from 'mastodon/initial_state';
import type { AppDispatch, RootState } from 'mastodon/store'; import type { AppDispatch, RootState } from 'mastodon/store';
import { createAppAsyncThunk } from 'mastodon/store/typed_functions'; import { createAppAsyncThunk } from 'mastodon/store/typed_functions';
import api, { authorizationTokenFromState } from '../api'; import api from '../api';
import { compareId } from '../compare_id'; import { compareId } from '../compare_id';
export const synchronouslySubmitMarkers = createAppAsyncThunk( export const synchronouslySubmitMarkers = createAppAsyncThunk(
'markers/submit', 'markers/submit',
async (_args, { getState }) => { async (_args, { getState }) => {
const accessToken = authorizationTokenFromState(getState); const accessToken = getAccessToken();
const params = buildPostMarkersParams(getState()); const params = buildPostMarkersParams(getState());
if (Object.keys(params).length === 0 || !accessToken) { if (
Object.keys(params).length === 0 ||
!accessToken ||
accessToken === ''
) {
return; return;
} }
@ -96,14 +101,14 @@ export const submitMarkersAction = createAppAsyncThunk<{
home: string | undefined; home: string | undefined;
notifications: string | undefined; notifications: string | undefined;
}>('markers/submitAction', async (_args, { getState }) => { }>('markers/submitAction', async (_args, { getState }) => {
const accessToken = authorizationTokenFromState(getState); const accessToken = getAccessToken();
const params = buildPostMarkersParams(getState()); const params = buildPostMarkersParams(getState());
if (Object.keys(params).length === 0 || accessToken === '') { if (Object.keys(params).length === 0 || !accessToken || accessToken === '') {
return { home: undefined, notifications: undefined }; return { home: undefined, notifications: undefined };
} }
await api(getState).post<MarkerJSON>('/api/v1/markers', params); await api().post<MarkerJSON>('/api/v1/markers', params);
return { return {
home: params.home?.last_read_id, home: params.home?.last_read_id,
@ -133,14 +138,11 @@ export const submitMarkers = createAppAsyncThunk(
}, },
); );
export const fetchMarkers = createAppAsyncThunk( export const fetchMarkers = createAppAsyncThunk('markers/fetch', async () => {
'markers/fetch', const response = await api().get<Record<string, MarkerJSON>>(
async (_args, { getState }) => { `/api/v1/markers`,
const response = await api(getState).get<Record<string, MarkerJSON>>( { params: { timeline: ['notifications'] } },
`/api/v1/markers`, );
{ params: { timeline: ['notifications'] } },
);
return { markers: response.data }; return { markers: response.data };
}, });
);

View file

@ -13,10 +13,10 @@ export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS';
export const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL'; export const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL';
export function fetchMutes() { export function fetchMutes() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchMutesRequest()); dispatch(fetchMutesRequest());
api(getState).get('/api/v1/mutes').then(response => { api().get('/api/v1/mutes').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(fetchMutesSuccess(response.data, next ? next.uri : null)); dispatch(fetchMutesSuccess(response.data, next ? next.uri : null));
@ -56,7 +56,7 @@ export function expandMutes() {
dispatch(expandMutesRequest()); dispatch(expandMutesRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data)); dispatch(importFetchedAccounts(response.data));
dispatch(expandMutesSuccess(response.data, next ? next.uri : null)); dispatch(expandMutesSuccess(response.data, next ? next.uri : null));

View file

@ -233,7 +233,7 @@ export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) {
dispatch(expandNotificationsRequest(isLoadingMore)); dispatch(expandNotificationsRequest(isLoadingMore));
api(getState).get('/api/v1/notifications', { params, signal: expandNotificationsController.signal }).then(response => { api().get('/api/v1/notifications', { params, signal: expandNotificationsController.signal }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map(item => item.account))); dispatch(importFetchedAccounts(response.data.map(item => item.account)));
@ -279,12 +279,12 @@ export function expandNotificationsFail(error, isLoadingMore) {
} }
export function clearNotifications() { export function clearNotifications() {
return (dispatch, getState) => { return (dispatch) => {
dispatch({ dispatch({
type: NOTIFICATIONS_CLEAR, type: NOTIFICATIONS_CLEAR,
}); });
api(getState).post('/api/v1/notifications/clear'); api().post('/api/v1/notifications/clear');
}; };
} }
@ -363,10 +363,10 @@ export function setBrowserPermission (value) {
}; };
} }
export const fetchNotificationPolicy = () => (dispatch, getState) => { export const fetchNotificationPolicy = () => (dispatch) => {
dispatch(fetchNotificationPolicyRequest()); dispatch(fetchNotificationPolicyRequest());
api(getState).get('/api/v1/notifications/policy').then(({ data }) => { api().get('/api/v1/notifications/policy').then(({ data }) => {
dispatch(fetchNotificationPolicySuccess(data)); dispatch(fetchNotificationPolicySuccess(data));
}).catch(err => { }).catch(err => {
dispatch(fetchNotificationPolicyFail(err)); dispatch(fetchNotificationPolicyFail(err));
@ -387,10 +387,10 @@ export const fetchNotificationPolicyFail = error => ({
error, error,
}); });
export const updateNotificationsPolicy = params => (dispatch, getState) => { export const updateNotificationsPolicy = params => (dispatch) => {
dispatch(fetchNotificationPolicyRequest()); dispatch(fetchNotificationPolicyRequest());
api(getState).put('/api/v1/notifications/policy', params).then(({ data }) => { api().put('/api/v1/notifications/policy', params).then(({ data }) => {
dispatch(fetchNotificationPolicySuccess(data)); dispatch(fetchNotificationPolicySuccess(data));
}).catch(err => { }).catch(err => {
dispatch(fetchNotificationPolicyFail(err)); dispatch(fetchNotificationPolicyFail(err));
@ -410,7 +410,7 @@ export const fetchNotificationRequests = () => (dispatch, getState) => {
dispatch(fetchNotificationRequestsRequest()); dispatch(fetchNotificationRequestsRequest());
api(getState).get('/api/v1/notifications/requests', { params }).then(response => { api().get('/api/v1/notifications/requests', { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map(x => x.account))); dispatch(importFetchedAccounts(response.data.map(x => x.account)));
dispatch(fetchNotificationRequestsSuccess(response.data, next ? next.uri : null)); dispatch(fetchNotificationRequestsSuccess(response.data, next ? next.uri : null));
@ -443,7 +443,7 @@ export const expandNotificationRequests = () => (dispatch, getState) => {
dispatch(expandNotificationRequestsRequest()); dispatch(expandNotificationRequestsRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map(x => x.account))); dispatch(importFetchedAccounts(response.data.map(x => x.account)));
dispatch(expandNotificationRequestsSuccess(response.data, next?.uri)); dispatch(expandNotificationRequestsSuccess(response.data, next?.uri));
@ -476,7 +476,7 @@ export const fetchNotificationRequest = id => (dispatch, getState) => {
dispatch(fetchNotificationRequestRequest(id)); dispatch(fetchNotificationRequestRequest(id));
api(getState).get(`/api/v1/notifications/requests/${id}`).then(({ data }) => { api().get(`/api/v1/notifications/requests/${id}`).then(({ data }) => {
dispatch(fetchNotificationRequestSuccess(data)); dispatch(fetchNotificationRequestSuccess(data));
}).catch(err => { }).catch(err => {
dispatch(fetchNotificationRequestFail(id, err)); dispatch(fetchNotificationRequestFail(id, err));
@ -499,10 +499,10 @@ export const fetchNotificationRequestFail = (id, error) => ({
error, error,
}); });
export const acceptNotificationRequest = id => (dispatch, getState) => { export const acceptNotificationRequest = id => (dispatch) => {
dispatch(acceptNotificationRequestRequest(id)); dispatch(acceptNotificationRequestRequest(id));
api(getState).post(`/api/v1/notifications/requests/${id}/accept`).then(() => { api().post(`/api/v1/notifications/requests/${id}/accept`).then(() => {
dispatch(acceptNotificationRequestSuccess(id)); dispatch(acceptNotificationRequestSuccess(id));
}).catch(err => { }).catch(err => {
dispatch(acceptNotificationRequestFail(id, err)); dispatch(acceptNotificationRequestFail(id, err));
@ -525,10 +525,10 @@ export const acceptNotificationRequestFail = (id, error) => ({
error, error,
}); });
export const dismissNotificationRequest = id => (dispatch, getState) => { export const dismissNotificationRequest = id => (dispatch) => {
dispatch(dismissNotificationRequestRequest(id)); dispatch(dismissNotificationRequestRequest(id));
api(getState).post(`/api/v1/notifications/requests/${id}/dismiss`).then(() =>{ api().post(`/api/v1/notifications/requests/${id}/dismiss`).then(() =>{
dispatch(dismissNotificationRequestSuccess(id)); dispatch(dismissNotificationRequestSuccess(id));
}).catch(err => { }).catch(err => {
dispatch(dismissNotificationRequestFail(id, err)); dispatch(dismissNotificationRequestFail(id, err));
@ -567,7 +567,7 @@ export const fetchNotificationsForRequest = accountId => (dispatch, getState) =>
dispatch(fetchNotificationsForRequestRequest()); dispatch(fetchNotificationsForRequestRequest());
api(getState).get('/api/v1/notifications', { params }).then(response => { api().get('/api/v1/notifications', { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map(item => item.account))); dispatch(importFetchedAccounts(response.data.map(item => item.account)));
dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status))); dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
@ -603,7 +603,7 @@ export const expandNotificationsForRequest = () => (dispatch, getState) => {
dispatch(expandNotificationsForRequestRequest()); dispatch(expandNotificationsForRequestRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedAccounts(response.data.map(item => item.account))); dispatch(importFetchedAccounts(response.data.map(item => item.account)));
dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status))); dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));

View file

@ -8,10 +8,10 @@ export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL'; export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
export function fetchPinnedStatuses() { export function fetchPinnedStatuses() {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchPinnedStatusesRequest()); dispatch(fetchPinnedStatusesRequest());
api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => { api().get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => {
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(fetchPinnedStatusesSuccess(response.data, null)); dispatch(fetchPinnedStatusesSuccess(response.data, null));
}).catch(error => { }).catch(error => {

View file

@ -10,10 +10,10 @@ export const POLL_FETCH_REQUEST = 'POLL_FETCH_REQUEST';
export const POLL_FETCH_SUCCESS = 'POLL_FETCH_SUCCESS'; export const POLL_FETCH_SUCCESS = 'POLL_FETCH_SUCCESS';
export const POLL_FETCH_FAIL = 'POLL_FETCH_FAIL'; export const POLL_FETCH_FAIL = 'POLL_FETCH_FAIL';
export const vote = (pollId, choices) => (dispatch, getState) => { export const vote = (pollId, choices) => (dispatch) => {
dispatch(voteRequest()); dispatch(voteRequest());
api(getState).post(`/api/v1/polls/${pollId}/votes`, { choices }) api().post(`/api/v1/polls/${pollId}/votes`, { choices })
.then(({ data }) => { .then(({ data }) => {
dispatch(importFetchedPoll(data)); dispatch(importFetchedPoll(data));
dispatch(voteSuccess(data)); dispatch(voteSuccess(data));
@ -21,10 +21,10 @@ export const vote = (pollId, choices) => (dispatch, getState) => {
.catch(err => dispatch(voteFail(err))); .catch(err => dispatch(voteFail(err)));
}; };
export const fetchPoll = pollId => (dispatch, getState) => { export const fetchPoll = pollId => (dispatch) => {
dispatch(fetchPollRequest()); dispatch(fetchPollRequest());
api(getState).get(`/api/v1/polls/${pollId}`) api().get(`/api/v1/polls/${pollId}`)
.then(({ data }) => { .then(({ data }) => {
dispatch(importFetchedPoll(data)); dispatch(importFetchedPoll(data));
dispatch(fetchPollSuccess(data)); dispatch(fetchPollSuccess(data));

View file

@ -15,10 +15,10 @@ export const initReport = (account, status) => dispatch =>
}, },
})); }));
export const submitReport = (params, onSuccess, onFail) => (dispatch, getState) => { export const submitReport = (params, onSuccess, onFail) => (dispatch) => {
dispatch(submitReportRequest()); dispatch(submitReportRequest());
api(getState).post('/api/v1/reports', params).then(response => { api().post('/api/v1/reports', params).then(response => {
dispatch(submitReportSuccess(response.data)); dispatch(submitReportSuccess(response.data));
if (onSuccess) onSuccess(); if (onSuccess) onSuccess();
}).catch(error => { }).catch(error => {

View file

@ -46,7 +46,7 @@ export function submitSearch(type) {
dispatch(fetchSearchRequest(type)); dispatch(fetchSearchRequest(type));
api(getState).get('/api/v2/search', { api().get('/api/v2/search', {
params: { params: {
q: value, q: value,
resolve: signedIn, resolve: signedIn,
@ -99,7 +99,7 @@ export const expandSearch = type => (dispatch, getState) => {
dispatch(expandSearchRequest(type)); dispatch(expandSearchRequest(type));
api(getState).get('/api/v2/search', { api().get('/api/v2/search', {
params: { params: {
q: value, q: value,
type, type,
@ -156,7 +156,7 @@ export const openURL = (value, history, onFailure) => (dispatch, getState) => {
dispatch(fetchSearchRequest()); dispatch(fetchSearchRequest());
api(getState).get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => { api().get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => {
if (response.data.accounts?.length > 0) { if (response.data.accounts?.length > 0) {
dispatch(importFetchedAccounts(response.data.accounts)); dispatch(importFetchedAccounts(response.data.accounts));
history.push(`/@${response.data.accounts[0].acct}`); history.push(`/@${response.data.accounts[0].acct}`);

View file

@ -25,7 +25,7 @@ export const fetchServer = () => (dispatch, getState) => {
dispatch(fetchServerRequest()); dispatch(fetchServerRequest());
api(getState) api()
.get('/api/v2/instance').then(({ data }) => { .get('/api/v2/instance').then(({ data }) => {
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account)); if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
dispatch(fetchServerSuccess(data)); dispatch(fetchServerSuccess(data));
@ -46,10 +46,10 @@ const fetchServerFail = error => ({
error, error,
}); });
export const fetchServerTranslationLanguages = () => (dispatch, getState) => { export const fetchServerTranslationLanguages = () => (dispatch) => {
dispatch(fetchServerTranslationLanguagesRequest()); dispatch(fetchServerTranslationLanguagesRequest());
api(getState) api()
.get('/api/v1/instance/translation_languages').then(({ data }) => { .get('/api/v1/instance/translation_languages').then(({ data }) => {
dispatch(fetchServerTranslationLanguagesSuccess(data)); dispatch(fetchServerTranslationLanguagesSuccess(data));
}).catch(err => dispatch(fetchServerTranslationLanguagesFail(err))); }).catch(err => dispatch(fetchServerTranslationLanguagesFail(err)));
@ -76,7 +76,7 @@ export const fetchExtendedDescription = () => (dispatch, getState) => {
dispatch(fetchExtendedDescriptionRequest()); dispatch(fetchExtendedDescriptionRequest());
api(getState) api()
.get('/api/v1/instance/extended_description') .get('/api/v1/instance/extended_description')
.then(({ data }) => dispatch(fetchExtendedDescriptionSuccess(data))) .then(({ data }) => dispatch(fetchExtendedDescriptionSuccess(data)))
.catch(err => dispatch(fetchExtendedDescriptionFail(err))); .catch(err => dispatch(fetchExtendedDescriptionFail(err)));
@ -103,7 +103,7 @@ export const fetchDomainBlocks = () => (dispatch, getState) => {
dispatch(fetchDomainBlocksRequest()); dispatch(fetchDomainBlocksRequest());
api(getState) api()
.get('/api/v1/instance/domain_blocks') .get('/api/v1/instance/domain_blocks')
.then(({ data }) => dispatch(fetchDomainBlocksSuccess(true, data))) .then(({ data }) => dispatch(fetchDomainBlocksSuccess(true, data)))
.catch(err => { .catch(err => {

View file

@ -61,7 +61,7 @@ export function fetchStatus(id, forceFetch = false) {
dispatch(fetchStatusRequest(id, skipLoading)); dispatch(fetchStatusRequest(id, skipLoading));
api(getState).get(`/api/v1/statuses/${id}`).then(response => { api().get(`/api/v1/statuses/${id}`).then(response => {
dispatch(importFetchedStatus(response.data)); dispatch(importFetchedStatus(response.data));
dispatch(fetchStatusSuccess(skipLoading)); dispatch(fetchStatusSuccess(skipLoading));
}).catch(error => { }).catch(error => {
@ -104,7 +104,7 @@ export const editStatus = (id, routerHistory) => (dispatch, getState) => {
dispatch(fetchStatusSourceRequest()); dispatch(fetchStatusSourceRequest());
api(getState).get(`/api/v1/statuses/${id}/source`).then(response => { api().get(`/api/v1/statuses/${id}/source`).then(response => {
dispatch(fetchStatusSourceSuccess()); dispatch(fetchStatusSourceSuccess());
ensureComposeIsVisible(getState, routerHistory); ensureComposeIsVisible(getState, routerHistory);
dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text)); dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text));
@ -136,7 +136,7 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
dispatch(deleteStatusRequest(id)); dispatch(deleteStatusRequest(id));
api(getState).delete(`/api/v1/statuses/${id}`).then(response => { api().delete(`/api/v1/statuses/${id}`).then(response => {
dispatch(deleteStatusSuccess(id)); dispatch(deleteStatusSuccess(id));
dispatch(deleteFromTimelines(id)); dispatch(deleteFromTimelines(id));
dispatch(importFetchedAccount(response.data.account)); dispatch(importFetchedAccount(response.data.account));
@ -177,10 +177,10 @@ export const updateStatus = status => dispatch =>
dispatch(importFetchedStatus(status)); dispatch(importFetchedStatus(status));
export function fetchContext(id) { export function fetchContext(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchContextRequest(id)); dispatch(fetchContextRequest(id));
api(getState).get(`/api/v1/statuses/${id}/context?with_reference=1`).then(response => { api().get(`/api/v1/statuses/${id}/context?with_reference=1`).then(response => {
dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants).concat(response.data.references))); dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants).concat(response.data.references)));
dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants, response.data.references)); dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants, response.data.references));
@ -222,10 +222,10 @@ export function fetchContextFail(id, error) {
} }
export function muteStatus(id) { export function muteStatus(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(muteStatusRequest(id)); dispatch(muteStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/mute`).then(() => { api().post(`/api/v1/statuses/${id}/mute`).then(() => {
dispatch(muteStatusSuccess(id)); dispatch(muteStatusSuccess(id));
}).catch(error => { }).catch(error => {
dispatch(muteStatusFail(id, error)); dispatch(muteStatusFail(id, error));
@ -256,10 +256,10 @@ export function muteStatusFail(id, error) {
} }
export function unmuteStatus(id) { export function unmuteStatus(id) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(unmuteStatusRequest(id)); dispatch(unmuteStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => { api().post(`/api/v1/statuses/${id}/unmute`).then(() => {
dispatch(unmuteStatusSuccess(id)); dispatch(unmuteStatusSuccess(id));
}).catch(error => { }).catch(error => {
dispatch(unmuteStatusFail(id, error)); dispatch(unmuteStatusFail(id, error));
@ -319,10 +319,10 @@ export function toggleStatusCollapse(id, isCollapsed) {
}; };
} }
export const translateStatus = id => (dispatch, getState) => { export const translateStatus = id => (dispatch) => {
dispatch(translateStatusRequest(id)); dispatch(translateStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/translate`).then(response => { api().post(`/api/v1/statuses/${id}/translate`).then(response => {
dispatch(translateStatusSuccess(id, response.data)); dispatch(translateStatusSuccess(id, response.data));
}).catch(error => { }).catch(error => {
dispatch(translateStatusFail(id, error)); dispatch(translateStatusFail(id, error));

View file

@ -10,10 +10,10 @@ export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL';
export const SUGGESTIONS_DISMISS = 'SUGGESTIONS_DISMISS'; export const SUGGESTIONS_DISMISS = 'SUGGESTIONS_DISMISS';
export function fetchSuggestions(withRelationships = false) { export function fetchSuggestions(withRelationships = false) {
return (dispatch, getState) => { return (dispatch) => {
dispatch(fetchSuggestionsRequest()); dispatch(fetchSuggestionsRequest());
api(getState).get('/api/v2/suggestions', { params: { limit: 20 } }).then(response => { api().get('/api/v2/suggestions', { params: { limit: 20 } }).then(response => {
dispatch(importFetchedAccounts(response.data.map(x => x.account))); dispatch(importFetchedAccounts(response.data.map(x => x.account)));
dispatch(fetchSuggestionsSuccess(response.data)); dispatch(fetchSuggestionsSuccess(response.data));
@ -48,11 +48,11 @@ export function fetchSuggestionsFail(error) {
}; };
} }
export const dismissSuggestion = accountId => (dispatch, getState) => { export const dismissSuggestion = accountId => (dispatch) => {
dispatch({ dispatch({
type: SUGGESTIONS_DISMISS, type: SUGGESTIONS_DISMISS,
id: accountId, id: accountId,
}); });
api(getState).delete(`/api/v1/suggestions/${accountId}`).catch(() => {}); api().delete(`/api/v1/suggestions/${accountId}`).catch(() => {});
}; };

View file

@ -20,10 +20,10 @@ export const HASHTAG_UNFOLLOW_REQUEST = 'HASHTAG_UNFOLLOW_REQUEST';
export const HASHTAG_UNFOLLOW_SUCCESS = 'HASHTAG_UNFOLLOW_SUCCESS'; export const HASHTAG_UNFOLLOW_SUCCESS = 'HASHTAG_UNFOLLOW_SUCCESS';
export const HASHTAG_UNFOLLOW_FAIL = 'HASHTAG_UNFOLLOW_FAIL'; export const HASHTAG_UNFOLLOW_FAIL = 'HASHTAG_UNFOLLOW_FAIL';
export const fetchHashtag = name => (dispatch, getState) => { export const fetchHashtag = name => (dispatch) => {
dispatch(fetchHashtagRequest()); dispatch(fetchHashtagRequest());
api(getState).get(`/api/v1/tags/${name}`).then(({ data }) => { api().get(`/api/v1/tags/${name}`).then(({ data }) => {
dispatch(fetchHashtagSuccess(name, data)); dispatch(fetchHashtagSuccess(name, data));
}).catch(err => { }).catch(err => {
dispatch(fetchHashtagFail(err)); dispatch(fetchHashtagFail(err));
@ -45,10 +45,10 @@ export const fetchHashtagFail = error => ({
error, error,
}); });
export const fetchFollowedHashtags = () => (dispatch, getState) => { export const fetchFollowedHashtags = () => (dispatch) => {
dispatch(fetchFollowedHashtagsRequest()); dispatch(fetchFollowedHashtagsRequest());
api(getState).get('/api/v1/followed_tags').then(response => { api().get('/api/v1/followed_tags').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null)); dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null));
}).catch(err => { }).catch(err => {
@ -87,7 +87,7 @@ export function expandFollowedHashtags() {
dispatch(expandFollowedHashtagsRequest()); dispatch(expandFollowedHashtagsRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null)); dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null));
}).catch(error => { }).catch(error => {
@ -117,10 +117,10 @@ export function expandFollowedHashtagsFail(error) {
}; };
} }
export const followHashtag = name => (dispatch, getState) => { export const followHashtag = name => (dispatch) => {
dispatch(followHashtagRequest(name)); dispatch(followHashtagRequest(name));
api(getState).post(`/api/v1/tags/${name}/follow`).then(({ data }) => { api().post(`/api/v1/tags/${name}/follow`).then(({ data }) => {
dispatch(followHashtagSuccess(name, data)); dispatch(followHashtagSuccess(name, data));
}).catch(err => { }).catch(err => {
dispatch(followHashtagFail(name, err)); dispatch(followHashtagFail(name, err));
@ -144,10 +144,10 @@ export const followHashtagFail = (name, error) => ({
error, error,
}); });
export const unfollowHashtag = name => (dispatch, getState) => { export const unfollowHashtag = name => (dispatch) => {
dispatch(unfollowHashtagRequest(name)); dispatch(unfollowHashtagRequest(name));
api(getState).post(`/api/v1/tags/${name}/unfollow`).then(({ data }) => { api().post(`/api/v1/tags/${name}/unfollow`).then(({ data }) => {
dispatch(unfollowHashtagSuccess(name, data)); dispatch(unfollowHashtagSuccess(name, data));
}).catch(err => { }).catch(err => {
dispatch(unfollowHashtagFail(name, err)); dispatch(unfollowHashtagFail(name, err));

View file

@ -114,7 +114,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
dispatch(expandTimelineRequest(timelineId, isLoadingMore)); dispatch(expandTimelineRequest(timelineId, isLoadingMore));
api(getState).get(path, { params }).then(response => { api().get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));

View file

@ -18,10 +18,10 @@ export const TRENDS_STATUSES_EXPAND_REQUEST = 'TRENDS_STATUSES_EXPAND_REQUEST';
export const TRENDS_STATUSES_EXPAND_SUCCESS = 'TRENDS_STATUSES_EXPAND_SUCCESS'; export const TRENDS_STATUSES_EXPAND_SUCCESS = 'TRENDS_STATUSES_EXPAND_SUCCESS';
export const TRENDS_STATUSES_EXPAND_FAIL = 'TRENDS_STATUSES_EXPAND_FAIL'; export const TRENDS_STATUSES_EXPAND_FAIL = 'TRENDS_STATUSES_EXPAND_FAIL';
export const fetchTrendingHashtags = () => (dispatch, getState) => { export const fetchTrendingHashtags = () => (dispatch) => {
dispatch(fetchTrendingHashtagsRequest()); dispatch(fetchTrendingHashtagsRequest());
api(getState) api()
.get('/api/v1/trends/tags') .get('/api/v1/trends/tags')
.then(({ data }) => dispatch(fetchTrendingHashtagsSuccess(data))) .then(({ data }) => dispatch(fetchTrendingHashtagsSuccess(data)))
.catch(err => dispatch(fetchTrendingHashtagsFail(err))); .catch(err => dispatch(fetchTrendingHashtagsFail(err)));
@ -45,10 +45,10 @@ export const fetchTrendingHashtagsFail = error => ({
skipAlert: true, skipAlert: true,
}); });
export const fetchTrendingLinks = () => (dispatch, getState) => { export const fetchTrendingLinks = () => (dispatch) => {
dispatch(fetchTrendingLinksRequest()); dispatch(fetchTrendingLinksRequest());
api(getState) api()
.get('/api/v1/trends/links') .get('/api/v1/trends/links')
.then(({ data }) => dispatch(fetchTrendingLinksSuccess(data))) .then(({ data }) => dispatch(fetchTrendingLinksSuccess(data)))
.catch(err => dispatch(fetchTrendingLinksFail(err))); .catch(err => dispatch(fetchTrendingLinksFail(err)));
@ -79,7 +79,7 @@ export const fetchTrendingStatuses = () => (dispatch, getState) => {
dispatch(fetchTrendingStatusesRequest()); dispatch(fetchTrendingStatusesRequest());
api(getState).get('/api/v1/trends/statuses').then(response => { api().get('/api/v1/trends/statuses').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(fetchTrendingStatusesSuccess(response.data, next ? next.uri : null)); dispatch(fetchTrendingStatusesSuccess(response.data, next ? next.uri : null));
@ -115,7 +115,7 @@ export const expandTrendingStatuses = () => (dispatch, getState) => {
dispatch(expandTrendingStatusesRequest()); dispatch(expandTrendingStatusesRequest());
api(getState).get(url).then(response => { api().get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next'); const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data)); dispatch(importFetchedStatuses(response.data));
dispatch(expandTrendingStatusesSuccess(response.data, next ? next.uri : null)); dispatch(expandTrendingStatusesSuccess(response.data, next ? next.uri : null));

View file

@ -1,9 +1,9 @@
import type { AxiosResponse, RawAxiosRequestHeaders } from 'axios'; import type { AxiosResponse, Method, RawAxiosRequestHeaders } from 'axios';
import axios from 'axios'; import axios from 'axios';
import LinkHeader from 'http-link-header'; import LinkHeader from 'http-link-header';
import { getAccessToken } from './initial_state';
import ready from './ready'; import ready from './ready';
import type { GetState } from './store';
export const getLinks = (response: AxiosResponse) => { export const getLinks = (response: AxiosResponse) => {
const value = response.headers.link as string | undefined; const value = response.headers.link as string | undefined;
@ -29,30 +29,22 @@ const setCSRFHeader = () => {
void ready(setCSRFHeader); void ready(setCSRFHeader);
export const authorizationTokenFromState = (getState?: GetState) => { const authorizationTokenFromInitialState = (): RawAxiosRequestHeaders => {
return ( const accessToken = getAccessToken();
getState && (getState().meta.get('access_token', '') as string | false)
);
};
const authorizationHeaderFromState = (getState?: GetState) => { if (!accessToken) return {};
const accessToken = authorizationTokenFromState(getState);
if (!accessToken) {
return {};
}
return { return {
Authorization: `Bearer ${accessToken}`, Authorization: `Bearer ${accessToken}`,
} as RawAxiosRequestHeaders; };
}; };
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
export default function api(getState: GetState) { export default function api(withAuthorization = true) {
return axios.create({ return axios.create({
headers: { headers: {
...csrfHeader, ...csrfHeader,
...authorizationHeaderFromState(getState), ...(withAuthorization ? authorizationTokenFromInitialState() : {}),
}, },
transformResponse: [ transformResponse: [
@ -66,3 +58,17 @@ export default function api(getState: GetState) {
], ],
}); });
} }
export async function apiRequest<ApiResponse = unknown>(
method: Method,
url: string,
params?: Record<string, unknown>,
) {
const { data } = await api().request<ApiResponse>({
method,
url: '/api/' + url,
data: params,
});
return data;
}

View file

@ -0,0 +1,7 @@
import { apiRequest } from 'mastodon/api';
import type { ApiRelationshipJSON } from 'mastodon/api_types/relationships';
export const apiSubmitAccountNote = (id: string, value: string) =>
apiRequest<ApiRelationshipJSON>('post', `v1/accounts/${id}/note`, {
comment: value,
});

View file

@ -0,0 +1,10 @@
import { apiRequest } from 'mastodon/api';
import type { Status, StatusVisibility } from 'mastodon/models/status';
export const apiReblog = (statusId: string, visibility: StatusVisibility) =>
apiRequest<{ reblog: Status }>('post', `v1/statuses/${statusId}/reblog`, {
visibility,
});
export const apiUnreblog = (statusId: string) =>
apiRequest<Status>('post', `v1/statuses/${statusId}/unreblog`);

View file

@ -49,7 +49,7 @@ export default class Counter extends PureComponent {
componentDidMount () { componentDidMount () {
const { measure, start_at, end_at, params } = this.props; const { measure, start_at, end_at, params } = this.props;
api().post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => { api(false).post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -27,7 +27,7 @@ export default class Dimension extends PureComponent {
componentDidMount () { componentDidMount () {
const { start_at, end_at, dimension, limit, params } = this.props; const { start_at, end_at, dimension, limit, params } = this.props;
api().post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => { api(false).post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -27,7 +27,7 @@ export default class ImpactReport extends PureComponent {
include_subdomains: true, include_subdomains: true,
}; };
api().post('/api/v1/admin/measures', { api(false).post('/api/v1/admin/measures', {
keys: ['instance_accounts', 'instance_follows', 'instance_followers'], keys: ['instance_accounts', 'instance_follows', 'instance_followers'],
start_at: null, start_at: null,
end_at: null, end_at: null,

View file

@ -105,7 +105,7 @@ class ReportReasonSelector extends PureComponent {
}; };
componentDidMount() { componentDidMount() {
api().get('/api/v1/instance').then(res => { api(false).get('/api/v1/instance').then(res => {
this.setState({ this.setState({
rules: res.data.rules, rules: res.data.rules,
}); });
@ -122,7 +122,7 @@ class ReportReasonSelector extends PureComponent {
return; return;
} }
api().put(`/api/v1/admin/reports/${id}`, { api(false).put(`/api/v1/admin/reports/${id}`, {
category, category,
rule_ids: category === 'violation' ? rule_ids : [], rule_ids: category === 'violation' ? rule_ids : [],
}).catch(err => { }).catch(err => {

View file

@ -34,7 +34,7 @@ export default class Retention extends PureComponent {
componentDidMount () { componentDidMount () {
const { start_at, end_at, frequency } = this.props; const { start_at, end_at, frequency } = this.props;
api().post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => { api(false).post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -22,7 +22,7 @@ export default class Trends extends PureComponent {
componentDidMount () { componentDidMount () {
const { limit } = this.props; const { limit } = this.props;
api().get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => { api(false).get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => {
this.setState({ this.setState({
loading: false, loading: false,
data: res.data, data: res.data,

View file

@ -14,8 +14,10 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SettingsIcon from '@/material-icons/400-24px/settings.svg?react'; import SettingsIcon from '@/material-icons/400-24px/settings.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context'; import { ButtonInTabsBar } from 'mastodon/features/ui/util/columns_context';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { useAppHistory } from './router'; import { useAppHistory } from './router';
const messages = defineMessages({ const messages = defineMessages({
@ -51,12 +53,8 @@ BackButton.propTypes = {
}; };
class ColumnHeader extends PureComponent { class ColumnHeader extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
title: PropTypes.node, title: PropTypes.node,
icon: PropTypes.string, icon: PropTypes.string,
@ -171,7 +169,7 @@ class ColumnHeader extends PureComponent {
); );
} }
if (this.context.identity.signedIn && (children || (multiColumn && this.props.onPin))) { if (this.props.identity.signedIn && (children || (multiColumn && this.props.onPin))) {
collapseButton = ( collapseButton = (
<button <button
className={collapsibleButtonClassName} className={collapsibleButtonClassName}
@ -232,4 +230,4 @@ class ColumnHeader extends PureComponent {
} }
export default injectIntl(withRouter(ColumnHeader)); export default injectIntl(withIdentity(withRouter(ColumnHeader)));

View file

@ -14,6 +14,7 @@ import CheckIcon from '@/material-icons/400-24px/check.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import emojify from 'mastodon/features/emoji/emoji'; import emojify from 'mastodon/features/emoji/emoji';
import Motion from 'mastodon/features/ui/util/optional_motion'; import Motion from 'mastodon/features/ui/util/optional_motion';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { RelativeTimestamp } from './relative_timestamp'; import { RelativeTimestamp } from './relative_timestamp';
@ -38,12 +39,8 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
}, {}); }, {});
class Poll extends ImmutablePureComponent { class Poll extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
poll: ImmutablePropTypes.map, poll: ImmutablePropTypes.map,
lang: PropTypes.string, lang: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -235,7 +232,7 @@ class Poll extends ImmutablePureComponent {
</ul> </ul>
<div className='poll__footer'> <div className='poll__footer'>
{!showResults && <button className='button button-secondary' disabled={disabled || !this.context.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>} {!showResults && <button className='button button-secondary' disabled={disabled || !this.props.identity.signedIn} onClick={this.handleVote}><FormattedMessage id='poll.vote' defaultMessage='Vote' /></button>}
{!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>} {!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>}
{showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>} {showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
{votesCount} {votesCount}
@ -247,4 +244,4 @@ class Poll extends ImmutablePureComponent {
} }
export default injectIntl(Poll); export default injectIntl(withIdentity(Poll));

View file

@ -23,6 +23,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -85,12 +86,8 @@ const mapStateToProps = (state, { status }) => ({
}); });
class StatusActionBar extends ImmutablePureComponent { class StatusActionBar extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
relationship: ImmutablePropTypes.record, relationship: ImmutablePropTypes.record,
onReply: PropTypes.func, onReply: PropTypes.func,
@ -134,7 +131,7 @@ class StatusActionBar extends ImmutablePureComponent {
]; ];
handleReplyClick = () => { handleReplyClick = () => {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
this.props.onReply(this.props.status, this.props.history); this.props.onReply(this.props.status, this.props.history);
@ -152,7 +149,7 @@ class StatusActionBar extends ImmutablePureComponent {
}; };
handleFavouriteClick = () => { handleFavouriteClick = () => {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
this.props.onFavourite(this.props.status); this.props.onFavourite(this.props.status);
@ -174,7 +171,7 @@ class StatusActionBar extends ImmutablePureComponent {
handleEmojiPickInnerButton = () => {}; handleEmojiPickInnerButton = () => {};
handleReblogClick = e => { handleReblogClick = e => {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
this.props.onReblog(this.props.status, e); this.props.onReblog(this.props.status, e);
@ -306,7 +303,7 @@ class StatusActionBar extends ImmutablePureComponent {
render () { render () {
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props; const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
const { signedIn, permissions } = this.context.identity; const { signedIn, permissions } = this.props.identity;
const publicStatus = ['public', 'unlisted', 'public_unlisted', 'login'].includes(status.get('visibility_ex')); const publicStatus = ['public', 'unlisted', 'public_unlisted', 'login'].includes(status.get('visibility_ex'));
const anonymousStatus = ['public', 'unlisted', 'public_unlisted'].includes(status.get('visibility_ex')); const anonymousStatus = ['public', 'unlisted', 'public_unlisted'].includes(status.get('visibility_ex'));
@ -546,4 +543,4 @@ class StatusActionBar extends ImmutablePureComponent {
} }
export default withRouter(connect(mapStateToProps)(injectIntl(StatusActionBar))); export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusActionBar))));

View file

@ -12,8 +12,10 @@ import { connect } from 'react-redux';
import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'; import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import PollContainer from 'mastodon/containers/poll_container'; import PollContainer from 'mastodon/containers/poll_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state'; import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top) const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
/** /**
@ -67,12 +69,8 @@ const mapStateToProps = state => ({
}); });
class StatusContent extends PureComponent { class StatusContent extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
statusContent: PropTypes.string, statusContent: PropTypes.string,
expanded: PropTypes.bool, expanded: PropTypes.bool,
@ -246,7 +244,7 @@ class StatusContent extends PureComponent {
const contentLocale = intl.locale.replace(/[_-].*/, ''); const contentLocale = intl.locale.replace(/[_-].*/, '');
const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
const renderTranslate = this.props.onTranslate && const renderTranslate = this.props.onTranslate &&
this.context.identity.signedIn && this.props.identity.signedIn &&
(['public', 'unlisted'].includes(status.get('visibility')) || status.getIn(['account', 'other_settings', 'translatable_private'])) && (['public', 'unlisted'].includes(status.get('visibility')) || status.getIn(['account', 'other_settings', 'translatable_private'])) &&
status.get('search_index').trim().length > 0 && status.get('search_index').trim().length > 0 &&
status.get('language') && status.get('language') &&
@ -333,4 +331,4 @@ class StatusContent extends PureComponent {
} }
export default withRouter(connect(mapStateToProps)(injectIntl(StatusContent))); export default withRouter(withIdentity(connect(mapStateToProps)(injectIntl(StatusContent))));

View file

@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react'; import { PureComponent } from 'react';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
@ -16,6 +15,7 @@ import { connectUserStream } from 'mastodon/actions/streaming';
import ErrorBoundary from 'mastodon/components/error_boundary'; import ErrorBoundary from 'mastodon/components/error_boundary';
import { Router } from 'mastodon/components/router'; import { Router } from 'mastodon/components/router';
import UI from 'mastodon/features/ui'; import UI from 'mastodon/features/ui';
import { IdentityContext, createIdentityContext } from 'mastodon/identity_context';
import initialState, { title as siteTitle } from 'mastodon/initial_state'; import initialState, { title as siteTitle } from 'mastodon/initial_state';
import { IntlProvider } from 'mastodon/locales'; import { IntlProvider } from 'mastodon/locales';
import { store } from 'mastodon/store'; import { store } from 'mastodon/store';
@ -32,33 +32,9 @@ if (initialState.meta.me) {
store.dispatch(fetchCircles()); store.dispatch(fetchCircles());
} }
const createIdentityContext = state => ({
signedIn: !!state.meta.me,
accountId: state.meta.me,
disabledAccountId: state.meta.disabled_account_id,
accessToken: state.meta.access_token,
permissions: state.role ? state.role.permissions : 0,
});
export default class Mastodon extends PureComponent { export default class Mastodon extends PureComponent {
static childContextTypes = {
identity: PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
accessToken: PropTypes.string,
}).isRequired,
};
identity = createIdentityContext(initialState); identity = createIdentityContext(initialState);
getChildContext() {
return {
identity: this.identity,
};
}
componentDidMount() { componentDidMount() {
if (this.identity.signedIn) { if (this.identity.signedIn) {
this.disconnect = store.dispatch(connectUserStream()); this.disconnect = store.dispatch(connectUserStream());
@ -78,19 +54,21 @@ export default class Mastodon extends PureComponent {
render () { render () {
return ( return (
<IntlProvider> <IdentityContext.Provider value={this.identity}>
<ReduxProvider store={store}> <IntlProvider>
<ErrorBoundary> <ReduxProvider store={store}>
<Router> <ErrorBoundary>
<ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}> <Router>
<Route path='/' component={UI} /> <ScrollContext shouldUpdateScroll={this.shouldUpdateScroll}>
</ScrollContext> <Route path='/' component={UI} />
</Router> </ScrollContext>
</Router>
<Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} /> <Helmet defaultTitle={title} titleTemplate={`%s - ${title}`} />
</ErrorBoundary> </ErrorBoundary>
</ReduxProvider> </ReduxProvider>
</IntlProvider> </IntlProvider>
</IdentityContext.Provider>
); );
} }

View file

@ -101,9 +101,9 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
} }
}, },

View file

@ -26,6 +26,7 @@ import { IconButton } from 'mastodon/components/icon_button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { ShortNumber } from 'mastodon/components/short_number'; import { ShortNumber } from 'mastodon/components/short_number';
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container'; import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { autoPlayGif, me, domain as localDomain, isShowItem } from 'mastodon/initial_state'; import { autoPlayGif, me, domain as localDomain, isShowItem } from 'mastodon/initial_state';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -115,6 +116,7 @@ const dateFormatOptions = {
class Header extends ImmutablePureComponent { class Header extends ImmutablePureComponent {
static propTypes = { static propTypes = {
identity: identityContextPropShape,
account: ImmutablePropTypes.record, account: ImmutablePropTypes.record,
featuredTags: PropTypes.array, featuredTags: PropTypes.array,
identity_props: ImmutablePropTypes.list, identity_props: ImmutablePropTypes.list,
@ -144,10 +146,6 @@ class Header extends ImmutablePureComponent {
...WithRouterPropTypes, ...WithRouterPropTypes,
}; };
static contextTypes = {
identity: PropTypes.object,
};
setRef = c => { setRef = c => {
this.node = c; this.node = c;
}; };
@ -277,7 +275,7 @@ class Header extends ImmutablePureComponent {
render () { render () {
const { account, hidden, intl } = this.props; const { account, hidden, intl } = this.props;
const { signedIn, permissions } = this.context.identity; const { signedIn, permissions } = this.props.identity;
if (!account) { if (!account) {
return null; return null;
@ -554,4 +552,4 @@ class Header extends ImmutablePureComponent {
} }
export default withRouter(injectIntl(Header)); export default withRouter(withIdentity(injectIntl(Header)));

View file

@ -11,7 +11,7 @@ const mapStateToProps = (state, { account }) => ({
const mapDispatchToProps = (dispatch, { account }) => ({ const mapDispatchToProps = (dispatch, { account }) => ({
onSave (value) { onSave (value) {
dispatch(submitAccountNote({ id: account.get('id'), value})); dispatch(submitAccountNote({ accountId: account.get('id'), note: value }));
}, },
}); });

View file

@ -9,6 +9,7 @@ import { connect } from 'react-redux';
import PeopleIcon from '@/material-icons/400-24px/group.svg?react'; import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state'; import { domain } from 'mastodon/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -38,16 +39,12 @@ const mapStateToProps = (state, { columnId }) => {
}; };
class CommunityTimeline extends PureComponent { class CommunityTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
}; };
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -77,7 +74,7 @@ class CommunityTimeline extends PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
dispatch(expandCommunityTimeline({ onlyMedia })); dispatch(expandCommunityTimeline({ onlyMedia }));
@ -87,7 +84,7 @@ class CommunityTimeline extends PureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (prevProps.onlyMedia !== this.props.onlyMedia) { if (prevProps.onlyMedia !== this.props.onlyMedia) {
const { dispatch, onlyMedia } = this.props; const { dispatch, onlyMedia } = this.props;
@ -161,4 +158,4 @@ class CommunityTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(CommunityTimeline)); export default withIdentity(connect(mapStateToProps)(injectIntl(CommunityTimeline)));

View file

@ -12,6 +12,7 @@ import CancelIcon from '@/material-icons/400-24px/cancel-fill.svg?react';
import CloseIcon from '@/material-icons/400-24px/close.svg?react'; import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import SearchIcon from '@/material-icons/400-24px/search.svg?react'; import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain, searchEnabled } from 'mastodon/initial_state'; import { domain, searchEnabled } from 'mastodon/initial_state';
import { HASHTAG_REGEX } from 'mastodon/utils/hashtags'; import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -33,12 +34,8 @@ const labelForRecentSearch = search => {
}; };
class Search extends PureComponent { class Search extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
value: PropTypes.string.isRequired, value: PropTypes.string.isRequired,
recent: ImmutablePropTypes.orderedSet, recent: ImmutablePropTypes.orderedSet,
submitted: PropTypes.bool, submitted: PropTypes.bool,
@ -279,7 +276,7 @@ class Search extends PureComponent {
} }
_calculateOptions (value) { _calculateOptions (value) {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
const trimmedValue = value.trim(); const trimmedValue = value.trim();
const options = []; const options = [];
@ -321,7 +318,7 @@ class Search extends PureComponent {
render () { render () {
const { intl, value, submitted, recent } = this.props; const { intl, value, submitted, recent } = this.props;
const { expanded, options, selectedOption } = this.state; const { expanded, options, selectedOption } = this.state;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
const hasValue = value.length > 0 || submitted; const hasValue = value.length > 0 || submitted;
@ -405,4 +402,4 @@ class Search extends PureComponent {
} }
export default withRouter(injectIntl(Search)); export default withRouter(withIdentity(injectIntl(Search)));

View file

@ -13,6 +13,7 @@ import SearchIcon from '@/material-icons/400-24px/search.svg?react';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import Search from 'mastodon/features/compose/containers/search_container'; import Search from 'mastodon/features/compose/containers/search_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { trendsEnabled } from 'mastodon/initial_state'; import { trendsEnabled } from 'mastodon/initial_state';
import Links from './links'; import Links from './links';
@ -32,12 +33,8 @@ const mapStateToProps = state => ({
}); });
class Explore extends PureComponent { class Explore extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
isSearching: PropTypes.bool, isSearching: PropTypes.bool,
@ -53,7 +50,7 @@ class Explore extends PureComponent {
render() { render() {
const { intl, multiColumn, isSearching } = this.props; const { intl, multiColumn, isSearching } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
@ -114,4 +111,4 @@ class Explore extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(Explore)); export default withIdentity(connect(mapStateToProps)(injectIntl(Explore)));

View file

@ -6,13 +6,14 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { useIdentity } from '@/mastodon/identity_context';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { addColumn } from 'mastodon/actions/columns'; import { addColumn } from 'mastodon/actions/columns';
import { changeSetting } from 'mastodon/actions/settings'; import { changeSetting } from 'mastodon/actions/settings';
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming'; import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines'; import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import initialState, { domain, enableLocalTimeline } from 'mastodon/initial_state'; import { domain, enableLocalTimeline } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store'; import { useAppDispatch, useAppSelector } from 'mastodon/store';
import Column from '../../components/column'; import Column from '../../components/column';
@ -24,15 +25,6 @@ const messages = defineMessages({
title: { id: 'column.firehose', defaultMessage: 'Live feeds' }, title: { id: 'column.firehose', defaultMessage: 'Live feeds' },
}); });
// TODO: use a proper React context later on
const useIdentity = () => ({
signedIn: !!initialState.meta.me,
accountId: initialState.meta.me,
disabledAccountId: initialState.meta.disabled_account_id,
accessToken: initialState.meta.access_token,
permissions: initialState.role ? initialState.role.permissions : 0,
});
const ColumnSettings = () => { const ColumnSettings = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const settings = useAppSelector((state) => state.getIn(['settings', 'firehose'])); const settings = useAppSelector((state) => state.getIn(['settings', 'firehose']));

View file

@ -26,6 +26,7 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import LinkFooter from 'mastodon/features/ui/components/link_footer'; import LinkFooter from 'mastodon/features/ui/components/link_footer';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { dtlTag, enableDtlMenu, me, showTrends } from '../../initial_state'; import { dtlTag, enableDtlMenu, me, showTrends } from '../../initial_state';
import { NavigationBar } from '../compose/components/navigation_bar'; import { NavigationBar } from '../compose/components/navigation_bar';
@ -80,12 +81,8 @@ const badgeDisplay = (number, limit) => {
}; };
class GettingStarted extends ImmutablePureComponent { class GettingStarted extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
myAccount: ImmutablePropTypes.record, myAccount: ImmutablePropTypes.record,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
@ -96,7 +93,7 @@ class GettingStarted extends ImmutablePureComponent {
componentDidMount () { componentDidMount () {
const { fetchFollowRequests } = this.props; const { fetchFollowRequests } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -107,7 +104,7 @@ class GettingStarted extends ImmutablePureComponent {
render () { render () {
const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props; const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
const navItems = []; const navItems = [];
@ -183,4 +180,4 @@ class GettingStarted extends ImmutablePureComponent {
} }
export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted)); export default withIdentity(connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted)));

View file

@ -17,6 +17,7 @@ import { fetchHashtag, followHashtag, unfollowHashtag } from 'mastodon/actions/t
import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines'; import { expandHashtagTimeline, clearTimeline } from 'mastodon/actions/timelines';
import Column from 'mastodon/components/column'; import Column from 'mastodon/components/column';
import ColumnHeader from 'mastodon/components/column_header'; import ColumnHeader from 'mastodon/components/column_header';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
@ -29,14 +30,10 @@ const mapStateToProps = (state, props) => ({
}); });
class HashtagTimeline extends PureComponent { class HashtagTimeline extends PureComponent {
disconnects = []; disconnects = [];
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -94,7 +91,7 @@ class HashtagTimeline extends PureComponent {
}; };
_subscribe (dispatch, id, tags = {}, local) { _subscribe (dispatch, id, tags = {}, local) {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -168,7 +165,7 @@ class HashtagTimeline extends PureComponent {
handleFollow = () => { handleFollow = () => {
const { dispatch, params, tag } = this.props; const { dispatch, params, tag } = this.props;
const { id } = params; const { id } = params;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (!signedIn) { if (!signedIn) {
return; return;
@ -185,7 +182,7 @@ class HashtagTimeline extends PureComponent {
const { hasUnread, columnId, multiColumn, tag } = this.props; const { hasUnread, columnId, multiColumn, tag } = this.props;
const { id, local } = this.props.params; const { id, local } = this.props.params;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
@ -225,4 +222,4 @@ class HashtagTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(HashtagTimeline); export default connect(mapStateToProps)(withIdentity(HashtagTimeline));

View file

@ -14,6 +14,7 @@ import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/an
import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { IconWithBadge } from 'mastodon/components/icon_with_badge';
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container'; import AnnouncementsContainer from 'mastodon/features/getting_started/containers/announcements_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { criticalUpdatesPending } from 'mastodon/initial_state'; import { criticalUpdatesPending } from 'mastodon/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -40,12 +41,8 @@ const mapStateToProps = state => ({
}); });
class HomeTimeline extends PureComponent { class HomeTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
@ -126,7 +123,7 @@ class HomeTimeline extends PureComponent {
render () { render () {
const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props; const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
const banners = []; const banners = [];
let announcementsButton; let announcementsButton;
@ -190,4 +187,4 @@ class HomeTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(HomeTimeline)); export default connect(mapStateToProps)(withIdentity(injectIntl(HomeTimeline)));

View file

@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { enableEmojiReaction } from 'mastodon/initial_state'; import { enableEmojiReaction } from 'mastodon/initial_state';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_REPORTS } from 'mastodon/permissions';
@ -13,13 +14,9 @@ import ClearColumnButton from './clear_column_button';
import GrantPermissionButton from './grant_permission_button'; import GrantPermissionButton from './grant_permission_button';
import SettingToggle from './setting_toggle'; import SettingToggle from './setting_toggle';
export default class ColumnSettings extends PureComponent { class ColumnSettings extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
settings: ImmutablePropTypes.map.isRequired, settings: ImmutablePropTypes.map.isRequired,
pushSettings: ImmutablePropTypes.map.isRequired, pushSettings: ImmutablePropTypes.map.isRequired,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
@ -258,7 +255,7 @@ export default class ColumnSettings extends PureComponent {
</div> </div>
</section> </section>
{((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && ( {((this.props.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) && (
<section role='group' aria-labelledby='notifications-admin-sign-up'> <section role='group' aria-labelledby='notifications-admin-sign-up'>
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3> <h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.sign_up' defaultMessage='New sign-ups:' /></h3>
@ -271,7 +268,7 @@ export default class ColumnSettings extends PureComponent {
</section> </section>
)} )}
{((this.context.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && ( {((this.props.identity.permissions & PERMISSION_MANAGE_REPORTS) === PERMISSION_MANAGE_REPORTS) && (
<section role='group' aria-labelledby='notifications-admin-report'> <section role='group' aria-labelledby='notifications-admin-report'>
<h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3> <h3 id='notifications-status'><FormattedMessage id='notifications.column_settings.admin.report' defaultMessage='New reports:' /></h3>
@ -288,3 +285,5 @@ export default class ColumnSettings extends PureComponent {
} }
} }
export default withIdentity(ColumnSettings);

View file

@ -40,12 +40,12 @@ const mapDispatchToProps = dispatch => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View file

@ -17,6 +17,7 @@ import NotificationsIcon from '@/material-icons/400-24px/notifications-fill.svg?
import { compareId } from 'mastodon/compare_id'; import { compareId } from 'mastodon/compare_id';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { submitMarkers } from '../../actions/markers'; import { submitMarkers } from '../../actions/markers';
@ -77,12 +78,8 @@ const mapStateToProps = state => ({
}); });
class Notifications extends PureComponent { class Notifications extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
columnId: PropTypes.string, columnId: PropTypes.string,
notifications: ImmutablePropTypes.list.isRequired, notifications: ImmutablePropTypes.list.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
@ -190,7 +187,7 @@ class Notifications extends PureComponent {
const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props; const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />; const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. When other people interact with you, you will see it here." />;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
let scrollableContent = null; let scrollableContent = null;
@ -299,4 +296,4 @@ class Notifications extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(Notifications)); export default connect(mapStateToProps)(withIdentity(injectIntl(Notifications)));

View file

@ -18,6 +18,7 @@ import { replyCompose } from 'mastodon/actions/compose';
import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions'; import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
import { IconButton } from 'mastodon/components/icon_button'; import { IconButton } from 'mastodon/components/icon_button';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { me, boostModal } from 'mastodon/initial_state'; import { me, boostModal } from 'mastodon/initial_state';
import { makeGetStatus } from 'mastodon/selectors'; import { makeGetStatus } from 'mastodon/selectors';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -47,12 +48,8 @@ const makeMapStateToProps = () => {
}; };
class Footer extends ImmutablePureComponent { class Footer extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
statusId: PropTypes.string.isRequired, statusId: PropTypes.string.isRequired,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -75,7 +72,7 @@ class Footer extends ImmutablePureComponent {
handleReplyClick = () => { handleReplyClick = () => {
const { dispatch, askReplyConfirmation, status, intl } = this.props; const { dispatch, askReplyConfirmation, status, intl } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
if (askReplyConfirmation) { if (askReplyConfirmation) {
@ -104,7 +101,7 @@ class Footer extends ImmutablePureComponent {
handleFavouriteClick = () => { handleFavouriteClick = () => {
const { dispatch, status } = this.props; const { dispatch, status } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
if (status.get('favourited')) { if (status.get('favourited')) {
@ -126,16 +123,16 @@ class Footer extends ImmutablePureComponent {
_performReblog = (status, privacy) => { _performReblog = (status, privacy) => {
const { dispatch } = this.props; const { dispatch } = this.props;
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = e => { handleReblogClick = e => {
const { dispatch, status } = this.props; const { dispatch, status } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else if ((e && e.shiftKey) || !boostModal) { } else if ((e && e.shiftKey) || !boostModal) {
this._performReblog(status); this._performReblog(status);
} else { } else {
@ -209,4 +206,4 @@ class Footer extends ImmutablePureComponent {
} }
export default connect(makeMapStateToProps)(withRouter(injectIntl(Footer))); export default connect(makeMapStateToProps)(withIdentity(withRouter(injectIntl(Footer))));

View file

@ -9,6 +9,7 @@ import { connect } from 'react-redux';
import PublicIcon from '@/material-icons/400-24px/public.svg?react'; import PublicIcon from '@/material-icons/400-24px/public.svg?react';
import { DismissableBanner } from 'mastodon/components/dismissable_banner'; import { DismissableBanner } from 'mastodon/components/dismissable_banner';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain } from 'mastodon/initial_state'; import { domain } from 'mastodon/initial_state';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
@ -40,16 +41,12 @@ const mapStateToProps = (state, { columnId }) => {
}; };
class PublicTimeline extends PureComponent { class PublicTimeline extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static defaultProps = { static defaultProps = {
onlyMedia: false, onlyMedia: false,
}; };
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
@ -80,7 +77,7 @@ class PublicTimeline extends PureComponent {
componentDidMount () { componentDidMount () {
const { dispatch, onlyMedia, onlyRemote } = this.props; const { dispatch, onlyMedia, onlyRemote } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
dispatch(expandPublicTimeline({ onlyMedia, onlyRemote })); dispatch(expandPublicTimeline({ onlyMedia, onlyRemote }));
@ -90,7 +87,7 @@ class PublicTimeline extends PureComponent {
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) { if (prevProps.onlyMedia !== this.props.onlyMedia || prevProps.onlyRemote !== this.props.onlyRemote) {
const { dispatch, onlyMedia, onlyRemote } = this.props; const { dispatch, onlyMedia, onlyRemote } = this.props;
@ -164,4 +161,4 @@ class PublicTimeline extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(PublicTimeline)); export default connect(mapStateToProps)(withIdentity(injectIntl(PublicTimeline)));

View file

@ -22,6 +22,7 @@ import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -77,12 +78,8 @@ const mapStateToProps = (state, { status }) => ({
}); });
class ActionBar extends PureComponent { class ActionBar extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
relationship: ImmutablePropTypes.record, relationship: ImmutablePropTypes.record,
onReply: PropTypes.func.isRequired, onReply: PropTypes.func.isRequired,
@ -243,7 +240,7 @@ class ActionBar extends PureComponent {
render () { render () {
const { status, relationship, intl } = this.props; const { status, relationship, intl } = this.props;
const { signedIn, permissions } = this.context.identity; const { signedIn, permissions } = this.props.identity;
const publicStatus = ['public', 'unlisted', 'public_unlisted', 'login'].includes(status.get('visibility_ex')); const publicStatus = ['public', 'unlisted', 'public_unlisted', 'login'].includes(status.get('visibility_ex'));
const anonymousStatus = ['public', 'unlisted', 'public_unlisted'].includes(status.get('visibility_ex')); const anonymousStatus = ['public', 'unlisted', 'public_unlisted'].includes(status.get('visibility_ex'));
@ -449,4 +446,4 @@ class ActionBar extends PureComponent {
} }
export default withRouter(connect(mapStateToProps)(injectIntl(ActionBar))); export default withRouter(connect(mapStateToProps)(withIdentity(injectIntl(ActionBar))));

View file

@ -76,12 +76,12 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}, },
onModalReblog (status, privacy) { onModalReblog (status, privacy) {
dispatch(reblog(status, privacy)); dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}, },
onReblog (status, e) { onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);

View file

@ -20,6 +20,7 @@ import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator'; import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import ScrollContainer from 'mastodon/containers/scroll_container'; import ScrollContainer from 'mastodon/containers/scroll_container';
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error'; import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
import { import {
@ -201,12 +202,8 @@ const titleFromStatus = (intl, status) => {
}; };
class Status extends ImmutablePureComponent { class Status extends ImmutablePureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
params: PropTypes.object.isRequired, params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
@ -257,7 +254,7 @@ class Status extends ImmutablePureComponent {
handleFavouriteClick = (status) => { handleFavouriteClick = (status) => {
const { dispatch } = this.props; const { dispatch } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
if (status.get('favourited')) { if (status.get('favourited')) {
@ -310,7 +307,7 @@ class Status extends ImmutablePureComponent {
handleReplyClick = (status) => { handleReplyClick = (status) => {
const { askReplyConfirmation, dispatch, intl } = this.props; const { askReplyConfirmation, dispatch, intl } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
if (askReplyConfirmation) { if (askReplyConfirmation) {
@ -338,16 +335,16 @@ class Status extends ImmutablePureComponent {
}; };
handleModalReblog = (status, privacy) => { handleModalReblog = (status, privacy) => {
this.props.dispatch(reblog(status, privacy)); this.props.dispatch(reblog({ statusId: status.get('id'), visibility: privacy }));
}; };
handleReblogClick = (status, e, force = false) => { handleReblogClick = (status, e, force = false) => {
const { dispatch } = this.props; const { dispatch } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
if (signedIn) { if (signedIn) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog({ statusId: status.get('id') }));
} else { } else {
if (!force && ((e && e.shiftKey) || !boostModal)) { if (!force && ((e && e.shiftKey) || !boostModal)) {
this.handleModalReblog(status); this.handleModalReblog(status);
@ -829,4 +826,4 @@ class Status extends ImmutablePureComponent {
} }
export default withRouter(injectIntl(connect(makeMapStateToProps)(Status))); export default withRouter(injectIntl(connect(makeMapStateToProps)(withIdentity(Status))));

View file

@ -7,16 +7,13 @@ import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/
import ServerBanner from 'mastodon/components/server_banner'; import ServerBanner from 'mastodon/components/server_banner';
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container'; import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
import SearchContainer from 'mastodon/features/compose/containers/search_container'; import SearchContainer from 'mastodon/features/compose/containers/search_container';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import LinkFooter from './link_footer'; import LinkFooter from './link_footer';
class ComposePanel extends PureComponent { class ComposePanel extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
}; };
@ -41,7 +38,7 @@ class ComposePanel extends PureComponent {
} }
render() { render() {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
return ( return (
<div className='compose-panel' onFocus={this.onFocus}> <div className='compose-panel' onFocus={this.onFocus}>
@ -65,4 +62,4 @@ class ComposePanel extends PureComponent {
} }
export default connect()(ComposePanel); export default connect()(withIdentity(ComposePanel));

View file

@ -14,6 +14,7 @@ import { fetchServer } from 'mastodon/actions/server';
import { Avatar } from 'mastodon/components/avatar'; import { Avatar } from 'mastodon/components/avatar';
import { Icon } from 'mastodon/components/icon'; import { Icon } from 'mastodon/components/icon';
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo'; import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state'; import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
const Account = connect(state => ({ const Account = connect(state => ({
@ -43,12 +44,8 @@ const mapDispatchToProps = (dispatch) => ({
}); });
class Header extends PureComponent { class Header extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
openClosedRegistrationsModal: PropTypes.func, openClosedRegistrationsModal: PropTypes.func,
location: PropTypes.object, location: PropTypes.object,
signupUrl: PropTypes.string.isRequired, signupUrl: PropTypes.string.isRequired,
@ -67,7 +64,7 @@ class Header extends PureComponent {
} }
render () { render () {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props; const { location, openClosedRegistrationsModal, signupUrl, intl } = this.props;
let content; let content;
@ -129,4 +126,4 @@ class Header extends PureComponent {
} }
export default injectIntl(withRouter(connect(mapStateToProps, mapDispatchToProps)(Header))); export default injectIntl(withRouter(withIdentity(connect(mapStateToProps, mapDispatchToProps)(Header))));

View file

@ -8,6 +8,7 @@ import { Link } from 'react-router-dom';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { openModal } from 'mastodon/actions/modal'; import { openModal } from 'mastodon/actions/modal';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state'; import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state';
import { PERMISSION_INVITE_USERS } from 'mastodon/permissions'; import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
import { logOut } from 'mastodon/utils/log_out'; import { logOut } from 'mastodon/utils/log_out';
@ -32,12 +33,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}); });
class LinkFooter extends PureComponent { class LinkFooter extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
onLogout: PropTypes.func.isRequired, onLogout: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
@ -53,7 +50,7 @@ class LinkFooter extends PureComponent {
}; };
render () { render () {
const { signedIn, permissions } = this.context.identity; const { signedIn, permissions } = this.props.identity;
const { multiColumn } = this.props; const { multiColumn } = this.props;
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS); const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
@ -108,4 +105,4 @@ class LinkFooter extends PureComponent {
} }
export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter)); export default injectIntl(withIdentity(connect(null, mapDispatchToProps)(LinkFooter)));

View file

@ -33,6 +33,7 @@ import { fetchFollowRequests } from 'mastodon/actions/accounts';
import { IconWithBadge } from 'mastodon/components/icon_with_badge'; import { IconWithBadge } from 'mastodon/components/icon_with_badge';
import { WordmarkLogo } from 'mastodon/components/logo'; import { WordmarkLogo } from 'mastodon/components/logo';
import { NavigationPortal } from 'mastodon/components/navigation_portal'; import { NavigationPortal } from 'mastodon/components/navigation_portal';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { enableDtlMenu, timelinePreview, trendsEnabled, dtlTag, enableLocalTimeline, isHideItem } from 'mastodon/initial_state'; import { enableDtlMenu, timelinePreview, trendsEnabled, dtlTag, enableLocalTimeline, isHideItem } from 'mastodon/initial_state';
import { transientSingleColumn } from 'mastodon/is_mobile'; import { transientSingleColumn } from 'mastodon/is_mobile';
@ -103,12 +104,8 @@ const FollowRequestsLink = () => {
}; };
class NavigationPanel extends Component { class NavigationPanel extends Component {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
@ -122,7 +119,7 @@ class NavigationPanel extends Component {
render () { render () {
const { intl } = this.props; const { intl } = this.props;
const { signedIn, disabledAccountId } = this.context.identity; const { signedIn, disabledAccountId } = this.props.identity;
const explorer = (trendsEnabled ? ( const explorer = (trendsEnabled ? (
<ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} /> <ColumnLink transparent to='/explore' icon='explore' iconComponent={ExploreIcon} activeIconComponent={ExploreActiveIcon} text={intl.formatMessage(messages.explore)} />
@ -226,4 +223,4 @@ class NavigationPanel extends Component {
} }
export default injectIntl(NavigationPanel); export default injectIntl(withIdentity(NavigationPanel));

View file

@ -15,6 +15,7 @@ import { focusApp, unfocusApp, changeLayout } from 'mastodon/actions/app';
import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers'; import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers';
import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding'; import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
import { PictureInPicture } from 'mastodon/features/picture_in_picture'; import { PictureInPicture } from 'mastodon/features/picture_in_picture';
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
import { layoutFromWindow } from 'mastodon/is_mobile'; import { layoutFromWindow } from 'mastodon/is_mobile';
import { WithRouterPropTypes } from 'mastodon/utils/react_router'; import { WithRouterPropTypes } from 'mastodon/utils/react_router';
@ -134,12 +135,8 @@ const keyMap = {
}; };
class SwitchingColumnsArea extends PureComponent { class SwitchingColumnsArea extends PureComponent {
static contextTypes = {
identity: PropTypes.object,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
children: PropTypes.node, children: PropTypes.node,
location: PropTypes.object, location: PropTypes.object,
singleColumn: PropTypes.bool, singleColumn: PropTypes.bool,
@ -174,7 +171,7 @@ class SwitchingColumnsArea extends PureComponent {
render () { render () {
const { children, singleColumn } = this.props; const { children, singleColumn } = this.props;
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
const pathName = this.props.location.pathname; const pathName = this.props.location.pathname;
let redirect; let redirect;
@ -282,12 +279,8 @@ class SwitchingColumnsArea extends PureComponent {
} }
class UI extends PureComponent { class UI extends PureComponent {
static contextTypes = {
identity: PropTypes.object.isRequired,
};
static propTypes = { static propTypes = {
identity: identityContextPropShape,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
children: PropTypes.node, children: PropTypes.node,
isComposing: PropTypes.bool, isComposing: PropTypes.bool,
@ -339,7 +332,7 @@ class UI extends PureComponent {
this.dragTargets.push(e.target); this.dragTargets.push(e.target);
} }
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) { if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.props.identity.signedIn) {
this.setState({ draggingOver: true }); this.setState({ draggingOver: true });
} }
}; };
@ -367,7 +360,7 @@ class UI extends PureComponent {
this.setState({ draggingOver: false }); this.setState({ draggingOver: false });
this.dragTargets = []; this.dragTargets = [];
if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) { if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.props.identity.signedIn) {
this.props.dispatch(uploadCompose(e.dataTransfer.files)); this.props.dispatch(uploadCompose(e.dataTransfer.files));
} }
}; };
@ -419,7 +412,7 @@ class UI extends PureComponent {
}; };
componentDidMount () { componentDidMount () {
const { signedIn } = this.context.identity; const { signedIn } = this.props.identity;
window.addEventListener('focus', this.handleWindowFocus, false); window.addEventListener('focus', this.handleWindowFocus, false);
window.addEventListener('blur', this.handleWindowBlur, false); window.addEventListener('blur', this.handleWindowBlur, false);
@ -621,7 +614,7 @@ class UI extends PureComponent {
<div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}> <div className={classNames('ui', { 'is-composing': isComposing })} ref={this.setRef}>
<Header /> <Header />
<SwitchingColumnsArea location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}> <SwitchingColumnsArea identity={this.props.identity} location={location} singleColumn={layout === 'mobile' || layout === 'single-column'}>
{children} {children}
</SwitchingColumnsArea> </SwitchingColumnsArea>
@ -637,4 +630,4 @@ class UI extends PureComponent {
} }
export default connect(mapStateToProps)(injectIntl(withRouter(UI))); export default connect(mapStateToProps)(injectIntl(withRouter(withIdentity(UI))));

View file

@ -0,0 +1,70 @@
import PropTypes from 'prop-types';
import { createContext, useContext } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import type { InitialState } from 'mastodon/initial_state';
export interface IdentityContextType {
signedIn: boolean;
accountId: string | undefined;
disabledAccountId: string | undefined;
permissions: number;
}
export const identityContextPropShape = PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
}).isRequired;
export const createIdentityContext = (state: InitialState) => ({
signedIn: !!state.meta.me,
accountId: state.meta.me,
disabledAccountId: state.meta.disabled_account_id,
permissions: state.role?.permissions ?? 0,
});
export const IdentityContext = createContext<IdentityContextType>({
signedIn: false,
permissions: 0,
accountId: undefined,
disabledAccountId: undefined,
});
export const useIdentity = () => useContext(IdentityContext);
export interface IdentityProps {
ref?: unknown;
wrappedComponentRef?: unknown;
}
/* Injects an `identity` props into the wrapped component to be able to use the new context in class components */
export function withIdentity<
ComponentType extends React.ComponentType<IdentityProps>,
>(Component: ComponentType) {
const displayName = `withIdentity(${Component.displayName ?? Component.name})`;
const C = (props: React.ComponentProps<ComponentType>) => {
const { wrappedComponentRef, ...remainingProps } = props;
return (
<IdentityContext.Consumer>
{(context) => {
return (
// @ts-expect-error - Dynamic covariant generic components are tough to type.
<Component
{...remainingProps}
identity={context}
ref={wrappedComponentRef}
/>
);
}}
</IdentityContext.Consumer>
);
};
C.displayName = displayName;
C.WrappedComponent = Component;
return hoistStatics(C, Component);
}

View file

@ -71,12 +71,22 @@
* @property {string} sso_redirect * @property {string} sso_redirect
*/ */
/**
* @typedef Role
* @property {string} id
* @property {string} name
* @property {string} permissions
* @property {string} color
* @property {boolean} highlighted
*/
/** /**
* @typedef InitialState * @typedef InitialState
* @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts * @property {Record<string, import("./api_types/accounts").ApiAccountJSON>} accounts
* @property {InitialStateLanguage[]} languages * @property {InitialStateLanguage[]} languages
* @property {boolean=} critical_updates_pending * @property {boolean=} critical_updates_pending
* @property {InitialStateMeta} meta * @property {InitialStateMeta} meta
* @property {Role?} role
*/ */
const element = document.getElementById('initial-state'); const element = document.getElementById('initial-state');
@ -159,4 +169,11 @@ export const criticalUpdatesPending = initialState?.critical_updates_pending;
export const statusPageUrl = getMeta('status_page_url'); export const statusPageUrl = getMeta('status_page_url');
export const sso_redirect = getMeta('sso_redirect'); export const sso_redirect = getMeta('sso_redirect');
/**
* @returns {string | undefined}
*/
export function getAccessToken() {
return getMeta('access_token');
}
export default initialState; export default initialState;

View file

@ -469,6 +469,7 @@
"notification.follow": "{name} падпісаўся на вас", "notification.follow": "{name} падпісаўся на вас",
"notification.follow_request": "{name} адправіў запыт на падпіску", "notification.follow_request": "{name} адправіў запыт на падпіску",
"notification.mention": "{name} згадаў вас", "notification.mention": "{name} згадаў вас",
"notification.moderation-warning.learn_more": "Даведацца больш",
"notification.own_poll": "Ваша апытанне скончылася", "notification.own_poll": "Ваша апытанне скончылася",
"notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася", "notification.poll": "Апытанне, дзе вы прынялі ўдзел, скончылася",
"notification.reblog": "{name} пашырыў ваш допіс", "notification.reblog": "{name} пашырыў ваш допіс",

View file

@ -50,6 +50,8 @@
"admin.dashboard.retention.cohort_size": "Mga bagong tagagamit", "admin.dashboard.retention.cohort_size": "Mga bagong tagagamit",
"alert.rate_limited.message": "Mangyaring subukan muli pagkatapos ng {retry_time, time, medium}.", "alert.rate_limited.message": "Mangyaring subukan muli pagkatapos ng {retry_time, time, medium}.",
"audio.hide": "Itago ang tunog", "audio.hide": "Itago ang tunog",
"block_modal.show_less": "Magpakita ng mas kaunti",
"block_modal.show_more": "Magpakita ng higit pa",
"block_modal.title": "Harangan ang tagagamit?", "block_modal.title": "Harangan ang tagagamit?",
"bundle_column_error.error.title": "Naku!", "bundle_column_error.error.title": "Naku!",
"bundle_column_error.network.body": "Nagkaroon ng kamalian habang sinusubukang i-karga ang pahinang ito. Maaaring dahil ito sa pansamantalang problema ng iyong koneksyon sa internet o ang server na ito.", "bundle_column_error.network.body": "Nagkaroon ng kamalian habang sinusubukang i-karga ang pahinang ito. Maaaring dahil ito sa pansamantalang problema ng iyong koneksyon sa internet o ang server na ito.",
@ -102,6 +104,7 @@
"compose_form.encryption_warning": "Ang mga post sa Mastodon ay hindi naka-encrypt nang dulo-dulo. Huwag magbahagi ng anumang sensitibong impormasyon sa Mastodon.", "compose_form.encryption_warning": "Ang mga post sa Mastodon ay hindi naka-encrypt nang dulo-dulo. Huwag magbahagi ng anumang sensitibong impormasyon sa Mastodon.",
"compose_form.hashtag_warning": "Hindi maililista ang post na ito sa anumang hashtag dahil hindi ito nakapubliko. Mga nakapublikong post lamang ang mahahanap ayon sa hashtag.", "compose_form.hashtag_warning": "Hindi maililista ang post na ito sa anumang hashtag dahil hindi ito nakapubliko. Mga nakapublikong post lamang ang mahahanap ayon sa hashtag.",
"compose_form.placeholder": "Anong nangyari?", "compose_form.placeholder": "Anong nangyari?",
"compose_form.poll.duration": "Tagal ng botohan",
"compose_form.poll.multiple": "Maraming pagpipilian", "compose_form.poll.multiple": "Maraming pagpipilian",
"compose_form.poll.single": "Piliin ang isa", "compose_form.poll.single": "Piliin ang isa",
"compose_form.reply": "Tumugon", "compose_form.reply": "Tumugon",
@ -173,6 +176,7 @@
"empty_column.list": "Wala pang laman ang listahang ito. Kapag naglathala ng mga bagong post ang mga miyembro ng listahang ito, makikita iyon dito.", "empty_column.list": "Wala pang laman ang listahang ito. Kapag naglathala ng mga bagong post ang mga miyembro ng listahang ito, makikita iyon dito.",
"empty_column.lists": "Wala ka pang mga listahan. Kapag gumawa ka ng isa, makikita yun dito.", "empty_column.lists": "Wala ka pang mga listahan. Kapag gumawa ka ng isa, makikita yun dito.",
"explore.search_results": "Mga resulta ng paghahanap", "explore.search_results": "Mga resulta ng paghahanap",
"explore.suggested_follows": "Mga tao",
"explore.title": "Tuklasin", "explore.title": "Tuklasin",
"explore.trending_links": "Mga balita", "explore.trending_links": "Mga balita",
"filter_modal.select_filter.search": "Hanapin o gumawa", "filter_modal.select_filter.search": "Hanapin o gumawa",
@ -186,9 +190,14 @@
"follow_suggestions.who_to_follow": "Sinong maaaring sundan", "follow_suggestions.who_to_follow": "Sinong maaaring sundan",
"footer.about": "Tungkol dito", "footer.about": "Tungkol dito",
"footer.get_app": "Kunin ang app", "footer.get_app": "Kunin ang app",
"footer.status": "Katayuan",
"generic.saved": "Nakaimbak", "generic.saved": "Nakaimbak",
"hashtag.column_header.tag_mode.all": "at {additional}", "hashtag.column_header.tag_mode.all": "at {additional}",
"hashtag.column_header.tag_mode.any": "o {additional}", "hashtag.column_header.tag_mode.any": "o {additional}",
"hashtag.column_settings.tag_mode.all": "Lahat ng nandito",
"hashtag.column_settings.tag_mode.any": "Ilan dito",
"hashtag.column_settings.tag_mode.none": "Wala dito",
"hashtags.and_other": "…at {count, plural, one {# iba pa} other {# na iba pa}}",
"home.column_settings.show_replies": "Ipakita ang mga tugon", "home.column_settings.show_replies": "Ipakita ang mga tugon",
"home.pending_critical_update.body": "Mangyaring i-update ang iyong serbiro ng Mastodon sa lalong madaling panahon!", "home.pending_critical_update.body": "Mangyaring i-update ang iyong serbiro ng Mastodon sa lalong madaling panahon!",
"interaction_modal.login.action": "Iuwi mo ako", "interaction_modal.login.action": "Iuwi mo ako",
@ -199,6 +208,7 @@
"intervals.full.days": "{number, plural, one {# araw} other {# na araw}}", "intervals.full.days": "{number, plural, one {# araw} other {# na araw}}",
"intervals.full.hours": "{number, plural, one {# oras} other {# na oras}}", "intervals.full.hours": "{number, plural, one {# oras} other {# na oras}}",
"intervals.full.minutes": "{number, plural, one {# minuto} other {# na minuto}}", "intervals.full.minutes": "{number, plural, one {# minuto} other {# na minuto}}",
"keyboard_shortcuts.blocked": "Buksan ang talaan ng mga nakaharang na mga tagagamit",
"keyboard_shortcuts.description": "Paglalarawan", "keyboard_shortcuts.description": "Paglalarawan",
"keyboard_shortcuts.down": "Ilipat pababa sa talaan", "keyboard_shortcuts.down": "Ilipat pababa sa talaan",
"keyboard_shortcuts.mention": "Banggitin ang may-akda", "keyboard_shortcuts.mention": "Banggitin ang may-akda",
@ -215,17 +225,23 @@
"lists.replies_policy.title": "Ipakita ang mga tugon sa:", "lists.replies_policy.title": "Ipakita ang mga tugon sa:",
"lists.subheading": "Iyong mga talaan", "lists.subheading": "Iyong mga talaan",
"loading_indicator.label": "Kumakarga…", "loading_indicator.label": "Kumakarga…",
"mute_modal.hide_from_notifications": "Itago mula sa mga abiso",
"navigation_bar.about": "Tungkol dito", "navigation_bar.about": "Tungkol dito",
"navigation_bar.blocks": "Nakaharang na mga tagagamit", "navigation_bar.blocks": "Nakaharang na mga tagagamit",
"navigation_bar.direct": "Mga palihim na banggit", "navigation_bar.direct": "Mga palihim na banggit",
"navigation_bar.discover": "Tuklasin",
"navigation_bar.explore": "Tuklasin",
"navigation_bar.favourites": "Mga paborito", "navigation_bar.favourites": "Mga paborito",
"navigation_bar.follow_requests": "Mga hiling sa pagsunod",
"navigation_bar.follows_and_followers": "Mga sinusundan at tagasunod", "navigation_bar.follows_and_followers": "Mga sinusundan at tagasunod",
"navigation_bar.lists": "Mga listahan", "navigation_bar.lists": "Mga listahan",
"navigation_bar.public_timeline": "Pinagsamang timeline",
"navigation_bar.search": "Maghanap", "navigation_bar.search": "Maghanap",
"notification.admin.report": "Iniulat ni {name} si {target}", "notification.admin.report": "Iniulat ni {name} si {target}",
"notification.follow": "Sinundan ka ni {name}", "notification.follow": "Sinundan ka ni {name}",
"notification.follow_request": "Hinihiling ni {name} na sundan ka", "notification.follow_request": "Hinihiling ni {name} na sundan ka",
"notification.mention": "Binanggit ka ni {name}", "notification.mention": "Binanggit ka ni {name}",
"notification.moderation_warning": "Mayroong kang natanggap na babala sa pagtitimpi",
"notification.relationships_severance_event.learn_more": "Matuto nang higit pa", "notification.relationships_severance_event.learn_more": "Matuto nang higit pa",
"notification_requests.accept": "Tanggapin", "notification_requests.accept": "Tanggapin",
"notification_requests.notifications_from": "Mga abiso mula kay/sa {name}", "notification_requests.notifications_from": "Mga abiso mula kay/sa {name}",
@ -234,10 +250,12 @@
"notifications.column_settings.alert": "Mga abiso sa Desktop", "notifications.column_settings.alert": "Mga abiso sa Desktop",
"notifications.column_settings.favourite": "Mga paborito:", "notifications.column_settings.favourite": "Mga paborito:",
"notifications.column_settings.follow": "Mga bagong tagasunod:", "notifications.column_settings.follow": "Mga bagong tagasunod:",
"notifications.column_settings.poll": "Resulta ng botohan:",
"notifications.column_settings.unread_notifications.category": "Hindi Nabasang mga Abiso", "notifications.column_settings.unread_notifications.category": "Hindi Nabasang mga Abiso",
"notifications.column_settings.update": "Mga pagbago:", "notifications.column_settings.update": "Mga pagbago:",
"notifications.filter.all": "Lahat", "notifications.filter.all": "Lahat",
"notifications.filter.favourites": "Mga paborito", "notifications.filter.favourites": "Mga paborito",
"notifications.filter.polls": "Resulta ng botohan",
"notifications.mark_as_read": "Markahan lahat ng abiso bilang nabasa na", "notifications.mark_as_read": "Markahan lahat ng abiso bilang nabasa na",
"notifications.policy.filter_not_followers_title": "Mga taong hindi ka susundan", "notifications.policy.filter_not_followers_title": "Mga taong hindi ka susundan",
"notifications.policy.filter_not_following_title": "Mga taong hindi mo sinusundan", "notifications.policy.filter_not_following_title": "Mga taong hindi mo sinusundan",
@ -246,6 +264,7 @@
"onboarding.profile.note_hint": "Maaari mong @bangitin ang ibang mga tao o mga #hashtag…", "onboarding.profile.note_hint": "Maaari mong @bangitin ang ibang mga tao o mga #hashtag…",
"onboarding.profile.save_and_continue": "Iimbak at magpatuloy", "onboarding.profile.save_and_continue": "Iimbak at magpatuloy",
"onboarding.share.next_steps": "Mga posibleng susunod na hakbang:", "onboarding.share.next_steps": "Mga posibleng susunod na hakbang:",
"picture_in_picture.restore": "Ilagay ito pabalik",
"poll.closed": "Sarado", "poll.closed": "Sarado",
"poll.reveal": "Ipakita ang mga resulta", "poll.reveal": "Ipakita ang mga resulta",
"poll.voted": "Binoto mo para sa sagot na ito", "poll.voted": "Binoto mo para sa sagot na ito",
@ -280,10 +299,13 @@
"report.thanks.title": "Ayaw mo bang makita ito?", "report.thanks.title": "Ayaw mo bang makita ito?",
"report.thanks.title_actionable": "Salamat sa pag-uulat, titingnan namin ito.", "report.thanks.title_actionable": "Salamat sa pag-uulat, titingnan namin ito.",
"report_notification.categories.other": "Iba pa", "report_notification.categories.other": "Iba pa",
"report_notification.categories.violation": "Paglabag sa patakaran",
"report_notification.open": "Buksan ang ulat",
"search.quick_action.open_url": "Buksan ang URL sa Mastodon", "search.quick_action.open_url": "Buksan ang URL sa Mastodon",
"search.search_or_paste": "Maghanap o ilagay ang URL", "search.search_or_paste": "Maghanap o ilagay ang URL",
"search_popout.full_text_search_disabled_message": "Hindi magagamit sa {domain}.", "search_popout.full_text_search_disabled_message": "Hindi magagamit sa {domain}.",
"search_popout.full_text_search_logged_out_message": "Magagamit lamang kapag naka-log in.", "search_popout.full_text_search_logged_out_message": "Magagamit lamang kapag naka-log in.",
"search_popout.recent": "Kamakailang mga paghahanap",
"search_results.all": "Lahat", "search_results.all": "Lahat",
"search_results.see_all": "Ipakita lahat", "search_results.see_all": "Ipakita lahat",
"server_banner.learn_more": "Matuto nang higit pa", "server_banner.learn_more": "Matuto nang higit pa",

View file

@ -19,7 +19,7 @@
"account.block_domain": "Blocar dominio {domain}", "account.block_domain": "Blocar dominio {domain}",
"account.block_short": "Blocar", "account.block_short": "Blocar",
"account.blocked": "Blocate", "account.blocked": "Blocate",
"account.browse_more_on_origin_server": "Navigar plus sur le profilo original", "account.browse_more_on_origin_server": "Percurrer plus sur le profilo original",
"account.cancel_follow_request": "Cancellar sequimento", "account.cancel_follow_request": "Cancellar sequimento",
"account.copy": "Copiar ligamine a profilo", "account.copy": "Copiar ligamine a profilo",
"account.direct": "Mentionar privatemente @{name}", "account.direct": "Mentionar privatemente @{name}",
@ -122,7 +122,7 @@
"column.direct": "Mentiones private", "column.direct": "Mentiones private",
"column.directory": "Navigar profilos", "column.directory": "Navigar profilos",
"column.domain_blocks": "Dominios blocate", "column.domain_blocks": "Dominios blocate",
"column.favourites": "Favoritos", "column.favourites": "Favorites",
"column.firehose": "Fluxos in directo", "column.firehose": "Fluxos in directo",
"column.follow_requests": "Requestas de sequimento", "column.follow_requests": "Requestas de sequimento",
"column.home": "Initio", "column.home": "Initio",
@ -204,7 +204,7 @@
"disabled_account_banner.account_settings": "Parametros de conto", "disabled_account_banner.account_settings": "Parametros de conto",
"disabled_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate.", "disabled_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate.",
"dismissable_banner.community_timeline": "Ecce le messages public le plus recente del personas con contos sur {domain}.", "dismissable_banner.community_timeline": "Ecce le messages public le plus recente del personas con contos sur {domain}.",
"dismissable_banner.dismiss": "Dimitter", "dismissable_banner.dismiss": "Clauder",
"dismissable_banner.explore_links": "Istes es le articulos de novas que se condivide le plus sur le rete social hodie. Le articulos de novas le plus recente, publicate per plus personas differente, se classifica plus in alto.", "dismissable_banner.explore_links": "Istes es le articulos de novas que se condivide le plus sur le rete social hodie. Le articulos de novas le plus recente, publicate per plus personas differente, se classifica plus in alto.",
"dismissable_banner.explore_statuses": "Ecce le messages de tote le rete social que gania popularitate hodie. Le messages plus nove con plus impulsos e favorites se classifica plus in alto.", "dismissable_banner.explore_statuses": "Ecce le messages de tote le rete social que gania popularitate hodie. Le messages plus nove con plus impulsos e favorites se classifica plus in alto.",
"dismissable_banner.explore_tags": "Ecce le hashtags que gania popularitate sur le rete social hodie. Le hashtags usate per plus personas differente se classifica plus in alto.", "dismissable_banner.explore_tags": "Ecce le hashtags que gania popularitate sur le rete social hodie. Le hashtags usate per plus personas differente se classifica plus in alto.",
@ -212,8 +212,8 @@
"domain_block_modal.block": "Blocar le servitor", "domain_block_modal.block": "Blocar le servitor",
"domain_block_modal.block_account_instead": "Blocar @{name} in su loco", "domain_block_modal.block_account_instead": "Blocar @{name} in su loco",
"domain_block_modal.they_can_interact_with_old_posts": "Le personas de iste servitor pote interager con tu messages ancian.", "domain_block_modal.they_can_interact_with_old_posts": "Le personas de iste servitor pote interager con tu messages ancian.",
"domain_block_modal.they_cant_follow": "Nulle persona ab iste servitor pote sequer te.", "domain_block_modal.they_cant_follow": "Necuno de iste servitor pote sequer te.",
"domain_block_modal.they_wont_know": "Illes non sapera que illes ha essite blocate.", "domain_block_modal.they_wont_know": "Ille non sapera que ille ha essite blocate.",
"domain_block_modal.title": "Blocar dominio?", "domain_block_modal.title": "Blocar dominio?",
"domain_block_modal.you_will_lose_followers": "Omne sequitores ab iste servitor essera removite.", "domain_block_modal.you_will_lose_followers": "Omne sequitores ab iste servitor essera removite.",
"domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes ab usatores sur iste servitor.", "domain_block_modal.you_wont_see_posts": "Tu non videra messages e notificationes ab usatores sur iste servitor.",
@ -307,7 +307,7 @@
"follow_request.reject": "Rejectar", "follow_request.reject": "Rejectar",
"follow_requests.unlocked_explanation": "Benque tu conto non es serrate, le personal de {domain} pensa que es un bon idea que tu revide manualmente le sequente requestas de iste contos.", "follow_requests.unlocked_explanation": "Benque tu conto non es serrate, le personal de {domain} pensa que es un bon idea que tu revide manualmente le sequente requestas de iste contos.",
"follow_suggestions.curated_suggestion": "Selection del equipa", "follow_suggestions.curated_suggestion": "Selection del equipa",
"follow_suggestions.dismiss": "Non monstrar novemente", "follow_suggestions.dismiss": "Non monstrar de novo",
"follow_suggestions.featured_longer": "Seligite con cura per le equipa de {domain}", "follow_suggestions.featured_longer": "Seligite con cura per le equipa de {domain}",
"follow_suggestions.friends_of_friends_longer": "Popular inter le gente que tu seque", "follow_suggestions.friends_of_friends_longer": "Popular inter le gente que tu seque",
"follow_suggestions.hints.featured": "Iste profilo ha essite seligite manualmente per le equipa de {domain}.", "follow_suggestions.hints.featured": "Iste profilo ha essite seligite manualmente per le equipa de {domain}.",
@ -412,7 +412,7 @@
"lightbox.next": "Sequente", "lightbox.next": "Sequente",
"lightbox.previous": "Precedente", "lightbox.previous": "Precedente",
"limited_account_hint.action": "Monstrar profilo in omne caso", "limited_account_hint.action": "Monstrar profilo in omne caso",
"limited_account_hint.title": "Iste profilo esseva celate per le moderatores de {domain}.", "limited_account_hint.title": "Iste profilo ha essite celate per le moderatores de {domain}.",
"link_preview.author": "Per {name}", "link_preview.author": "Per {name}",
"lists.account.add": "Adder al lista", "lists.account.add": "Adder al lista",
"lists.account.remove": "Remover del lista", "lists.account.remove": "Remover del lista",
@ -432,12 +432,12 @@
"loading_indicator.label": "Cargante…", "loading_indicator.label": "Cargante…",
"media_gallery.toggle_visible": "{number, plural, one {Celar imagine} other {Celar imagines}}", "media_gallery.toggle_visible": "{number, plural, one {Celar imagine} other {Celar imagines}}",
"moved_to_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate perque tu ha cambiate de conto a {movedToAccount}.", "moved_to_account_banner.text": "Tu conto {disabledAccount} es actualmente disactivate perque tu ha cambiate de conto a {movedToAccount}.",
"mute_modal.hide_from_notifications": "Celar ab notificationes", "mute_modal.hide_from_notifications": "Celar in notificationes",
"mute_modal.hide_options": "Celar optiones", "mute_modal.hide_options": "Celar optiones",
"mute_modal.indefinite": "Usque io dissilentia iste persona", "mute_modal.indefinite": "Usque io dissilentia iste persona",
"mute_modal.show_options": "Monstrar optiones", "mute_modal.show_options": "Monstrar optiones",
"mute_modal.they_can_mention_and_follow": "Illes pote mentionar te e sequer te, ma tu non potera vider los.", "mute_modal.they_can_mention_and_follow": "Ille pote mentionar te e sequer te, ma tu non potera vider le.",
"mute_modal.they_wont_know": "Illes non sapera que illes ha essite silentiate.", "mute_modal.they_wont_know": "Ille non sapera que ille ha essite silentiate.",
"mute_modal.title": "Silentiar le usator?", "mute_modal.title": "Silentiar le usator?",
"mute_modal.you_wont_see_mentions": "Tu non videra le messages que mentiona iste persona.", "mute_modal.you_wont_see_mentions": "Tu non videra le messages que mentiona iste persona.",
"mute_modal.you_wont_see_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.", "mute_modal.you_wont_see_posts": "Iste persona pote totevia vider tu messages, ma tu non videra le sues.",
@ -451,13 +451,13 @@
"navigation_bar.discover": "Discoperir", "navigation_bar.discover": "Discoperir",
"navigation_bar.domain_blocks": "Dominios blocate", "navigation_bar.domain_blocks": "Dominios blocate",
"navigation_bar.explore": "Explorar", "navigation_bar.explore": "Explorar",
"navigation_bar.favourites": "Favoritos", "navigation_bar.favourites": "Favorites",
"navigation_bar.filters": "Parolas silentiate", "navigation_bar.filters": "Parolas silentiate",
"navigation_bar.follow_requests": "Requestas de sequimento", "navigation_bar.follow_requests": "Requestas de sequimento",
"navigation_bar.followed_tags": "Hashtags sequite", "navigation_bar.followed_tags": "Hashtags sequite",
"navigation_bar.follows_and_followers": "Sequites e sequitores", "navigation_bar.follows_and_followers": "Sequites e sequitores",
"navigation_bar.lists": "Listas", "navigation_bar.lists": "Listas",
"navigation_bar.logout": "Clauder le session", "navigation_bar.logout": "Clauder session",
"navigation_bar.mutes": "Usatores silentiate", "navigation_bar.mutes": "Usatores silentiate",
"navigation_bar.opened_in_classic_interface": "Messages, contos e altere paginas specific es aperite per predefinition in le interfacie web classic.", "navigation_bar.opened_in_classic_interface": "Messages, contos e altere paginas specific es aperite per predefinition in le interfacie web classic.",
"navigation_bar.personal": "Personal", "navigation_bar.personal": "Personal",
@ -501,7 +501,7 @@
"notifications.column_settings.admin.report": "Nove signalationes:", "notifications.column_settings.admin.report": "Nove signalationes:",
"notifications.column_settings.admin.sign_up": "Nove inscriptiones:", "notifications.column_settings.admin.sign_up": "Nove inscriptiones:",
"notifications.column_settings.alert": "Notificationes de scriptorio", "notifications.column_settings.alert": "Notificationes de scriptorio",
"notifications.column_settings.favourite": "Favoritos:", "notifications.column_settings.favourite": "Favorites:",
"notifications.column_settings.filter_bar.advanced": "Monstrar tote le categorias", "notifications.column_settings.filter_bar.advanced": "Monstrar tote le categorias",
"notifications.column_settings.filter_bar.category": "Barra de filtro rapide", "notifications.column_settings.filter_bar.category": "Barra de filtro rapide",
"notifications.column_settings.follow": "Nove sequitores:", "notifications.column_settings.follow": "Nove sequitores:",
@ -518,7 +518,7 @@
"notifications.column_settings.update": "Modificationes:", "notifications.column_settings.update": "Modificationes:",
"notifications.filter.all": "Toto", "notifications.filter.all": "Toto",
"notifications.filter.boosts": "Impulsos", "notifications.filter.boosts": "Impulsos",
"notifications.filter.favourites": "Favoritos", "notifications.filter.favourites": "Favorites",
"notifications.filter.follows": "Sequites", "notifications.filter.follows": "Sequites",
"notifications.filter.mentions": "Mentiones", "notifications.filter.mentions": "Mentiones",
"notifications.filter.polls": "Resultatos del sondage", "notifications.filter.polls": "Resultatos del sondage",
@ -717,7 +717,7 @@
"status.edited": "Ultime modification le {date}", "status.edited": "Ultime modification le {date}",
"status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}", "status.edited_x_times": "Modificate {count, plural, one {{count} vice} other {{count} vices}}",
"status.embed": "Incastrar", "status.embed": "Incastrar",
"status.favourite": "Adder al favoritos", "status.favourite": "Adder al favorites",
"status.favourites": "{count, plural, one {favorite} other {favorites}}", "status.favourites": "{count, plural, one {favorite} other {favorites}}",
"status.filter": "Filtrar iste message", "status.filter": "Filtrar iste message",
"status.filtered": "Filtrate", "status.filtered": "Filtrate",

View file

@ -90,7 +90,7 @@
"attachments_list.unprocessed": "(neapstrādāti)", "attachments_list.unprocessed": "(neapstrādāti)",
"audio.hide": "Slēpt audio", "audio.hide": "Slēpt audio",
"block_modal.remote_users_caveat": "Mēs vaicāsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr atbilstība nav nodrošināta, jo atsevišķi serveri var apstrādāt bloķēšanu citādi. Publiski ieraksti joprojām var būt redzami lietotājiem, kuri nav pieteikušies.", "block_modal.remote_users_caveat": "Mēs vaicāsim serverim {domain} ņemt vērā Tavu lēmumu. Tomēr atbilstība nav nodrošināta, jo atsevišķi serveri var apstrādāt bloķēšanu citādi. Publiski ieraksti joprojām var būt redzami lietotājiem, kuri nav pieteikušies.",
"block_modal.show_less": "Parādīt vairāk", "block_modal.show_less": "Rādīt mazāk",
"block_modal.show_more": "Parādīt mazāk", "block_modal.show_more": "Parādīt mazāk",
"boost_modal.combo": "Nospied {combo}, lai nākamreiz šo izlaistu", "boost_modal.combo": "Nospied {combo}, lai nākamreiz šo izlaistu",
"bundle_column_error.copy_stacktrace": "Kopēt kļūdu ziņojumu", "bundle_column_error.copy_stacktrace": "Kopēt kļūdu ziņojumu",
@ -309,7 +309,7 @@
"hashtag.counter_by_uses_today": "{count, plural, zero {{counter} ierakstu} one {{counter} ieraksts} other {{counter} ieraksti}} šodien", "hashtag.counter_by_uses_today": "{count, plural, zero {{counter} ierakstu} one {{counter} ieraksts} other {{counter} ieraksti}} šodien",
"hashtag.follow": "Sekot tēmturim", "hashtag.follow": "Sekot tēmturim",
"hashtag.unfollow": "Pārstāt sekot tēmturim", "hashtag.unfollow": "Pārstāt sekot tēmturim",
"hashtags.and_other": "..un {count, plural, other {# vairāk}}", "hashtags.and_other": "… un {count, plural, other {vēl #}}",
"home.column_settings.show_reblogs": "Rādīt pastiprinātos ierakstus", "home.column_settings.show_reblogs": "Rādīt pastiprinātos ierakstus",
"home.column_settings.show_replies": "Rādīt atbildes", "home.column_settings.show_replies": "Rādīt atbildes",
"home.hide_announcements": "Slēpt paziņojumus", "home.hide_announcements": "Slēpt paziņojumus",
@ -491,7 +491,7 @@
"onboarding.actions.go_to_home": "Dodieties uz manu mājas plūsmu", "onboarding.actions.go_to_home": "Dodieties uz manu mājas plūsmu",
"onboarding.compose.template": "Sveiki, #Mastodon!", "onboarding.compose.template": "Sveiki, #Mastodon!",
"onboarding.follows.empty": "Diemžēl pašlaik nevar parādīt rezultātus. Vari mēģināt izmantot meklēšanu vai pārlūkot izpētes lapu, lai atrastu cilvēkus, kuriem sekot, vai vēlāk mēģināt vēlreiz.", "onboarding.follows.empty": "Diemžēl pašlaik nevar parādīt rezultātus. Vari mēģināt izmantot meklēšanu vai pārlūkot izpētes lapu, lai atrastu cilvēkus, kuriem sekot, vai vēlāk mēģināt vēlreiz.",
"onboarding.follows.lead": "Tava mājas plūsma ir galvenais veids, kā izbaudīt Mastodon. Jo vairāk cilvēku sekosi, jo aktīvāk un interesantāk tas būs. Lai sāktu, šeit ir daži ieteikumi:", "onboarding.follows.lead": "Tava mājas plūsma ir galvenais veids, kā pieredzēt Mastodon. Jo vairāk cilvēkiem sekosi, jo dzīvīgāka un aizraujošāka tā būs. Lai sāktu, šeit ir daži ieteikumi:",
"onboarding.follows.title": "Pielāgo savu mājas barotni", "onboarding.follows.title": "Pielāgo savu mājas barotni",
"onboarding.profile.discoverable": "Padarīt manu profilu atklājamu", "onboarding.profile.discoverable": "Padarīt manu profilu atklājamu",
"onboarding.profile.display_name": "Attēlojamais vārds", "onboarding.profile.display_name": "Attēlojamais vārds",

View file

@ -468,6 +468,7 @@
"notification.follow": "{name} подписался (-лась) на вас", "notification.follow": "{name} подписался (-лась) на вас",
"notification.follow_request": "{name} отправил запрос на подписку", "notification.follow_request": "{name} отправил запрос на подписку",
"notification.mention": "{name} упомянул(а) вас", "notification.mention": "{name} упомянул(а) вас",
"notification.moderation_warning.action_delete_statuses": "Некоторые из ваших публикаций были удалены.",
"notification.own_poll": "Ваш опрос закончился", "notification.own_poll": "Ваш опрос закончился",
"notification.poll": "Опрос, в котором вы приняли участие, завершился", "notification.poll": "Опрос, в котором вы приняли участие, завершился",
"notification.reblog": "{name} продвинул(а) ваш пост", "notification.reblog": "{name} продвинул(а) ваш пост",

View file

@ -474,6 +474,7 @@
"notification.follow_request": "{name} vam želi slediti", "notification.follow_request": "{name} vam želi slediti",
"notification.mention": "{name} vas je omenil/a", "notification.mention": "{name} vas je omenil/a",
"notification.moderation-warning.learn_more": "Več o tem", "notification.moderation-warning.learn_more": "Več o tem",
"notification.moderation_warning": "Prejeli ste opozorilo moderatorjev",
"notification.moderation_warning.action_delete_statuses": "Nekatere vaše objave so odstranjene.", "notification.moderation_warning.action_delete_statuses": "Nekatere vaše objave so odstranjene.",
"notification.moderation_warning.action_disable": "Vaš račun je bil onemogočen.", "notification.moderation_warning.action_disable": "Vaš račun je bil onemogočen.",
"notification.moderation_warning.action_mark_statuses_as_sensitive": "Nekatere vaše objave so bile označene kot občutljive.", "notification.moderation_warning.action_mark_statuses_as_sensitive": "Nekatere vaše objave so bile označene kot občutljive.",

View file

@ -158,7 +158,7 @@
"compose_form.poll.option_placeholder": "ตัวเลือก {number}", "compose_form.poll.option_placeholder": "ตัวเลือก {number}",
"compose_form.poll.single": "เลือกอย่างใดอย่างหนึ่ง", "compose_form.poll.single": "เลือกอย่างใดอย่างหนึ่ง",
"compose_form.poll.switch_to_multiple": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตหลายตัวเลือก", "compose_form.poll.switch_to_multiple": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตหลายตัวเลือก",
"compose_form.poll.switch_to_single": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตตัวเลือกเดียว", "compose_form.poll.switch_to_single": "เปลี่ยนการสำรวจความคิดเห็นเป็นอนุญาตตัวเลือกเดียว",
"compose_form.poll.type": "ลักษณะ", "compose_form.poll.type": "ลักษณะ",
"compose_form.publish": "โพสต์", "compose_form.publish": "โพสต์",
"compose_form.publish_form": "โพสต์ใหม่", "compose_form.publish_form": "โพสต์ใหม่",

View file

@ -6,7 +6,6 @@ import { layoutFromWindow } from 'mastodon/is_mobile';
const initialState = ImmutableMap({ const initialState = ImmutableMap({
streaming_api_base_url: null, streaming_api_base_url: null,
access_token: null,
layout: layoutFromWindow(), layout: layoutFromWindow(),
permissions: '0', permissions: '0',
}); });
@ -14,7 +13,8 @@ const initialState = ImmutableMap({
export default function meta(state = initialState, action) { export default function meta(state = initialState, action) {
switch(action.type) { switch(action.type) {
case STORE_HYDRATE: case STORE_HYDRATE:
return state.merge(action.state.get('meta')).set('permissions', action.state.getIn(['role', 'permissions'])); // we do not want `access_token` to be stored in the state
return state.merge(action.state.get('meta')).delete('access_token').set('permissions', action.state.getIn(['role', 'permissions']));
case changeLayout.type: case changeLayout.type:
return state.set('layout', action.payload.layout); return state.set('layout', action.payload.layout);
default: default:

View file

@ -9,10 +9,6 @@ import {
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
import { normalizeStatusTranslation } from '../actions/importer/normalizer'; import { normalizeStatusTranslation } from '../actions/importer/normalizer';
import { import {
REBLOG_REQUEST,
REBLOG_FAIL,
UNREBLOG_REQUEST,
UNREBLOG_FAIL,
FAVOURITE_REQUEST, FAVOURITE_REQUEST,
FAVOURITE_FAIL, FAVOURITE_FAIL,
UNFAVOURITE_REQUEST, UNFAVOURITE_REQUEST,
@ -22,6 +18,10 @@ import {
UNBOOKMARK_REQUEST, UNBOOKMARK_REQUEST,
UNBOOKMARK_FAIL, UNBOOKMARK_FAIL,
} from '../actions/interactions'; } from '../actions/interactions';
import {
reblog,
unreblog,
} from '../actions/interactions_typed';
import { import {
STATUS_MUTE_SUCCESS, STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS, STATUS_UNMUTE_SUCCESS,
@ -99,6 +99,7 @@ const statusTranslateUndo = (state, id) => {
const initialState = ImmutableMap(); const initialState = ImmutableMap();
/** @type {import('@reduxjs/toolkit').Reducer<typeof initialState>} */
export default function statuses(state = initialState, action) { export default function statuses(state = initialState, action) {
switch(action.type) { switch(action.type) {
case STATUS_FETCH_REQUEST: case STATUS_FETCH_REQUEST:
@ -129,14 +130,6 @@ export default function statuses(state = initialState, action) {
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], false);
case UNBOOKMARK_FAIL: case UNBOOKMARK_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true); return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'bookmarked'], true);
case REBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], true);
case REBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_REQUEST:
return state.setIn([action.status.get('id'), 'reblogged'], false);
case UNREBLOG_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], true);
case STATUS_MUTE_SUCCESS: case STATUS_MUTE_SUCCESS:
return state.setIn([action.id, 'muted'], true); return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS: case STATUS_UNMUTE_SUCCESS:
@ -168,6 +161,15 @@ export default function statuses(state = initialState, action) {
case STATUS_EMOJI_REACTION_UPDATE: case STATUS_EMOJI_REACTION_UPDATE:
return updateStatusEmojiReaction(state, action.emoji_reaction); return updateStatusEmojiReaction(state, action.emoji_reaction);
default: default:
return state; if(reblog.pending.match(action))
return state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else if(reblog.rejected.match(action))
return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.pending.match(action))
return state.setIn([action.meta.arg.statusId, 'reblogged'], false);
else if(unreblog.rejected.match(action))
return state.get(action.meta.arg.statusId) === undefined ? state : state.setIn([action.meta.arg.statusId, 'reblogged'], true);
else
return state;
} }
} }

View file

@ -2,6 +2,8 @@ import { createAsyncThunk } from '@reduxjs/toolkit';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports // eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import type { BaseThunkAPI } from '@reduxjs/toolkit/dist/createAsyncThunk';
import type { AppDispatch, RootState } from './store'; import type { AppDispatch, RootState } from './store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>(); export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
@ -13,8 +15,192 @@ export interface AsyncThunkRejectValue {
error?: unknown; error?: unknown;
} }
interface AppMeta {
skipLoading?: boolean;
}
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState; state: RootState;
dispatch: AppDispatch; dispatch: AppDispatch;
rejectValue: AsyncThunkRejectValue; rejectValue: AsyncThunkRejectValue;
}>(); }>();
type AppThunkApi = Pick<
BaseThunkAPI<
RootState,
unknown,
AppDispatch,
AsyncThunkRejectValue,
AppMeta,
AppMeta
>,
'getState' | 'dispatch'
>;
interface AppThunkOptions {
skipLoading?: boolean;
}
const createBaseAsyncThunk = createAsyncThunk.withTypes<{
state: RootState;
dispatch: AppDispatch;
rejectValue: AsyncThunkRejectValue;
fulfilledMeta: AppMeta;
rejectedMeta: AppMeta;
}>();
export function createThunk<Arg = void, Returned = void>(
name: string,
creator: (arg: Arg, api: AppThunkApi) => Returned | Promise<Returned>,
options: AppThunkOptions = {},
) {
return createBaseAsyncThunk(
name,
async (
arg: Arg,
{ getState, dispatch, fulfillWithValue, rejectWithValue },
) => {
try {
const result = await creator(arg, { dispatch, getState });
return fulfillWithValue(result, {
skipLoading: options.skipLoading,
});
} catch (error) {
return rejectWithValue({ error }, { skipLoading: true });
}
},
{
getPendingMeta() {
if (options.skipLoading) return { skipLoading: true };
return {};
},
},
);
}
const discardLoadDataInPayload = Symbol('discardLoadDataInPayload');
type DiscardLoadData = typeof discardLoadDataInPayload;
type OnData<LoadDataResult, ReturnedData> = (
data: LoadDataResult,
api: AppThunkApi & {
discardLoadData: DiscardLoadData;
},
) => ReturnedData | DiscardLoadData | Promise<ReturnedData | DiscardLoadData>;
// Overload when there is no `onData` method, the payload is the `onData` result
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
// Overload when the `onData` method returns discardLoadDataInPayload, then the payload is empty
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?:
| AppThunkOptions
| OnData<LoadDataResult, DiscardLoadData>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, void>>;
// Overload when the `onData` method returns nothing, then the mayload is the `onData` result
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, void>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, LoadDataResult>>;
// Overload when there is an `onData` method returning something
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
thunkOptions?: AppThunkOptions,
): ReturnType<typeof createThunk<Args, Returned>>;
/**
* This function creates a Redux Thunk that handles loading data asynchronously (usually from the API), dispatching `pending`, `fullfilled` and `rejected` actions.
*
* You can run a callback on the `onData` results to either dispatch side effects or modify the payload.
*
* It is a wrapper around RTK's [`createAsyncThunk`](https://redux-toolkit.js.org/api/createAsyncThunk)
* @param name Prefix for the actions types
* @param loadData Function that loads the data. It's (object) argument will become the thunk's argument
* @param onDataOrThunkOptions
* Callback called on the results from `loadData`.
*
* First argument will be the return from `loadData`.
*
* Second argument is an object with: `dispatch`, `getState` and `discardLoadData`.
* It can return:
* - `undefined` (or no explicit return), meaning that the `onData` results will be the payload
* - `discardLoadData` to discard the `onData` results and return an empty payload
* - anything else, which will be the payload
*
* You can also omit this parameter and pass `thunkOptions` directly
* @param maybeThunkOptions
* Additional Mastodon specific options for the thunk. Currently supports:
* - `skipLoading` to avoid showing the loading bar when the request is in progress
* @returns The created thunk
*/
export function createDataLoadingThunk<
LoadDataResult,
Args extends Record<string, unknown>,
Returned,
>(
name: string,
loadData: (args: Args) => Promise<LoadDataResult>,
onDataOrThunkOptions?: AppThunkOptions | OnData<LoadDataResult, Returned>,
maybeThunkOptions?: AppThunkOptions,
) {
let onData: OnData<LoadDataResult, Returned> | undefined;
let thunkOptions: AppThunkOptions | undefined;
if (typeof onDataOrThunkOptions === 'function') onData = onDataOrThunkOptions;
else if (typeof onDataOrThunkOptions === 'object')
thunkOptions = onDataOrThunkOptions;
if (maybeThunkOptions) {
thunkOptions = maybeThunkOptions;
}
return createThunk<Args, Returned>(
name,
async (arg, { getState, dispatch }) => {
const data = await loadData(arg);
if (!onData) return data as Returned;
const result = await onData(data, {
dispatch,
getState,
discardLoadData: discardLoadDataInPayload,
});
// if there is no return in `onData`, we return the `onData` result
if (typeof result === 'undefined') return data as Returned;
// the user explicitely asked to discard the payload
else if (result === discardLoadDataInPayload)
return undefined as Returned;
else return result;
},
thunkOptions,
);
}

View file

@ -2,6 +2,8 @@
import WebSocketClient from '@gamestdio/websocket'; import WebSocketClient from '@gamestdio/websocket';
import { getAccessToken } from './initial_state';
/** /**
* @type {WebSocketClient | undefined} * @type {WebSocketClient | undefined}
*/ */
@ -147,9 +149,11 @@ const channelNameWithInlineParams = (channelName, params) => {
// @ts-expect-error // @ts-expect-error
export const connectStream = (channelName, params, callbacks) => (dispatch, getState) => { export const connectStream = (channelName, params, callbacks) => (dispatch, getState) => {
const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']); const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']);
const accessToken = getState().getIn(['meta', 'access_token']); const accessToken = getAccessToken();
const { onConnect, onReceive, onDisconnect } = callbacks(dispatch, getState); const { onConnect, onReceive, onDisconnect } = callbacks(dispatch, getState);
if(!accessToken) throw new Error("Trying to connect to the streaming server but no access token is available.");
// If we cannot use a websockets connection, we must fall back // If we cannot use a websockets connection, we must fall back
// to using individual connections for each channel // to using individual connections for each channel
if (!streamingAPIBaseURL.startsWith('ws')) { if (!streamingAPIBaseURL.startsWith('ws')) {

View file

@ -1,7 +1,3 @@
import PropTypes from 'prop-types';
import type { PropsWithChildren } from 'react';
import { Component } from 'react';
import { IntlProvider } from 'react-intl'; import { IntlProvider } from 'react-intl';
import { MemoryRouter } from 'react-router'; import { MemoryRouter } from 'react-router';
@ -9,44 +5,26 @@ import { MemoryRouter } from 'react-router';
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
import { render as rtlRender } from '@testing-library/react'; import { render as rtlRender } from '@testing-library/react';
class FakeIdentityWrapper extends Component< import { IdentityContext } from './identity_context';
PropsWithChildren<{ signedIn: boolean }>
> {
static childContextTypes = {
identity: PropTypes.shape({
signedIn: PropTypes.bool.isRequired,
accountId: PropTypes.string,
disabledAccountId: PropTypes.string,
accessToken: PropTypes.string,
}).isRequired,
};
getChildContext() {
return {
identity: {
signedIn: this.props.signedIn,
accountId: '123',
accessToken: 'test-access-token',
},
};
}
render() {
return this.props.children;
}
}
function render( function render(
ui: React.ReactElement, ui: React.ReactElement,
{ locale = 'en', signedIn = true, ...renderOptions } = {}, { locale = 'en', signedIn = true, ...renderOptions } = {},
) { ) {
const fakeIdentity = {
signedIn: signedIn,
accountId: '123',
disabledAccountId: undefined,
permissions: 0,
};
const Wrapper = (props: { children: React.ReactNode }) => { const Wrapper = (props: { children: React.ReactNode }) => {
return ( return (
<MemoryRouter> <MemoryRouter>
<IntlProvider locale={locale}> <IntlProvider locale={locale}>
<FakeIdentityWrapper signedIn={signedIn}> <IdentityContext.Provider value={fakeIdentity}>
{props.children} {props.children}
</FakeIdentityWrapper> </IdentityContext.Provider>
</IntlProvider> </IntlProvider>
</MemoryRouter> </MemoryRouter>
); );

View file

@ -4521,7 +4521,7 @@ a.status-card {
color: $primary-text-color; color: $primary-text-color;
} }
.icon { .icon-sliders {
transform: rotate(60deg); transform: rotate(60deg);
} }
} }
@ -4532,7 +4532,7 @@ a.status-card {
} }
} }
.no-reduce-motion .column-header__button .icon { .no-reduce-motion .column-header__button .icon-sliders {
transition: transform 150ms ease-in-out; transition: transform 150ms ease-in-out;
} }

View file

@ -3,6 +3,8 @@
class ActivityPub::Parser::StatusParser class ActivityPub::Parser::StatusParser
include JsonLdHelper include JsonLdHelper
NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze
# @param [Hash] json # @param [Hash] json
# @param [Hash] options # @param [Hash] options
# @option options [String] :followers_collection # @option options [String] :followers_collection
@ -118,10 +120,13 @@ class ActivityPub::Parser::StatusParser
end end
def language def language
@language ||= original_language || (misskey_software? ? 'ja' : nil) lang = raw_language_code || (misskey_software? ? 'ja' : nil)
lang.presence && NORMALIZED_LOCALE_NAMES.fetch(lang.downcase.to_sym, lang)
end end
def original_language private
def raw_language_code
if content_language_map? if content_language_map?
@object['contentMap'].keys.first @object['contentMap'].keys.first
elsif name_language_map? elsif name_language_map?
@ -131,8 +136,6 @@ class ActivityPub::Parser::StatusParser
end end
end end
private
def audience_to def audience_to
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) } as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end end

View file

@ -23,6 +23,12 @@ module ApplicationExtension
redirect_uri.lines.first.strip redirect_uri.lines.first.strip
end end
def redirect_uris
# Doorkeeper stores the redirect_uri value as a newline delimeted list in
# the database:
redirect_uri.split
end
def push_to_streaming_api def push_to_streaming_api
# TODO: #28793 Combine into a single topic # TODO: #28793 Combine into a single topic
payload = Oj.dump(event: :kill) payload = Oj.dump(event: :kill)

View file

@ -9,10 +9,10 @@ class Vacuum::ImportsVacuum
private private
def clean_unconfirmed_imports! def clean_unconfirmed_imports!
BulkImport.state_unconfirmed.where('created_at <= ?', 10.minutes.ago).reorder(nil).in_batches.delete_all BulkImport.state_unconfirmed.where(created_at: ..10.minutes.ago).reorder(nil).in_batches.delete_all
end end
def clean_old_imports! def clean_old_imports!
BulkImport.where('created_at <= ?', 1.week.ago).reorder(nil).in_batches.delete_all BulkImport.where(created_at: ..1.week.ago).reorder(nil).in_batches.delete_all
end end
end end

View file

@ -34,7 +34,7 @@ class Vacuum::StatusesVacuum
def statuses_scope def statuses_scope
scope = Status.unscoped.kept scope = Status.unscoped.kept
.joins(:account).merge(Account.remote) .joins(:account).merge(Account.remote)
.where('statuses.id < ?', retention_period_as_id) .where(statuses: { id: ...retention_period_as_id })
if Setting.delete_content_cache_without_reaction if Setting.delete_content_cache_without_reaction
scope = scope.where.not(id: favourited_statuses) scope = scope.where.not(id: favourited_statuses)

View file

@ -5,7 +5,6 @@ class UserMailer < Devise::Mailer
helper :accounts helper :accounts
helper :application helper :application
helper :mascot
helper :formatting helper :formatting
helper :instance helper :instance
helper :routing helper :routing

View file

@ -61,6 +61,7 @@ class Admin::ActionLogFilter
unsuspend_account: { target_type: 'Account', action: 'unsuspend' }.freeze, unsuspend_account: { target_type: 'Account', action: 'unsuspend' }.freeze,
update_announcement: { target_type: 'Announcement', action: 'update' }.freeze, update_announcement: { target_type: 'Announcement', action: 'update' }.freeze,
update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze, update_custom_emoji: { target_type: 'CustomEmoji', action: 'update' }.freeze,
update_report: { target_type: 'Report', action: 'update' }.freeze,
update_status: { target_type: 'Status', action: 'update' }.freeze, update_status: { target_type: 'Status', action: 'update' }.freeze,
force_cw_status: { target_type: 'Status', action: 'force_cw' }.freeze, force_cw_status: { target_type: 'Status', action: 'force_cw' }.freeze,
force_sensitive_status: { target_type: 'Status', action: 'force_sensitive' }.freeze, force_sensitive_status: { target_type: 'Status', action: 'force_sensitive' }.freeze,

View file

@ -20,7 +20,6 @@ class CanonicalEmailBlock < ApplicationRecord
validates :canonical_email_hash, presence: true, uniqueness: true validates :canonical_email_hash, presence: true, uniqueness: true
scope :matching_email, ->(email) { where(canonical_email_hash: email_to_canonical_email_hash(email)) } scope :matching_email, ->(email) { where(canonical_email_hash: email_to_canonical_email_hash(email)) }
scope :matching_account, ->(account) { matching_email(account&.user_email).or(where(reference_account: account)) }
def to_log_human_identifier def to_log_human_identifier
canonical_email_hash canonical_email_hash

View file

@ -4,7 +4,7 @@ module Expireable
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
scope :expired, -> { where.not(expires_at: nil).where('expires_at < ?', Time.now.utc) } scope :expired, -> { where.not(expires_at: nil).where(expires_at: ...Time.now.utc) }
def expires_in def expires_in
return @expires_in if defined?(@expires_in) return @expires_in if defined?(@expires_in)

View file

@ -24,7 +24,7 @@ class Invite < ApplicationRecord
belongs_to :user, inverse_of: :invites belongs_to :user, inverse_of: :invites
has_many :users, inverse_of: :invite, dependent: nil has_many :users, inverse_of: :invite, dependent: nil
scope :available, -> { where(expires_at: nil).or(where('expires_at >= ?', Time.now.utc)) } scope :available, -> { where(expires_at: nil).or(where(expires_at: Time.now.utc..)) }
validates :comment, length: { maximum: COMMENT_SIZE_LIMIT } validates :comment, length: { maximum: COMMENT_SIZE_LIMIT }

View file

@ -4,6 +4,6 @@ class BackupPolicy < ApplicationPolicy
MIN_AGE = 6.days MIN_AGE = 6.days
def create? def create?
user_signed_in? && current_user.backups.where('created_at >= ?', MIN_AGE.ago).count.zero? user_signed_in? && current_user.backups.where(created_at: MIN_AGE.ago..).count.zero?
end end
end end

View file

@ -1,24 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
class REST::ApplicationSerializer < ActiveModel::Serializer class REST::ApplicationSerializer < ActiveModel::Serializer
attributes :id, :name, :website, :scopes, :redirect_uri, attributes :id, :name, :website, :scopes, :redirect_uris
:client_id, :client_secret
# NOTE: Deprecated in 4.3.0, needs to be removed in 5.0.0 # NOTE: Deprecated in 4.3.0, needs to be removed in 5.0.0
attribute :vapid_key attribute :vapid_key
# We should consider this property deprecated for 4.3.0
attribute :redirect_uri
def id def id
object.id.to_s object.id.to_s
end end
def client_id
object.uid
end
def client_secret
object.secret
end
def website def website
object.website.presence object.website.presence
end end

Some files were not shown because too many files have changed in this diff Show more