about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/actions
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch/actions')
-rw-r--r--app/javascript/flavours/glitch/actions/account_notes.js14
-rw-r--r--app/javascript/flavours/glitch/actions/accounts.js166
-rw-r--r--app/javascript/flavours/glitch/actions/alerts.js6
-rw-r--r--app/javascript/flavours/glitch/actions/blocks.js16
-rw-r--r--app/javascript/flavours/glitch/actions/bookmarks.js16
-rw-r--r--app/javascript/flavours/glitch/actions/boosts.js2
-rw-r--r--app/javascript/flavours/glitch/actions/columns.js6
-rw-r--r--app/javascript/flavours/glitch/actions/compose.js146
-rw-r--r--app/javascript/flavours/glitch/actions/custom_emojis.js8
-rw-r--r--app/javascript/flavours/glitch/actions/domain_blocks.js32
-rw-r--r--app/javascript/flavours/glitch/actions/emojis.js2
-rw-r--r--app/javascript/flavours/glitch/actions/favourites.js16
-rw-r--r--app/javascript/flavours/glitch/actions/height_cache.js4
-rw-r--r--app/javascript/flavours/glitch/actions/interactions.js80
-rw-r--r--app/javascript/flavours/glitch/actions/local_settings.js10
-rw-r--r--app/javascript/flavours/glitch/actions/markers.js12
-rw-r--r--app/javascript/flavours/glitch/actions/modal.js4
-rw-r--r--app/javascript/flavours/glitch/actions/mutes.js16
-rw-r--r--app/javascript/flavours/glitch/actions/notifications.js40
-rw-r--r--app/javascript/flavours/glitch/actions/onboarding.js2
-rw-r--r--app/javascript/flavours/glitch/actions/pin_statuses.js8
-rw-r--r--app/javascript/flavours/glitch/actions/push_notifications/registerer.js2
-rw-r--r--app/javascript/flavours/glitch/actions/search.js12
-rw-r--r--app/javascript/flavours/glitch/actions/server.js27
-rw-r--r--app/javascript/flavours/glitch/actions/settings.js4
-rw-r--r--app/javascript/flavours/glitch/actions/statuses.js48
-rw-r--r--app/javascript/flavours/glitch/actions/store.js4
-rw-r--r--app/javascript/flavours/glitch/actions/suggestions.js8
-rw-r--r--app/javascript/flavours/glitch/actions/tags.js82
-rw-r--r--app/javascript/flavours/glitch/actions/timelines.js20
30 files changed, 481 insertions, 332 deletions
diff --git a/app/javascript/flavours/glitch/actions/account_notes.js b/app/javascript/flavours/glitch/actions/account_notes.js
index 059ed9e80..62a6b4cbb 100644
--- a/app/javascript/flavours/glitch/actions/account_notes.js
+++ b/app/javascript/flavours/glitch/actions/account_notes.js
@@ -21,27 +21,27 @@ export function submitAccountNote() {
       dispatch(submitAccountNoteSuccess(response.data));
     }).catch(error => dispatch(submitAccountNoteFail(error)));
   };
-};
+}
 
 export function submitAccountNoteRequest() {
   return {
     type: ACCOUNT_NOTE_SUBMIT_REQUEST,
   };
-};
+}
 
 export function submitAccountNoteSuccess(relationship) {
   return {
     type: ACCOUNT_NOTE_SUBMIT_SUCCESS,
     relationship,
   };
-};
+}
 
 export function submitAccountNoteFail(error) {
   return {
     type: ACCOUNT_NOTE_SUBMIT_FAIL,
     error,
   };
-};
+}
 
 export function initEditAccountNote(account) {
   return (dispatch, getState) => {
@@ -53,17 +53,17 @@ export function initEditAccountNote(account) {
       comment,
     });
   };
-};
+}
 
 export function cancelAccountNote() {
   return {
     type: ACCOUNT_NOTE_CANCEL,
   };
-};
+}
 
 export function changeAccountNoteComment(comment) {
   return {
     type: ACCOUNT_NOTE_CHANGE_COMMENT,
     comment,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js
index dc670e50a..6b5b2ade5 100644
--- a/app/javascript/flavours/glitch/actions/accounts.js
+++ b/app/javascript/flavours/glitch/actions/accounts.js
@@ -108,7 +108,7 @@ export function fetchAccount(id) {
       dispatch(fetchAccountFail(id, error));
     });
   };
-};
+}
 
 export const lookupAccount = acct => (dispatch, getState) => {
   dispatch(lookupAccountRequest(acct));
@@ -143,13 +143,13 @@ export function fetchAccountRequest(id) {
     type: ACCOUNT_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchAccountSuccess() {
   return {
     type: ACCOUNT_FETCH_SUCCESS,
   };
-};
+}
 
 export function fetchAccountFail(id, error) {
   return {
@@ -158,7 +158,7 @@ export function fetchAccountFail(id, error) {
     error,
     skipAlert: true,
   };
-};
+}
 
 export function followAccount(id, options = { reblogs: true }) {
   return (dispatch, getState) => {
@@ -173,7 +173,7 @@ export function followAccount(id, options = { reblogs: true }) {
       dispatch(followAccountFail(error, locked));
     });
   };
-};
+}
 
 export function unfollowAccount(id) {
   return (dispatch, getState) => {
@@ -185,7 +185,7 @@ export function unfollowAccount(id) {
       dispatch(unfollowAccountFail(error));
     });
   };
-};
+}
 
 export function followAccountRequest(id, locked) {
   return {
@@ -194,7 +194,7 @@ export function followAccountRequest(id, locked) {
     locked,
     skipLoading: true,
   };
-};
+}
 
 export function followAccountSuccess(relationship, alreadyFollowing) {
   return {
@@ -203,7 +203,7 @@ export function followAccountSuccess(relationship, alreadyFollowing) {
     alreadyFollowing,
     skipLoading: true,
   };
-};
+}
 
 export function followAccountFail(error, locked) {
   return {
@@ -212,7 +212,7 @@ export function followAccountFail(error, locked) {
     locked,
     skipLoading: true,
   };
-};
+}
 
 export function unfollowAccountRequest(id) {
   return {
@@ -220,7 +220,7 @@ export function unfollowAccountRequest(id) {
     id,
     skipLoading: true,
   };
-};
+}
 
 export function unfollowAccountSuccess(relationship, statuses) {
   return {
@@ -229,7 +229,7 @@ export function unfollowAccountSuccess(relationship, statuses) {
     statuses,
     skipLoading: true,
   };
-};
+}
 
 export function unfollowAccountFail(error) {
   return {
@@ -237,7 +237,7 @@ export function unfollowAccountFail(error) {
     error,
     skipLoading: true,
   };
-};
+}
 
 export function blockAccount(id) {
   return (dispatch, getState) => {
@@ -250,7 +250,7 @@ export function blockAccount(id) {
       dispatch(blockAccountFail(id, error));
     });
   };
-};
+}
 
 export function unblockAccount(id) {
   return (dispatch, getState) => {
@@ -262,14 +262,14 @@ export function unblockAccount(id) {
       dispatch(unblockAccountFail(id, error));
     });
   };
-};
+}
 
 export function blockAccountRequest(id) {
   return {
     type: ACCOUNT_BLOCK_REQUEST,
     id,
   };
-};
+}
 
 export function blockAccountSuccess(relationship, statuses) {
   return {
@@ -277,35 +277,35 @@ export function blockAccountSuccess(relationship, statuses) {
     relationship,
     statuses,
   };
-};
+}
 
 export function blockAccountFail(error) {
   return {
     type: ACCOUNT_BLOCK_FAIL,
     error,
   };
-};
+}
 
 export function unblockAccountRequest(id) {
   return {
     type: ACCOUNT_UNBLOCK_REQUEST,
     id,
   };
-};
+}
 
 export function unblockAccountSuccess(relationship) {
   return {
     type: ACCOUNT_UNBLOCK_SUCCESS,
     relationship,
   };
-};
+}
 
 export function unblockAccountFail(error) {
   return {
     type: ACCOUNT_UNBLOCK_FAIL,
     error,
   };
-};
+}
 
 
 export function muteAccount(id, notifications, duration=0) {
@@ -319,7 +319,7 @@ export function muteAccount(id, notifications, duration=0) {
       dispatch(muteAccountFail(id, error));
     });
   };
-};
+}
 
 export function unmuteAccount(id) {
   return (dispatch, getState) => {
@@ -331,14 +331,14 @@ export function unmuteAccount(id) {
       dispatch(unmuteAccountFail(id, error));
     });
   };
-};
+}
 
 export function muteAccountRequest(id) {
   return {
     type: ACCOUNT_MUTE_REQUEST,
     id,
   };
-};
+}
 
 export function muteAccountSuccess(relationship, statuses) {
   return {
@@ -346,35 +346,35 @@ export function muteAccountSuccess(relationship, statuses) {
     relationship,
     statuses,
   };
-};
+}
 
 export function muteAccountFail(error) {
   return {
     type: ACCOUNT_MUTE_FAIL,
     error,
   };
-};
+}
 
 export function unmuteAccountRequest(id) {
   return {
     type: ACCOUNT_UNMUTE_REQUEST,
     id,
   };
-};
+}
 
 export function unmuteAccountSuccess(relationship) {
   return {
     type: ACCOUNT_UNMUTE_SUCCESS,
     relationship,
   };
-};
+}
 
 export function unmuteAccountFail(error) {
   return {
     type: ACCOUNT_UNMUTE_FAIL,
     error,
   };
-};
+}
 
 
 export function fetchFollowers(id) {
@@ -391,14 +391,14 @@ export function fetchFollowers(id) {
       dispatch(fetchFollowersFail(id, error));
     });
   };
-};
+}
 
 export function fetchFollowersRequest(id) {
   return {
     type: FOLLOWERS_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchFollowersSuccess(id, accounts, next) {
   return {
@@ -407,7 +407,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchFollowersFail(id, error) {
   return {
@@ -416,7 +416,7 @@ export function fetchFollowersFail(id, error) {
     error,
     skipNotFound: true,
   };
-};
+}
 
 export function expandFollowers(id) {
   return (dispatch, getState) => {
@@ -438,14 +438,14 @@ export function expandFollowers(id) {
       dispatch(expandFollowersFail(id, error));
     });
   };
-};
+}
 
 export function expandFollowersRequest(id) {
   return {
     type: FOLLOWERS_EXPAND_REQUEST,
     id,
   };
-};
+}
 
 export function expandFollowersSuccess(id, accounts, next) {
   return {
@@ -454,7 +454,7 @@ export function expandFollowersSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandFollowersFail(id, error) {
   return {
@@ -462,7 +462,7 @@ export function expandFollowersFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function fetchFollowing(id) {
   return (dispatch, getState) => {
@@ -478,14 +478,14 @@ export function fetchFollowing(id) {
       dispatch(fetchFollowingFail(id, error));
     });
   };
-};
+}
 
 export function fetchFollowingRequest(id) {
   return {
     type: FOLLOWING_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchFollowingSuccess(id, accounts, next) {
   return {
@@ -494,7 +494,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchFollowingFail(id, error) {
   return {
@@ -503,7 +503,7 @@ export function fetchFollowingFail(id, error) {
     error,
     skipNotFound: true,
   };
-};
+}
 
 export function expandFollowing(id) {
   return (dispatch, getState) => {
@@ -525,14 +525,14 @@ export function expandFollowing(id) {
       dispatch(expandFollowingFail(id, error));
     });
   };
-};
+}
 
 export function expandFollowingRequest(id) {
   return {
     type: FOLLOWING_EXPAND_REQUEST,
     id,
   };
-};
+}
 
 export function expandFollowingSuccess(id, accounts, next) {
   return {
@@ -541,7 +541,7 @@ export function expandFollowingSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandFollowingFail(id, error) {
   return {
@@ -549,7 +549,7 @@ export function expandFollowingFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function fetchRelationships(accountIds) {
   return (dispatch, getState) => {
@@ -570,7 +570,7 @@ export function fetchRelationships(accountIds) {
       dispatch(fetchRelationshipsFail(error));
     });
   };
-};
+}
 
 export function fetchRelationshipsRequest(ids) {
   return {
@@ -578,7 +578,7 @@ export function fetchRelationshipsRequest(ids) {
     ids,
     skipLoading: true,
   };
-};
+}
 
 export function fetchRelationshipsSuccess(relationships) {
   return {
@@ -586,7 +586,7 @@ export function fetchRelationshipsSuccess(relationships) {
     relationships,
     skipLoading: true,
   };
-};
+}
 
 export function fetchRelationshipsFail(error) {
   return {
@@ -595,7 +595,7 @@ export function fetchRelationshipsFail(error) {
     skipLoading: true,
     skipNotFound: true,
   };
-};
+}
 
 export function fetchFollowRequests() {
   return (dispatch, getState) => {
@@ -607,13 +607,13 @@ export function fetchFollowRequests() {
       dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
     }).catch(error => dispatch(fetchFollowRequestsFail(error)));
   };
-};
+}
 
 export function fetchFollowRequestsRequest() {
   return {
     type: FOLLOW_REQUESTS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchFollowRequestsSuccess(accounts, next) {
   return {
@@ -621,14 +621,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchFollowRequestsFail(error) {
   return {
     type: FOLLOW_REQUESTS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandFollowRequests() {
   return (dispatch, getState) => {
@@ -646,13 +646,13 @@ export function expandFollowRequests() {
       dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
     }).catch(error => dispatch(expandFollowRequestsFail(error)));
   };
-};
+}
 
 export function expandFollowRequestsRequest() {
   return {
     type: FOLLOW_REQUESTS_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandFollowRequestsSuccess(accounts, next) {
   return {
@@ -660,14 +660,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandFollowRequestsFail(error) {
   return {
     type: FOLLOW_REQUESTS_EXPAND_FAIL,
     error,
   };
-};
+}
 
 export function authorizeFollowRequest(id) {
   return (dispatch, getState) => {
@@ -678,21 +678,21 @@ export function authorizeFollowRequest(id) {
       .then(() => dispatch(authorizeFollowRequestSuccess(id)))
       .catch(error => dispatch(authorizeFollowRequestFail(id, error)));
   };
-};
+}
 
 export function authorizeFollowRequestRequest(id) {
   return {
     type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
     id,
   };
-};
+}
 
 export function authorizeFollowRequestSuccess(id) {
   return {
     type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
     id,
   };
-};
+}
 
 export function authorizeFollowRequestFail(id, error) {
   return {
@@ -700,7 +700,7 @@ export function authorizeFollowRequestFail(id, error) {
     id,
     error,
   };
-};
+}
 
 
 export function rejectFollowRequest(id) {
@@ -712,21 +712,21 @@ export function rejectFollowRequest(id) {
       .then(() => dispatch(rejectFollowRequestSuccess(id)))
       .catch(error => dispatch(rejectFollowRequestFail(id, error)));
   };
-};
+}
 
 export function rejectFollowRequestRequest(id) {
   return {
     type: FOLLOW_REQUEST_REJECT_REQUEST,
     id,
   };
-};
+}
 
 export function rejectFollowRequestSuccess(id) {
   return {
     type: FOLLOW_REQUEST_REJECT_SUCCESS,
     id,
   };
-};
+}
 
 export function rejectFollowRequestFail(id, error) {
   return {
@@ -734,7 +734,7 @@ export function rejectFollowRequestFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function pinAccount(id) {
   return (dispatch, getState) => {
@@ -746,7 +746,7 @@ export function pinAccount(id) {
       dispatch(pinAccountFail(error));
     });
   };
-};
+}
 
 export function unpinAccount(id) {
   return (dispatch, getState) => {
@@ -758,49 +758,49 @@ export function unpinAccount(id) {
       dispatch(unpinAccountFail(error));
     });
   };
-};
+}
 
 export function pinAccountRequest(id) {
   return {
     type: ACCOUNT_PIN_REQUEST,
     id,
   };
-};
+}
 
 export function pinAccountSuccess(relationship) {
   return {
     type: ACCOUNT_PIN_SUCCESS,
     relationship,
   };
-};
+}
 
 export function pinAccountFail(error) {
   return {
     type: ACCOUNT_PIN_FAIL,
     error,
   };
-};
+}
 
 export function unpinAccountRequest(id) {
   return {
     type: ACCOUNT_UNPIN_REQUEST,
     id,
   };
-};
+}
 
 export function unpinAccountSuccess(relationship) {
   return {
     type: ACCOUNT_UNPIN_SUCCESS,
     relationship,
   };
-};
+}
 
 export function unpinAccountFail(error) {
   return {
     type: ACCOUNT_UNPIN_FAIL,
     error,
   };
-};
+}
 
 export const revealAccount = id => ({
   type: ACCOUNT_REVEAL,
@@ -811,18 +811,18 @@ export function fetchPinnedAccounts() {
   return (dispatch, getState) => {
     dispatch(fetchPinnedAccountsRequest());
 
-    api(getState).get(`/api/v1/endorsements`, { params: { limit: 0 } }).then(response => {
+    api(getState).get('/api/v1/endorsements', { params: { limit: 0 } }).then(response => {
       dispatch(importFetchedAccounts(response.data));
       dispatch(fetchPinnedAccountsSuccess(response.data));
     }).catch(err => dispatch(fetchPinnedAccountsFail(err)));
   };
-};
+}
 
 export function fetchPinnedAccountsRequest() {
   return {
     type: PINNED_ACCOUNTS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchPinnedAccountsSuccess(accounts, next) {
   return {
@@ -830,14 +830,14 @@ export function fetchPinnedAccountsSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchPinnedAccountsFail(error) {
   return {
     type: PINNED_ACCOUNTS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function fetchPinnedAccountsSuggestions(q) {
   return (dispatch, getState) => {
@@ -853,7 +853,7 @@ export function fetchPinnedAccountsSuggestions(q) {
       dispatch(fetchPinnedAccountsSuggestionsReady(q, response.data));
     });
   };
-};
+}
 
 export function fetchPinnedAccountsSuggestionsReady(query, accounts) {
   return {
@@ -861,24 +861,24 @@ export function fetchPinnedAccountsSuggestionsReady(query, accounts) {
     query,
     accounts,
   };
-};
+}
 
 export function clearPinnedAccountsSuggestions() {
   return {
     type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR,
   };
-};
+}
 
 export function changePinnedAccountsSuggestions(value) {
   return {
     type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE,
     value,
-  }
-};
+  };
+}
 
 export function resetPinnedAccountsEditor() {
   return {
     type: PINNED_ACCOUNTS_EDITOR_RESET,
   };
-};
+}
 
diff --git a/app/javascript/flavours/glitch/actions/alerts.js b/app/javascript/flavours/glitch/actions/alerts.js
index 1670f9c10..0220b0af5 100644
--- a/app/javascript/flavours/glitch/actions/alerts.js
+++ b/app/javascript/flavours/glitch/actions/alerts.js
@@ -17,13 +17,13 @@ export function dismissAlert(alert) {
     type: ALERT_DISMISS,
     alert,
   };
-};
+}
 
 export function clearAlert() {
   return {
     type: ALERT_CLEAR,
   };
-};
+}
 
 export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, message_values = undefined) {
   return {
@@ -32,7 +32,7 @@ export function showAlert(title = messages.unexpectedTitle, message = messages.u
     message,
     message_values,
   };
-};
+}
 
 export function showAlertForError(error, skipNotFound = false) {
   if (error.response) {
diff --git a/app/javascript/flavours/glitch/actions/blocks.js b/app/javascript/flavours/glitch/actions/blocks.js
index fd9881302..192aa3ce4 100644
--- a/app/javascript/flavours/glitch/actions/blocks.js
+++ b/app/javascript/flavours/glitch/actions/blocks.js
@@ -24,13 +24,13 @@ export function fetchBlocks() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(fetchBlocksFail(error)));
   };
-};
+}
 
 export function fetchBlocksRequest() {
   return {
     type: BLOCKS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchBlocksSuccess(accounts, next) {
   return {
@@ -38,14 +38,14 @@ export function fetchBlocksSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchBlocksFail(error) {
   return {
     type: BLOCKS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandBlocks() {
   return (dispatch, getState) => {
@@ -64,13 +64,13 @@ export function expandBlocks() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(expandBlocksFail(error)));
   };
-};
+}
 
 export function expandBlocksRequest() {
   return {
     type: BLOCKS_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandBlocksSuccess(accounts, next) {
   return {
@@ -78,14 +78,14 @@ export function expandBlocksSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandBlocksFail(error) {
   return {
     type: BLOCKS_EXPAND_FAIL,
     error,
   };
-};
+}
 
 export function initBlockModal(account) {
   return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/bookmarks.js b/app/javascript/flavours/glitch/actions/bookmarks.js
index 544ed2ff2..3c8eec546 100644
--- a/app/javascript/flavours/glitch/actions/bookmarks.js
+++ b/app/javascript/flavours/glitch/actions/bookmarks.js
@@ -25,13 +25,13 @@ export function fetchBookmarkedStatuses() {
       dispatch(fetchBookmarkedStatusesFail(error));
     });
   };
-};
+}
 
 export function fetchBookmarkedStatusesRequest() {
   return {
     type: BOOKMARKED_STATUSES_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchBookmarkedStatusesSuccess(statuses, next) {
   return {
@@ -39,14 +39,14 @@ export function fetchBookmarkedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function fetchBookmarkedStatusesFail(error) {
   return {
     type: BOOKMARKED_STATUSES_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandBookmarkedStatuses() {
   return (dispatch, getState) => {
@@ -66,13 +66,13 @@ export function expandBookmarkedStatuses() {
       dispatch(expandBookmarkedStatusesFail(error));
     });
   };
-};
+}
 
 export function expandBookmarkedStatusesRequest() {
   return {
     type: BOOKMARKED_STATUSES_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandBookmarkedStatusesSuccess(statuses, next) {
   return {
@@ -80,11 +80,11 @@ export function expandBookmarkedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function expandBookmarkedStatusesFail(error) {
   return {
     type: BOOKMARKED_STATUSES_EXPAND_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/boosts.js b/app/javascript/flavours/glitch/actions/boosts.js
index 6e14065d6..c0f0f3acc 100644
--- a/app/javascript/flavours/glitch/actions/boosts.js
+++ b/app/javascript/flavours/glitch/actions/boosts.js
@@ -11,7 +11,7 @@ export function initBoostModal(props) {
 
     dispatch({
       type: BOOSTS_INIT_MODAL,
-      privacy
+      privacy,
     });
 
     dispatch(openModal('BOOST', props));
diff --git a/app/javascript/flavours/glitch/actions/columns.js b/app/javascript/flavours/glitch/actions/columns.js
index 9b87415fb..302c3f0f9 100644
--- a/app/javascript/flavours/glitch/actions/columns.js
+++ b/app/javascript/flavours/glitch/actions/columns.js
@@ -15,7 +15,7 @@ export function addColumn(id, params) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 export function removeColumn(uuid) {
   return dispatch => {
@@ -26,7 +26,7 @@ export function removeColumn(uuid) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 export function moveColumn(uuid, direction) {
   return dispatch => {
@@ -38,7 +38,7 @@ export function moveColumn(uuid, direction) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 export function changeColumnParams(uuid, path, value) {
   return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 7a4af4cda..9c0ef83df 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -101,20 +101,20 @@ export function setComposeToStatus(status, text, spoiler_text, content_type) {
     spoiler_text,
     content_type,
   };
-};
+}
 
 export function changeCompose(text) {
   return {
     type: COMPOSE_CHANGE,
     text: text,
   };
-};
+}
 
 export function cycleElefriendCompose() {
   return {
     type: COMPOSE_CYCLE_ELEFRIEND,
   };
-};
+}
 
 export function replyCompose(status, routerHistory) {
   return (dispatch, getState) => {
@@ -127,19 +127,19 @@ export function replyCompose(status, routerHistory) {
 
     ensureComposeIsVisible(getState, routerHistory);
   };
-};
+}
 
 export function cancelReplyCompose() {
   return {
     type: COMPOSE_REPLY_CANCEL,
   };
-};
+}
 
 export function resetCompose() {
   return {
     type: COMPOSE_RESET,
   };
-};
+}
 
 export function mentionCompose(account, routerHistory) {
   return (dispatch, getState) => {
@@ -150,7 +150,7 @@ export function mentionCompose(account, routerHistory) {
 
     ensureComposeIsVisible(getState, routerHistory);
   };
-};
+}
 
 export function directCompose(account, routerHistory) {
   return (dispatch, getState) => {
@@ -161,7 +161,7 @@ export function directCompose(account, routerHistory) {
 
     ensureComposeIsVisible(getState, routerHistory);
   };
-};
+}
 
 export function submitCompose(routerHistory) {
   return function (dispatch, getState) {
@@ -181,6 +181,26 @@ export function submitCompose(routerHistory) {
 
     dispatch(submitComposeRequest());
 
+    // If we're editing a post with media attachments, those have not
+    // necessarily been changed on the server. Do it now in the same
+    // API call.
+    let media_attributes;
+    if (statusId !== null) {
+      media_attributes = media.map(item => {
+        let focus;
+
+        if (item.getIn(['meta', 'focus'])) {
+          focus = `${item.getIn(['meta', 'focus', 'x']).toFixed(2)},${item.getIn(['meta', 'focus', 'y']).toFixed(2)}`;
+        }
+
+        return {
+          id: item.get('id'),
+          description: item.get('description'),
+          focus,
+        };
+      });
+    }
+
     api(getState).request({
       url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
       method: statusId === null ? 'post' : 'put',
@@ -189,6 +209,7 @@ export function submitCompose(routerHistory) {
         content_type: getState().getIn(['compose', 'content_type']),
         in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
         media_ids: media.map(item => item.get('id')),
+        media_attributes,
         sensitive: getState().getIn(['compose', 'sensitive']) || (spoilerText.length > 0 && media.size !== 0),
         spoiler_text: spoilerText,
         visibility: getState().getIn(['compose', 'privacy']),
@@ -244,34 +265,34 @@ export function submitCompose(routerHistory) {
       dispatch(submitComposeFail(error));
     });
   };
-};
+}
 
 export function submitComposeRequest() {
   return {
     type: COMPOSE_SUBMIT_REQUEST,
   };
-};
+}
 
 export function submitComposeSuccess(status) {
   return {
     type: COMPOSE_SUBMIT_SUCCESS,
     status: status,
   };
-};
+}
 
 export function submitComposeFail(error) {
   return {
     type: COMPOSE_SUBMIT_FAIL,
     error: error,
   };
-};
+}
 
 export function doodleSet(options) {
   return {
     type: COMPOSE_DOODLE_SET,
     options: options,
   };
-};
+}
 
 export function uploadCompose(files) {
   return function (dispatch, getState) {
@@ -334,9 +355,9 @@ export function uploadCompose(files) {
           }
         });
       }).catch(error => dispatch(uploadComposeFail(error)));
-    };
+    }
   };
-};
+}
 
 export const uploadComposeProcessing = () => ({
   type: COMPOSE_UPLOAD_PROCESSING,
@@ -394,14 +415,14 @@ export function initMediaEditModal(id) {
 
     dispatch(openModal('FOCAL_POINT', { id }));
   };
-};
+}
 
 export function onChangeMediaDescription(description) {
   return {
     type: COMPOSE_CHANGE_MEDIA_DESCRIPTION,
     description,
   };
-};
+}
 
 export function onChangeMediaFocus(focusX, focusY) {
   return {
@@ -409,34 +430,55 @@ export function onChangeMediaFocus(focusX, focusY) {
     focusX,
     focusY,
   };
-};
+}
 
 export function changeUploadCompose(id, params) {
   return (dispatch, getState) => {
     dispatch(changeUploadComposeRequest());
 
-    api(getState).put(`/api/v1/media/${id}`, params).then(response => {
-      dispatch(changeUploadComposeSuccess(response.data));
-    }).catch(error => {
-      dispatch(changeUploadComposeFail(id, error));
-    });
+    let media = getState().getIn(['compose', 'media_attachments']).find((item) => item.get('id') === id);
+
+    // Editing already-attached media is deferred to editing the post itself.
+    // For simplicity's sake, fake an API reply.
+    if (media && !media.get('unattached')) {
+      let { description, focus } = params;
+      const data = media.toJS();
+
+      if (description) {
+        data.description = description;
+      }
+
+      if (focus) {
+        focus = focus.split(',');
+        data.meta = { focus: { x: parseFloat(focus[0]), y: parseFloat(focus[1]) } };
+      }
+
+      dispatch(changeUploadComposeSuccess(data, true));
+    } else {
+      api(getState).put(`/api/v1/media/${id}`, params).then(response => {
+        dispatch(changeUploadComposeSuccess(response.data, false));
+      }).catch(error => {
+        dispatch(changeUploadComposeFail(id, error));
+      });
+    }
   };
-};
+}
 
 export function changeUploadComposeRequest() {
   return {
     type: COMPOSE_UPLOAD_CHANGE_REQUEST,
     skipLoading: true,
   };
-};
+}
 
-export function changeUploadComposeSuccess(media) {
+export function changeUploadComposeSuccess(media, attached) {
   return {
     type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
     media: media,
+    attached: attached,
     skipLoading: true,
   };
-};
+}
 
 export function changeUploadComposeFail(error) {
   return {
@@ -444,14 +486,14 @@ export function changeUploadComposeFail(error) {
     error: error,
     skipLoading: true,
   };
-};
+}
 
 export function uploadComposeRequest() {
   return {
     type: COMPOSE_UPLOAD_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function uploadComposeProgress(loaded, total) {
   return {
@@ -459,7 +501,7 @@ export function uploadComposeProgress(loaded, total) {
     loaded: loaded,
     total: total,
   };
-};
+}
 
 export function uploadComposeSuccess(media, file) {
   return {
@@ -468,7 +510,7 @@ export function uploadComposeSuccess(media, file) {
     file: file,
     skipLoading: true,
   };
-};
+}
 
 export function uploadComposeFail(error) {
   return {
@@ -476,14 +518,14 @@ export function uploadComposeFail(error) {
     error: error,
     skipLoading: true,
   };
-};
+}
 
 export function undoUploadCompose(media_id) {
   return {
     type: COMPOSE_UPLOAD_UNDO,
     media_id: media_id,
   };
-};
+}
 
 export function clearComposeSuggestions() {
   if (fetchComposeSuggestionsAccountsController) {
@@ -492,7 +534,7 @@ export function clearComposeSuggestions() {
   return {
     type: COMPOSE_SUGGESTIONS_CLEAR,
   };
-};
+}
 
 const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
   if (fetchComposeSuggestionsAccountsController) {
@@ -569,7 +611,7 @@ export function fetchComposeSuggestions(token) {
       break;
     }
   };
-};
+}
 
 export function readyComposeSuggestionsEmojis(token, emojis) {
   return {
@@ -577,7 +619,7 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
     token,
     emojis,
   };
-};
+}
 
 export function readyComposeSuggestionsAccounts(token, accounts) {
   return {
@@ -585,7 +627,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
     token,
     accounts,
   };
-};
+}
 
 export const readyComposeSuggestionsTags = (token, tags) => ({
   type: COMPOSE_SUGGESTIONS_READY,
@@ -625,7 +667,7 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
       });
     }
   };
-};
+}
 
 export function updateSuggestionTags(token) {
   return {
@@ -673,13 +715,13 @@ export function mountCompose() {
   return {
     type: COMPOSE_MOUNT,
   };
-};
+}
 
 export function unmountCompose() {
   return {
     type: COMPOSE_UNMOUNT,
   };
-};
+}
 
 export function changeComposeAdvancedOption(option, value) {
   return {
@@ -693,7 +735,7 @@ export function changeComposeSensitivity() {
   return {
     type: COMPOSE_SENSITIVITY_CHANGE,
   };
-};
+}
 
 export const changeComposeLanguage = language => ({
   type: COMPOSE_LANGUAGE_CHANGE,
@@ -704,28 +746,28 @@ export function changeComposeSpoilerness() {
   return {
     type: COMPOSE_SPOILERNESS_CHANGE,
   };
-};
+}
 
 export function changeComposeSpoilerText(text) {
   return {
     type: COMPOSE_SPOILER_TEXT_CHANGE,
     text,
   };
-};
+}
 
 export function changeComposeVisibility(value) {
   return {
     type: COMPOSE_VISIBILITY_CHANGE,
     value,
   };
-};
+}
 
 export function changeComposeContentType(value) {
   return {
     type: COMPOSE_CONTENT_TYPE_CHANGE,
     value,
   };
-};
+}
 
 export function insertEmojiCompose(position, emoji) {
   return {
@@ -733,26 +775,26 @@ export function insertEmojiCompose(position, emoji) {
     position,
     emoji,
   };
-};
+}
 
 export function addPoll() {
   return {
     type: COMPOSE_POLL_ADD,
   };
-};
+}
 
 export function removePoll() {
   return {
     type: COMPOSE_POLL_REMOVE,
   };
-};
+}
 
 export function addPollOption(title) {
   return {
     type: COMPOSE_POLL_OPTION_ADD,
     title,
   };
-};
+}
 
 export function changePollOption(index, title) {
   return {
@@ -760,14 +802,14 @@ export function changePollOption(index, title) {
     index,
     title,
   };
-};
+}
 
 export function removePollOption(index) {
   return {
     type: COMPOSE_POLL_OPTION_REMOVE,
     index,
   };
-};
+}
 
 export function changePollSettings(expiresIn, isMultiple) {
   return {
@@ -775,4 +817,4 @@ export function changePollSettings(expiresIn, isMultiple) {
     expiresIn,
     isMultiple,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/custom_emojis.js b/app/javascript/flavours/glitch/actions/custom_emojis.js
index 7b7d0091b..9ec8156b1 100644
--- a/app/javascript/flavours/glitch/actions/custom_emojis.js
+++ b/app/javascript/flavours/glitch/actions/custom_emojis.js
@@ -14,14 +14,14 @@ export function fetchCustomEmojis() {
       dispatch(fetchCustomEmojisFail(error));
     });
   };
-};
+}
 
 export function fetchCustomEmojisRequest() {
   return {
     type: CUSTOM_EMOJIS_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchCustomEmojisSuccess(custom_emojis) {
   return {
@@ -29,7 +29,7 @@ export function fetchCustomEmojisSuccess(custom_emojis) {
     custom_emojis,
     skipLoading: true,
   };
-};
+}
 
 export function fetchCustomEmojisFail(error) {
   return {
@@ -37,4 +37,4 @@ export function fetchCustomEmojisFail(error) {
     error,
     skipLoading: true,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/domain_blocks.js b/app/javascript/flavours/glitch/actions/domain_blocks.js
index 34a33a654..d06de20a2 100644
--- a/app/javascript/flavours/glitch/actions/domain_blocks.js
+++ b/app/javascript/flavours/glitch/actions/domain_blocks.js
@@ -29,14 +29,14 @@ export function blockDomain(domain) {
       dispatch(blockDomainFail(domain, err));
     });
   };
-};
+}
 
 export function blockDomainRequest(domain) {
   return {
     type: DOMAIN_BLOCK_REQUEST,
     domain,
   };
-};
+}
 
 export function blockDomainSuccess(domain, accounts) {
   return {
@@ -44,7 +44,7 @@ export function blockDomainSuccess(domain, accounts) {
     domain,
     accounts,
   };
-};
+}
 
 export function blockDomainFail(domain, error) {
   return {
@@ -52,7 +52,7 @@ export function blockDomainFail(domain, error) {
     domain,
     error,
   };
-};
+}
 
 export function unblockDomain(domain) {
   return (dispatch, getState) => {
@@ -66,14 +66,14 @@ export function unblockDomain(domain) {
       dispatch(unblockDomainFail(domain, err));
     });
   };
-};
+}
 
 export function unblockDomainRequest(domain) {
   return {
     type: DOMAIN_UNBLOCK_REQUEST,
     domain,
   };
-};
+}
 
 export function unblockDomainSuccess(domain, accounts) {
   return {
@@ -81,7 +81,7 @@ export function unblockDomainSuccess(domain, accounts) {
     domain,
     accounts,
   };
-};
+}
 
 export function unblockDomainFail(domain, error) {
   return {
@@ -89,7 +89,7 @@ export function unblockDomainFail(domain, error) {
     domain,
     error,
   };
-};
+}
 
 export function fetchDomainBlocks() {
   return (dispatch, getState) => {
@@ -102,13 +102,13 @@ export function fetchDomainBlocks() {
       dispatch(fetchDomainBlocksFail(err));
     });
   };
-};
+}
 
 export function fetchDomainBlocksRequest() {
   return {
     type: DOMAIN_BLOCKS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchDomainBlocksSuccess(domains, next) {
   return {
@@ -116,14 +116,14 @@ export function fetchDomainBlocksSuccess(domains, next) {
     domains,
     next,
   };
-};
+}
 
 export function fetchDomainBlocksFail(error) {
   return {
     type: DOMAIN_BLOCKS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandDomainBlocks() {
   return (dispatch, getState) => {
@@ -142,13 +142,13 @@ export function expandDomainBlocks() {
       dispatch(expandDomainBlocksFail(err));
     });
   };
-};
+}
 
 export function expandDomainBlocksRequest() {
   return {
     type: DOMAIN_BLOCKS_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandDomainBlocksSuccess(domains, next) {
   return {
@@ -156,11 +156,11 @@ export function expandDomainBlocksSuccess(domains, next) {
     domains,
     next,
   };
-};
+}
 
 export function expandDomainBlocksFail(error) {
   return {
     type: DOMAIN_BLOCKS_EXPAND_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/emojis.js b/app/javascript/flavours/glitch/actions/emojis.js
index 7cd9d4b7b..3b5d53996 100644
--- a/app/javascript/flavours/glitch/actions/emojis.js
+++ b/app/javascript/flavours/glitch/actions/emojis.js
@@ -11,4 +11,4 @@ export function useEmoji(emoji) {
 
     dispatch(saveSettings());
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/favourites.js b/app/javascript/flavours/glitch/actions/favourites.js
index 9448b1efe..7388e0c58 100644
--- a/app/javascript/flavours/glitch/actions/favourites.js
+++ b/app/javascript/flavours/glitch/actions/favourites.js
@@ -25,14 +25,14 @@ export function fetchFavouritedStatuses() {
       dispatch(fetchFavouritedStatusesFail(error));
     });
   };
-};
+}
 
 export function fetchFavouritedStatusesRequest() {
   return {
     type: FAVOURITED_STATUSES_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchFavouritedStatusesSuccess(statuses, next) {
   return {
@@ -41,7 +41,7 @@ export function fetchFavouritedStatusesSuccess(statuses, next) {
     next,
     skipLoading: true,
   };
-};
+}
 
 export function fetchFavouritedStatusesFail(error) {
   return {
@@ -49,7 +49,7 @@ export function fetchFavouritedStatusesFail(error) {
     error,
     skipLoading: true,
   };
-};
+}
 
 export function expandFavouritedStatuses() {
   return (dispatch, getState) => {
@@ -69,13 +69,13 @@ export function expandFavouritedStatuses() {
       dispatch(expandFavouritedStatusesFail(error));
     });
   };
-};
+}
 
 export function expandFavouritedStatusesRequest() {
   return {
     type: FAVOURITED_STATUSES_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandFavouritedStatusesSuccess(statuses, next) {
   return {
@@ -83,11 +83,11 @@ export function expandFavouritedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function expandFavouritedStatusesFail(error) {
   return {
     type: FAVOURITED_STATUSES_EXPAND_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/height_cache.js b/app/javascript/flavours/glitch/actions/height_cache.js
index 4c752993f..a8645410c 100644
--- a/app/javascript/flavours/glitch/actions/height_cache.js
+++ b/app/javascript/flavours/glitch/actions/height_cache.js
@@ -8,10 +8,10 @@ export function setHeight (key, id, height) {
     id,
     height,
   };
-};
+}
 
 export function clearHeight () {
   return {
     type: HEIGHT_CACHE_CLEAR,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js
index 225ee7eb2..c7b552a65 100644
--- a/app/javascript/flavours/glitch/actions/interactions.js
+++ b/app/javascript/flavours/glitch/actions/interactions.js
@@ -54,7 +54,7 @@ export function reblog(status, visibility) {
       dispatch(reblogFail(status, error));
     });
   };
-};
+}
 
 export function unreblog(status) {
   return (dispatch, getState) => {
@@ -67,21 +67,21 @@ export function unreblog(status) {
       dispatch(unreblogFail(status, error));
     });
   };
-};
+}
 
 export function reblogRequest(status) {
   return {
     type: REBLOG_REQUEST,
     status: status,
   };
-};
+}
 
 export function reblogSuccess(status) {
   return {
     type: REBLOG_SUCCESS,
     status: status,
   };
-};
+}
 
 export function reblogFail(status, error) {
   return {
@@ -89,21 +89,21 @@ export function reblogFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function unreblogRequest(status) {
   return {
     type: UNREBLOG_REQUEST,
     status: status,
   };
-};
+}
 
 export function unreblogSuccess(status) {
   return {
     type: UNREBLOG_SUCCESS,
     status: status,
   };
-};
+}
 
 export function unreblogFail(status, error) {
   return {
@@ -111,7 +111,7 @@ export function unreblogFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function favourite(status) {
   return function (dispatch, getState) {
@@ -124,7 +124,7 @@ export function favourite(status) {
       dispatch(favouriteFail(status, error));
     });
   };
-};
+}
 
 export function unfavourite(status) {
   return (dispatch, getState) => {
@@ -137,21 +137,21 @@ export function unfavourite(status) {
       dispatch(unfavouriteFail(status, error));
     });
   };
-};
+}
 
 export function favouriteRequest(status) {
   return {
     type: FAVOURITE_REQUEST,
     status: status,
   };
-};
+}
 
 export function favouriteSuccess(status) {
   return {
     type: FAVOURITE_SUCCESS,
     status: status,
   };
-};
+}
 
 export function favouriteFail(status, error) {
   return {
@@ -159,21 +159,21 @@ export function favouriteFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function unfavouriteRequest(status) {
   return {
     type: UNFAVOURITE_REQUEST,
     status: status,
   };
-};
+}
 
 export function unfavouriteSuccess(status) {
   return {
     type: UNFAVOURITE_SUCCESS,
     status: status,
   };
-};
+}
 
 export function unfavouriteFail(status, error) {
   return {
@@ -181,7 +181,7 @@ export function unfavouriteFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function bookmark(status) {
   return function (dispatch, getState) {
@@ -194,7 +194,7 @@ export function bookmark(status) {
       dispatch(bookmarkFail(status, error));
     });
   };
-};
+}
 
 export function unbookmark(status) {
   return (dispatch, getState) => {
@@ -207,21 +207,21 @@ export function unbookmark(status) {
       dispatch(unbookmarkFail(status, error));
     });
   };
-};
+}
 
 export function bookmarkRequest(status) {
   return {
     type: BOOKMARK_REQUEST,
     status: status,
   };
-};
+}
 
 export function bookmarkSuccess(status) {
   return {
     type: BOOKMARK_SUCCESS,
     status: status,
   };
-};
+}
 
 export function bookmarkFail(status, error) {
   return {
@@ -229,21 +229,21 @@ export function bookmarkFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function unbookmarkRequest(status) {
   return {
     type: UNBOOKMARK_REQUEST,
     status: status,
   };
-};
+}
 
 export function unbookmarkSuccess(status) {
   return {
     type: UNBOOKMARK_SUCCESS,
     status: status,
   };
-};
+}
 
 export function unbookmarkFail(status, error) {
   return {
@@ -251,7 +251,7 @@ export function unbookmarkFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function fetchReblogs(id) {
   return (dispatch, getState) => {
@@ -264,14 +264,14 @@ export function fetchReblogs(id) {
       dispatch(fetchReblogsFail(id, error));
     });
   };
-};
+}
 
 export function fetchReblogsRequest(id) {
   return {
     type: REBLOGS_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchReblogsSuccess(id, accounts) {
   return {
@@ -279,14 +279,14 @@ export function fetchReblogsSuccess(id, accounts) {
     id,
     accounts,
   };
-};
+}
 
 export function fetchReblogsFail(id, error) {
   return {
     type: REBLOGS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function fetchFavourites(id) {
   return (dispatch, getState) => {
@@ -299,14 +299,14 @@ export function fetchFavourites(id) {
       dispatch(fetchFavouritesFail(id, error));
     });
   };
-};
+}
 
 export function fetchFavouritesRequest(id) {
   return {
     type: FAVOURITES_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchFavouritesSuccess(id, accounts) {
   return {
@@ -314,14 +314,14 @@ export function fetchFavouritesSuccess(id, accounts) {
     id,
     accounts,
   };
-};
+}
 
 export function fetchFavouritesFail(id, error) {
   return {
     type: FAVOURITES_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function pin(status) {
   return (dispatch, getState) => {
@@ -334,21 +334,21 @@ export function pin(status) {
       dispatch(pinFail(status, error));
     });
   };
-};
+}
 
 export function pinRequest(status) {
   return {
     type: PIN_REQUEST,
     status,
   };
-};
+}
 
 export function pinSuccess(status) {
   return {
     type: PIN_SUCCESS,
     status,
   };
-};
+}
 
 export function pinFail(status, error) {
   return {
@@ -356,7 +356,7 @@ export function pinFail(status, error) {
     status,
     error,
   };
-};
+}
 
 export function unpin (status) {
   return (dispatch, getState) => {
@@ -369,21 +369,21 @@ export function unpin (status) {
       dispatch(unpinFail(status, error));
     });
   };
-};
+}
 
 export function unpinRequest(status) {
   return {
     type: UNPIN_REQUEST,
     status,
   };
-};
+}
 
 export function unpinSuccess(status) {
   return {
     type: UNPIN_SUCCESS,
     status,
   };
-};
+}
 
 export function unpinFail(status, error) {
   return {
@@ -391,4 +391,4 @@ export function unpinFail(status, error) {
     status,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/local_settings.js b/app/javascript/flavours/glitch/actions/local_settings.js
index a4a928611..adf7fd2ab 100644
--- a/app/javascript/flavours/glitch/actions/local_settings.js
+++ b/app/javascript/flavours/glitch/actions/local_settings.js
@@ -33,14 +33,14 @@ export function checkDeprecatedLocalSettings() {
       }));
     }
   };
-};
+}
 
 export function clearDeprecatedLocalSettings() {
   return (dispatch) => {
     dispatch(deleteLocalSetting(['content_warnings', 'auto_unfold']));
     dispatch(deleteLocalSetting(['swipe_to_change_columns']));
   };
-};
+}
 
 export function changeLocalSetting(key, value) {
   return dispatch => {
@@ -52,7 +52,7 @@ export function changeLocalSetting(key, value) {
 
     dispatch(saveLocalSettings());
   };
-};
+}
 
 export function deleteLocalSetting(key) {
   return dispatch => {
@@ -63,7 +63,7 @@ export function deleteLocalSetting(key) {
 
     dispatch(saveLocalSettings());
   };
-};
+}
 
 //  __TODO :__
 //  Right now `saveLocalSettings()` doesn't keep track of which user
@@ -74,4 +74,4 @@ export function saveLocalSettings() {
     const localSettings = getState().get('local_settings').toJS();
     localStorage.setItem('mastodon-settings', JSON.stringify(localSettings));
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/markers.js b/app/javascript/flavours/glitch/actions/markers.js
index 3b6a76bc4..f82675342 100644
--- a/app/javascript/flavours/glitch/actions/markers.js
+++ b/app/javascript/flavours/glitch/actions/markers.js
@@ -55,7 +55,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
     client.open('POST', '/api/v1/markers', false);
     client.setRequestHeader('Content-Type', 'application/json');
     client.setRequestHeader('Authorization', `Bearer ${accessToken}`);
-    client.SUBMIT(JSON.stringify(params));
+    client.send(JSON.stringify(params));
   } catch (e) {
     // Do not make the BeforeUnload handler error out
   }
@@ -101,7 +101,7 @@ export function submitMarkersSuccess({ home, notifications }) {
     home: (home || {}).last_read_id,
     notifications: (notifications || {}).last_read_id,
   };
-};
+}
 
 export function submitMarkers(params = {}) {
   const result = (dispatch, getState) => debouncedSubmitMarkers(dispatch, getState);
@@ -111,7 +111,7 @@ export function submitMarkers(params = {}) {
   }
 
   return result;
-};
+}
 
 export const fetchMarkers = () => (dispatch, getState) => {
   const params = { timeline: ['notifications'] };
@@ -130,7 +130,7 @@ export function fetchMarkersRequest() {
     type: MARKERS_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchMarkersSuccess(markers) {
   return {
@@ -138,7 +138,7 @@ export function fetchMarkersSuccess(markers) {
     markers,
     skipLoading: true,
   };
-};
+}
 
 export function fetchMarkersFail(error) {
   return {
@@ -147,4 +147,4 @@ export function fetchMarkersFail(error) {
     skipLoading: true,
     skipAlert: true,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/modal.js b/app/javascript/flavours/glitch/actions/modal.js
index 3e576fab8..ef2ae0e4c 100644
--- a/app/javascript/flavours/glitch/actions/modal.js
+++ b/app/javascript/flavours/glitch/actions/modal.js
@@ -7,7 +7,7 @@ export function openModal(type, props) {
     modalType: type,
     modalProps: props,
   };
-};
+}
 
 export function closeModal(type, options = { ignoreFocus: false }) {
   return {
@@ -15,4 +15,4 @@ export function closeModal(type, options = { ignoreFocus: false }) {
     modalType: type,
     ignoreFocus: options.ignoreFocus,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/mutes.js b/app/javascript/flavours/glitch/actions/mutes.js
index 1ccf9592f..aa47d1464 100644
--- a/app/javascript/flavours/glitch/actions/mutes.js
+++ b/app/javascript/flavours/glitch/actions/mutes.js
@@ -26,13 +26,13 @@ export function fetchMutes() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(fetchMutesFail(error)));
   };
-};
+}
 
 export function fetchMutesRequest() {
   return {
     type: MUTES_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchMutesSuccess(accounts, next) {
   return {
@@ -40,14 +40,14 @@ export function fetchMutesSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchMutesFail(error) {
   return {
     type: MUTES_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandMutes() {
   return (dispatch, getState) => {
@@ -66,13 +66,13 @@ export function expandMutes() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(expandMutesFail(error)));
   };
-};
+}
 
 export function expandMutesRequest() {
   return {
     type: MUTES_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandMutesSuccess(accounts, next) {
   return {
@@ -80,14 +80,14 @@ export function expandMutesSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandMutesFail(error) {
   return {
     type: MUTES_EXPAND_FAIL,
     error,
   };
-};
+}
 
 export function initMuteModal(account) {
   return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index 158a5b7e4..989bc4144 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -129,7 +129,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
       });
     }
   };
-};
+}
 
 const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
 
@@ -209,14 +209,14 @@ export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) {
       done();
     });
   };
-};
+}
 
 export function expandNotificationsRequest(isLoadingMore) {
   return {
     type: NOTIFICATIONS_EXPAND_REQUEST,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandNotificationsSuccess(notifications, next, isLoadingMore, isLoadingRecent, usePendingItems) {
   return {
@@ -227,7 +227,7 @@ export function expandNotificationsSuccess(notifications, next, isLoadingMore, i
     usePendingItems,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandNotificationsFail(error, isLoadingMore) {
   return {
@@ -236,7 +236,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
     skipLoading: !isLoadingMore,
     skipAlert: !isLoadingMore || error.name === 'AbortError',
   };
-};
+}
 
 export function clearNotifications() {
   return (dispatch, getState) => {
@@ -246,14 +246,14 @@ export function clearNotifications() {
 
     api(getState).post('/api/v1/notifications/clear');
   };
-};
+}
 
 export function scrollTopNotifications(top) {
   return {
     type: NOTIFICATIONS_SCROLL_TOP,
     top,
   };
-};
+}
 
 export function deleteMarkedNotifications() {
   return (dispatch, getState) => {
@@ -277,33 +277,33 @@ export function deleteMarkedNotifications() {
       dispatch(deleteMarkedNotificationsFail(error));
     });
   };
-};
+}
 
 export function enterNotificationClearingMode(yes) {
   return {
     type: NOTIFICATIONS_ENTER_CLEARING_MODE,
     yes: yes,
   };
-};
+}
 
 export function markAllNotifications(yes) {
   return {
     type: NOTIFICATIONS_MARK_ALL_FOR_DELETE,
     yes: yes, // true, false or null. null = invert
   };
-};
+}
 
 export function deleteMarkedNotificationsRequest() {
   return {
     type: NOTIFICATIONS_DELETE_MARKED_REQUEST,
   };
-};
+}
 
 export function deleteMarkedNotificationsFail() {
   return {
     type: NOTIFICATIONS_DELETE_MARKED_FAIL,
   };
-};
+}
 
 export function markNotificationForDelete(id, yes) {
   return {
@@ -311,32 +311,32 @@ export function markNotificationForDelete(id, yes) {
     id: id,
     yes: yes,
   };
-};
+}
 
 export function deleteMarkedNotificationsSuccess() {
   return {
     type: NOTIFICATIONS_DELETE_MARKED_SUCCESS,
   };
-};
+}
 
 export function mountNotifications() {
   return {
     type: NOTIFICATIONS_MOUNT,
   };
-};
+}
 
 export function unmountNotifications() {
   return {
     type: NOTIFICATIONS_UNMOUNT,
   };
-};
+}
 
 export function notificationsSetVisibility(visibility) {
   return {
     type: NOTIFICATIONS_SET_VISIBILITY,
     visibility: visibility,
   };
-};
+}
 
 export function setFilter (filterType) {
   return dispatch => {
@@ -348,13 +348,13 @@ export function setFilter (filterType) {
     dispatch(expandNotifications({ forceLoad: true }));
     dispatch(saveSettings());
   };
-};
+}
 
 export function markNotificationsAsRead() {
   return {
     type: NOTIFICATIONS_MARK_AS_READ,
   };
-};
+}
 
 // Browser support
 export function setupBrowserNotifications() {
@@ -379,7 +379,7 @@ export function requestBrowserPermission(callback = noOp) {
       callback(permission);
     });
   };
-};
+}
 
 export function setBrowserSupport (value) {
   return {
diff --git a/app/javascript/flavours/glitch/actions/onboarding.js b/app/javascript/flavours/glitch/actions/onboarding.js
index a161c50ef..5038b7eb6 100644
--- a/app/javascript/flavours/glitch/actions/onboarding.js
+++ b/app/javascript/flavours/glitch/actions/onboarding.js
@@ -11,4 +11,4 @@ export function showOnboardingOnce() {
       dispatch(saveSettings());
     }
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/pin_statuses.js b/app/javascript/flavours/glitch/actions/pin_statuses.js
index 0926978ac..d8c0a1373 100644
--- a/app/javascript/flavours/glitch/actions/pin_statuses.js
+++ b/app/javascript/flavours/glitch/actions/pin_statuses.js
@@ -18,13 +18,13 @@ export function fetchPinnedStatuses() {
       dispatch(fetchPinnedStatusesFail(error));
     });
   };
-};
+}
 
 export function fetchPinnedStatusesRequest() {
   return {
     type: PINNED_STATUSES_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchPinnedStatusesSuccess(statuses, next) {
   return {
@@ -32,11 +32,11 @@ export function fetchPinnedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function fetchPinnedStatusesFail(error) {
   return {
     type: PINNED_STATUSES_FETCH_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
index 762fe260c..bc5634233 100644
--- a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
+++ b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
@@ -6,7 +6,7 @@ import { setBrowserSupport, setSubscription, clearSubscription } from './setter'
 const urlBase64ToUint8Array = (base64String) => {
   const padding = '='.repeat((4 - base64String.length % 4) % 4);
   const base64 = (base64String + padding)
-    .replace(/\-/g, '+')
+    .replace(/-/g, '+')
     .replace(/_/g, '/');
 
   const rawData = window.atob(base64);
diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js
index f21c0058b..0012808e5 100644
--- a/app/javascript/flavours/glitch/actions/search.js
+++ b/app/javascript/flavours/glitch/actions/search.js
@@ -19,13 +19,13 @@ export function changeSearch(value) {
     type: SEARCH_CHANGE,
     value,
   };
-};
+}
 
 export function clearSearch() {
   return {
     type: SEARCH_CLEAR,
   };
-};
+}
 
 export function submitSearch() {
   return (dispatch, getState) => {
@@ -60,13 +60,13 @@ export function submitSearch() {
       dispatch(fetchSearchFail(error));
     });
   };
-};
+}
 
 export function fetchSearchRequest() {
   return {
     type: SEARCH_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchSearchSuccess(results, searchTerm) {
   return {
@@ -74,14 +74,14 @@ export function fetchSearchSuccess(results, searchTerm) {
     results,
     searchTerm,
   };
-};
+}
 
 export function fetchSearchFail(error) {
   return {
     type: SEARCH_FETCH_FAIL,
     error,
   };
-};
+}
 
 export const expandSearch = type => (dispatch, getState) => {
   const value  = getState().getIn(['search', 'value']);
diff --git a/app/javascript/flavours/glitch/actions/server.js b/app/javascript/flavours/glitch/actions/server.js
index 31d4aea10..091af0f0f 100644
--- a/app/javascript/flavours/glitch/actions/server.js
+++ b/app/javascript/flavours/glitch/actions/server.js
@@ -5,6 +5,10 @@ export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST';
 export const SERVER_FETCH_SUCCESS = 'Server_FETCH_SUCCESS';
 export const SERVER_FETCH_FAIL    = 'Server_FETCH_FAIL';
 
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST = 'SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST';
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS = 'SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS';
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL    = 'SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL';
+
 export const EXTENDED_DESCRIPTION_REQUEST = 'EXTENDED_DESCRIPTION_REQUEST';
 export const EXTENDED_DESCRIPTION_SUCCESS = 'EXTENDED_DESCRIPTION_SUCCESS';
 export const EXTENDED_DESCRIPTION_FAIL    = 'EXTENDED_DESCRIPTION_FAIL';
@@ -37,6 +41,29 @@ const fetchServerFail = error => ({
   error,
 });
 
+export const fetchServerTranslationLanguages = () => (dispatch, getState) => {
+  dispatch(fetchServerTranslationLanguagesRequest());
+
+  api(getState)
+    .get('/api/v1/instance/translation_languages').then(({ data }) => {
+      dispatch(fetchServerTranslationLanguagesSuccess(data));
+    }).catch(err => dispatch(fetchServerTranslationLanguagesFail(err)));
+};
+
+const fetchServerTranslationLanguagesRequest = () => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST,
+});
+
+const fetchServerTranslationLanguagesSuccess = translationLanguages => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS,
+  translationLanguages,
+});
+
+const fetchServerTranslationLanguagesFail = error => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL,
+  error,
+});
+
 export const fetchExtendedDescription = () => (dispatch, getState) => {
   dispatch(fetchExtendedDescriptionRequest());
 
diff --git a/app/javascript/flavours/glitch/actions/settings.js b/app/javascript/flavours/glitch/actions/settings.js
index 5634a11ef..60f0abf95 100644
--- a/app/javascript/flavours/glitch/actions/settings.js
+++ b/app/javascript/flavours/glitch/actions/settings.js
@@ -15,7 +15,7 @@ export function changeSetting(path, value) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 const debouncedSave = debounce((dispatch, getState) => {
   if (getState().getIn(['settings', 'saved'])) {
@@ -31,4 +31,4 @@ const debouncedSave = debounce((dispatch, getState) => {
 
 export function saveSettings() {
   return (dispatch, getState) => debouncedSave(dispatch, getState);
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js
index efb4cc33b..487cd6988 100644
--- a/app/javascript/flavours/glitch/actions/statuses.js
+++ b/app/javascript/flavours/glitch/actions/statuses.js
@@ -45,7 +45,7 @@ export function fetchStatusRequest(id, skipLoading) {
     id,
     skipLoading,
   };
-};
+}
 
 export function fetchStatus(id, forceFetch = false) {
   return (dispatch, getState) => {
@@ -66,14 +66,14 @@ export function fetchStatus(id, forceFetch = false) {
       dispatch(fetchStatusFail(id, error, skipLoading));
     });
   };
-};
+}
 
 export function fetchStatusSuccess(skipLoading) {
   return {
     type: STATUS_FETCH_SUCCESS,
     skipLoading,
   };
-};
+}
 
 export function fetchStatusFail(id, error, skipLoading) {
   return {
@@ -83,7 +83,7 @@ export function fetchStatusFail(id, error, skipLoading) {
     skipLoading,
     skipAlert: true,
   };
-};
+}
 
 export function redraft(status, raw_text, content_type) {
   return {
@@ -92,7 +92,7 @@ export function redraft(status, raw_text, content_type) {
     raw_text,
     content_type,
   };
-};
+}
 
 export const editStatus = (id, routerHistory) => (dispatch, getState) => {
   let status = getState().getIn(['statuses', id]);
@@ -148,21 +148,21 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
       dispatch(deleteStatusFail(id, error));
     });
   };
-};
+}
 
 export function deleteStatusRequest(id) {
   return {
     type: STATUS_DELETE_REQUEST,
     id: id,
   };
-};
+}
 
 export function deleteStatusSuccess(id) {
   return {
     type: STATUS_DELETE_SUCCESS,
     id: id,
   };
-};
+}
 
 export function deleteStatusFail(id, error) {
   return {
@@ -170,7 +170,7 @@ export function deleteStatusFail(id, error) {
     id: id,
     error: error,
   };
-};
+}
 
 export const updateStatus = status => dispatch =>
   dispatch(importFetchedStatus(status));
@@ -191,14 +191,14 @@ export function fetchContext(id) {
       dispatch(fetchContextFail(id, error));
     });
   };
-};
+}
 
 export function fetchContextRequest(id) {
   return {
     type: CONTEXT_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchContextSuccess(id, ancestors, descendants) {
   return {
@@ -208,7 +208,7 @@ export function fetchContextSuccess(id, ancestors, descendants) {
     descendants,
     statuses: ancestors.concat(descendants),
   };
-};
+}
 
 export function fetchContextFail(id, error) {
   return {
@@ -217,7 +217,7 @@ export function fetchContextFail(id, error) {
     error,
     skipAlert: true,
   };
-};
+}
 
 export function muteStatus(id) {
   return (dispatch, getState) => {
@@ -229,21 +229,21 @@ export function muteStatus(id) {
       dispatch(muteStatusFail(id, error));
     });
   };
-};
+}
 
 export function muteStatusRequest(id) {
   return {
     type: STATUS_MUTE_REQUEST,
     id,
   };
-};
+}
 
 export function muteStatusSuccess(id) {
   return {
     type: STATUS_MUTE_SUCCESS,
     id,
   };
-};
+}
 
 export function muteStatusFail(id, error) {
   return {
@@ -251,7 +251,7 @@ export function muteStatusFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function unmuteStatus(id) {
   return (dispatch, getState) => {
@@ -263,21 +263,21 @@ export function unmuteStatus(id) {
       dispatch(unmuteStatusFail(id, error));
     });
   };
-};
+}
 
 export function unmuteStatusRequest(id) {
   return {
     type: STATUS_UNMUTE_REQUEST,
     id,
   };
-};
+}
 
 export function unmuteStatusSuccess(id) {
   return {
     type: STATUS_UNMUTE_SUCCESS,
     id,
   };
-};
+}
 
 export function unmuteStatusFail(id, error) {
   return {
@@ -285,7 +285,7 @@ export function unmuteStatusFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function hideStatus(ids) {
   if (!Array.isArray(ids)) {
@@ -296,7 +296,7 @@ export function hideStatus(ids) {
     type: STATUS_HIDE,
     ids,
   };
-};
+}
 
 export function revealStatus(ids) {
   if (!Array.isArray(ids)) {
@@ -307,7 +307,7 @@ export function revealStatus(ids) {
     type: STATUS_REVEAL,
     ids,
   };
-};
+}
 
 export function toggleStatusCollapse(id, isCollapsed) {
   return {
@@ -315,7 +315,7 @@ export function toggleStatusCollapse(id, isCollapsed) {
     id,
     isCollapsed,
   };
-};
+}
 
 export const translateStatus = id => (dispatch, getState) => {
   dispatch(translateStatusRequest(id));
diff --git a/app/javascript/flavours/glitch/actions/store.js b/app/javascript/flavours/glitch/actions/store.js
index 9dbc0b214..137b68e22 100644
--- a/app/javascript/flavours/glitch/actions/store.js
+++ b/app/javascript/flavours/glitch/actions/store.js
@@ -18,7 +18,7 @@ const applyMigrations = (state) => {
       if (state.getIn(['settings', 'notifications', 'showUnread']) !== false) {
         state.setIn(['settings', 'notifications', 'showUnread'], state.getIn(['local_settings', 'notifications', 'show_unread']));
       }
-      state.removeIn(['local_settings', 'notifications', 'show_unread'])
+      state.removeIn(['local_settings', 'notifications', 'show_unread']);
     }
   });
 };
@@ -36,4 +36,4 @@ export function hydrateStore(rawState) {
     dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
     dispatch(saveSettings());
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/suggestions.js b/app/javascript/flavours/glitch/actions/suggestions.js
index 1f1116e75..9e8cd1ea4 100644
--- a/app/javascript/flavours/glitch/actions/suggestions.js
+++ b/app/javascript/flavours/glitch/actions/suggestions.js
@@ -21,14 +21,14 @@ export function fetchSuggestions(withRelationships = false) {
       }
     }).catch(error => dispatch(fetchSuggestionsFail(error)));
   };
-};
+}
 
 export function fetchSuggestionsRequest() {
   return {
     type: SUGGESTIONS_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchSuggestionsSuccess(suggestions) {
   return {
@@ -36,7 +36,7 @@ export function fetchSuggestionsSuccess(suggestions) {
     suggestions,
     skipLoading: true,
   };
-};
+}
 
 export function fetchSuggestionsFail(error) {
   return {
@@ -45,7 +45,7 @@ export function fetchSuggestionsFail(error) {
     skipLoading: true,
     skipAlert: true,
   };
-};
+}
 
 export const dismissSuggestion = accountId => (dispatch, getState) => {
   dispatch({
diff --git a/app/javascript/flavours/glitch/actions/tags.js b/app/javascript/flavours/glitch/actions/tags.js
index 37e79d4cb..dda8c924b 100644
--- a/app/javascript/flavours/glitch/actions/tags.js
+++ b/app/javascript/flavours/glitch/actions/tags.js
@@ -1,9 +1,17 @@
-import api from '../api';
+import api, { getLinks } from '../api';
 
 export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
 export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
 export const HASHTAG_FETCH_FAIL    = 'HASHTAG_FETCH_FAIL';
 
+export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST';
+export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS';
+export const FOLLOWED_HASHTAGS_FETCH_FAIL    = 'FOLLOWED_HASHTAGS_FETCH_FAIL';
+
+export const FOLLOWED_HASHTAGS_EXPAND_REQUEST = 'FOLLOWED_HASHTAGS_EXPAND_REQUEST';
+export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS';
+export const FOLLOWED_HASHTAGS_EXPAND_FAIL    = 'FOLLOWED_HASHTAGS_EXPAND_FAIL';
+
 export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
 export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
 export const HASHTAG_FOLLOW_FAIL    = 'HASHTAG_FOLLOW_FAIL';
@@ -37,6 +45,78 @@ export const fetchHashtagFail = error => ({
   error,
 });
 
+export const fetchFollowedHashtags = () => (dispatch, getState) => {
+  dispatch(fetchFollowedHashtagsRequest());
+
+  api(getState).get('/api/v1/followed_tags').then(response => {
+    const next = getLinks(response).refs.find(link => link.rel === 'next');
+    dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null));
+  }).catch(err => {
+    dispatch(fetchFollowedHashtagsFail(err));
+  });
+};
+
+export function fetchFollowedHashtagsRequest() {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_REQUEST,
+  };
+}
+
+export function fetchFollowedHashtagsSuccess(followed_tags, next) {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_SUCCESS,
+    followed_tags,
+    next,
+  };
+}
+
+export function fetchFollowedHashtagsFail(error) {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_FAIL,
+    error,
+  };
+}
+
+export function expandFollowedHashtags() {
+  return (dispatch, getState) => {
+    const url = getState().getIn(['followed_tags', 'next']);
+
+    if (url === null) {
+      return;
+    }
+
+    dispatch(expandFollowedHashtagsRequest());
+
+    api(getState).get(url).then(response => {
+      const next = getLinks(response).refs.find(link => link.rel === 'next');
+      dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null));
+    }).catch(error => {
+      dispatch(expandFollowedHashtagsFail(error));
+    });
+  };
+}
+
+export function expandFollowedHashtagsRequest() {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_REQUEST,
+  };
+}
+
+export function expandFollowedHashtagsSuccess(followed_tags, next) {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_SUCCESS,
+    followed_tags,
+    next,
+  };
+}
+
+export function expandFollowedHashtagsFail(error) {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_FAIL,
+    error,
+  };
+}
+
 export const followHashtag = name => (dispatch, getState) => {
   dispatch(followHashtagRequest(name));
 
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index a1c4dd43a..eb817daf9 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -55,14 +55,14 @@ export function updateTimeline(timeline, status, accept) {
       timeline,
       status,
       usePendingItems: preferPendingItems,
-      filtered
+      filtered,
     });
 
     if (timeline === 'home') {
       dispatch(submitMarkers());
     }
   };
-};
+}
 
 export function deleteFromTimelines(id) {
   return (dispatch, getState) => {
@@ -78,13 +78,13 @@ export function deleteFromTimelines(id) {
       reblogOf,
     });
   };
-};
+}
 
 export function clearTimeline(timeline) {
   return (dispatch) => {
     dispatch({ type: TIMELINE_CLEAR, timeline });
   };
-};
+}
 
 const noOp = () => {};
 
@@ -134,7 +134,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
       done();
     });
   };
-};
+}
 
 export function fillTimelineGaps(timelineId, path, params = {}, done = noOp) {
   return (dispatch, getState) => {
@@ -181,7 +181,7 @@ export function expandTimelineRequest(timeline, isLoadingMore) {
     timeline,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) {
   return {
@@ -194,7 +194,7 @@ export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadi
     usePendingItems,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandTimelineFail(timeline, error, isLoadingMore) {
   return {
@@ -204,7 +204,7 @@ export function expandTimelineFail(timeline, error, isLoadingMore) {
     skipLoading: !isLoadingMore,
     skipNotFound: timeline.startsWith('account:'),
   };
-};
+}
 
 export function scrollTopTimeline(timeline, top) {
   return {
@@ -212,7 +212,7 @@ export function scrollTopTimeline(timeline, top) {
     timeline,
     top,
   };
-};
+}
 
 export function connectTimeline(timeline) {
   return {
@@ -220,7 +220,7 @@ export function connectTimeline(timeline) {
     timeline,
     usePendingItems: preferPendingItems,
   };
-};
+}
 
 export const disconnectTimeline = timeline => ({
   type: TIMELINE_DISCONNECT,