Merge tag 'v4.1.8' into chinwag-4.1
This commit is contained in:
commit
d9dfd09ac8
995 changed files with 53450 additions and 19456 deletions
|
|
@ -14,24 +14,24 @@ export function submitAccountNote(id, value) {
|
|||
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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export function fetchAccount(id) {
|
|||
dispatch(fetchAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const lookupAccount = acct => (dispatch, getState) => {
|
||||
dispatch(lookupAccountRequest(acct));
|
||||
|
|
@ -126,13 +126,13 @@ export function fetchAccountRequest(id) {
|
|||
type: ACCOUNT_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAccountSuccess() {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_SUCCESS,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAccountFail(id, error) {
|
||||
return {
|
||||
|
|
@ -141,7 +141,7 @@ export function fetchAccountFail(id, error) {
|
|||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccount(id, options = { reblogs: true }) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -156,7 +156,7 @@ export function followAccount(id, options = { reblogs: true }) {
|
|||
dispatch(followAccountFail(error, locked));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -168,7 +168,7 @@ export function unfollowAccount(id) {
|
|||
dispatch(unfollowAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccountRequest(id, locked) {
|
||||
return {
|
||||
|
|
@ -177,7 +177,7 @@ export function followAccountRequest(id, locked) {
|
|||
locked,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccountSuccess(relationship, alreadyFollowing) {
|
||||
return {
|
||||
|
|
@ -186,7 +186,7 @@ export function followAccountSuccess(relationship, alreadyFollowing) {
|
|||
alreadyFollowing,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function followAccountFail(error, locked) {
|
||||
return {
|
||||
|
|
@ -195,7 +195,7 @@ export function followAccountFail(error, locked) {
|
|||
locked,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccountRequest(id) {
|
||||
return {
|
||||
|
|
@ -203,7 +203,7 @@ export function unfollowAccountRequest(id) {
|
|||
id,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
|
|
@ -212,7 +212,7 @@ export function unfollowAccountSuccess(relationship, statuses) {
|
|||
statuses,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfollowAccountFail(error) {
|
||||
return {
|
||||
|
|
@ -220,7 +220,7 @@ export function unfollowAccountFail(error) {
|
|||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function blockAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -233,7 +233,7 @@ export function blockAccount(id) {
|
|||
dispatch(blockAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unblockAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -245,14 +245,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 {
|
||||
|
|
@ -260,35 +260,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) {
|
||||
|
|
@ -302,7 +302,7 @@ export function muteAccount(id, notifications, duration=0) {
|
|||
dispatch(muteAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -314,14 +314,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 {
|
||||
|
|
@ -329,35 +329,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) {
|
||||
|
|
@ -374,14 +374,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 {
|
||||
|
|
@ -390,7 +390,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
|
|||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowersFail(id, error) {
|
||||
return {
|
||||
|
|
@ -399,7 +399,7 @@ export function fetchFollowersFail(id, error) {
|
|||
error,
|
||||
skipNotFound: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowers(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -421,14 +421,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 {
|
||||
|
|
@ -437,7 +437,7 @@ export function expandFollowersSuccess(id, accounts, next) {
|
|||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowersFail(id, error) {
|
||||
return {
|
||||
|
|
@ -445,7 +445,7 @@ export function expandFollowersFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowing(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -461,14 +461,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 {
|
||||
|
|
@ -477,7 +477,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
|
|||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowingFail(id, error) {
|
||||
return {
|
||||
|
|
@ -486,7 +486,7 @@ export function fetchFollowingFail(id, error) {
|
|||
error,
|
||||
skipNotFound: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowing(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -508,14 +508,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 {
|
||||
|
|
@ -524,7 +524,7 @@ export function expandFollowingSuccess(id, accounts, next) {
|
|||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandFollowingFail(id, error) {
|
||||
return {
|
||||
|
|
@ -532,7 +532,7 @@ export function expandFollowingFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationships(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -553,7 +553,7 @@ export function fetchRelationships(accountIds) {
|
|||
dispatch(fetchRelationshipsFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationshipsRequest(ids) {
|
||||
return {
|
||||
|
|
@ -561,7 +561,7 @@ export function fetchRelationshipsRequest(ids) {
|
|||
ids,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationshipsSuccess(relationships) {
|
||||
return {
|
||||
|
|
@ -569,7 +569,7 @@ export function fetchRelationshipsSuccess(relationships) {
|
|||
relationships,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRelationshipsFail(error) {
|
||||
return {
|
||||
|
|
@ -578,7 +578,7 @@ export function fetchRelationshipsFail(error) {
|
|||
skipLoading: true,
|
||||
skipNotFound: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchFollowRequests() {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -590,13 +590,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 {
|
||||
|
|
@ -604,14 +604,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) => {
|
||||
|
|
@ -629,13 +629,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 {
|
||||
|
|
@ -643,14 +643,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) => {
|
||||
|
|
@ -661,21 +661,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 {
|
||||
|
|
@ -683,7 +683,7 @@ export function authorizeFollowRequestFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function rejectFollowRequest(id) {
|
||||
|
|
@ -695,21 +695,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 {
|
||||
|
|
@ -717,7 +717,7 @@ export function rejectFollowRequestFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -729,7 +729,7 @@ export function pinAccount(id) {
|
|||
dispatch(pinAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -741,49 +741,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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export const addReaction = (announcementId, name) => (dispatch, getState) => {
|
|||
dispatch(addReactionRequest(announcementId, name, alreadyAdded));
|
||||
}
|
||||
|
||||
api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
|
||||
api(getState).put(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
|
||||
dispatch(addReactionSuccess(announcementId, name, alreadyAdded));
|
||||
}).catch(err => {
|
||||
if (!alreadyAdded) {
|
||||
|
|
@ -136,7 +136,7 @@ export const addReactionFail = (announcementId, name, error) => ({
|
|||
export const removeReaction = (announcementId, name) => (dispatch, getState) => {
|
||||
dispatch(removeReactionRequest(announcementId, name));
|
||||
|
||||
api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${name}`).then(() => {
|
||||
api(getState).delete(`/api/v1/announcements/${announcementId}/reactions/${encodeURIComponent(name)}`).then(() => {
|
||||
dispatch(removeReactionSuccess(announcementId, name));
|
||||
}).catch(err => {
|
||||
dispatch(removeReactionFail(announcementId, name, err));
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -94,14 +94,14 @@ export function setComposeToStatus(status, text, spoiler_text) {
|
|||
text,
|
||||
spoiler_text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeCompose(text) {
|
||||
return {
|
||||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function replyCompose(status, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -112,19 +112,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) => {
|
||||
|
|
@ -135,7 +135,7 @@ export function mentionCompose(account, routerHistory) {
|
|||
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function directCompose(account, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -146,7 +146,7 @@ export function directCompose(account, routerHistory) {
|
|||
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function submitCompose(routerHistory) {
|
||||
return function (dispatch, getState) {
|
||||
|
|
@ -160,6 +160,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',
|
||||
|
|
@ -167,6 +187,7 @@ export function submitCompose(routerHistory) {
|
|||
status,
|
||||
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']),
|
||||
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
||||
visibility: getState().getIn(['compose', 'privacy']),
|
||||
|
|
@ -211,27 +232,27 @@ 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 uploadCompose(files) {
|
||||
return function (dispatch, getState) {
|
||||
|
|
@ -294,9 +315,9 @@ export function uploadCompose(files) {
|
|||
}
|
||||
});
|
||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const uploadComposeProcessing = () => ({
|
||||
type: COMPOSE_UPLOAD_PROCESSING,
|
||||
|
|
@ -354,14 +375,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 {
|
||||
|
|
@ -369,34 +390,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 {
|
||||
|
|
@ -404,14 +446,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 {
|
||||
|
|
@ -419,7 +461,7 @@ export function uploadComposeProgress(loaded, total) {
|
|||
loaded: loaded,
|
||||
total: total,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadComposeSuccess(media, file) {
|
||||
return {
|
||||
|
|
@ -428,7 +470,7 @@ export function uploadComposeSuccess(media, file) {
|
|||
file: file,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function uploadComposeFail(error) {
|
||||
return {
|
||||
|
|
@ -436,14 +478,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) {
|
||||
|
|
@ -452,7 +494,7 @@ export function clearComposeSuggestions() {
|
|||
return {
|
||||
type: COMPOSE_SUGGESTIONS_CLEAR,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
||||
if (fetchComposeSuggestionsAccountsController) {
|
||||
|
|
@ -530,7 +572,7 @@ export function fetchComposeSuggestions(token) {
|
|||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function readyComposeSuggestionsEmojis(token, emojis) {
|
||||
return {
|
||||
|
|
@ -538,7 +580,7 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
|
|||
token,
|
||||
emojis,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function readyComposeSuggestionsAccounts(token, accounts) {
|
||||
return {
|
||||
|
|
@ -546,7 +588,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
|
|||
token,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const readyComposeSuggestionsTags = (token, tags) => ({
|
||||
type: COMPOSE_SUGGESTIONS_READY,
|
||||
|
|
@ -591,7 +633,7 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
|
|||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function updateSuggestionTags(token) {
|
||||
return {
|
||||
|
|
@ -652,19 +694,19 @@ export function mountCompose() {
|
|||
return {
|
||||
type: COMPOSE_MOUNT,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmountCompose() {
|
||||
return {
|
||||
type: COMPOSE_UNMOUNT,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposeSensitivity() {
|
||||
return {
|
||||
type: COMPOSE_SENSITIVITY_CHANGE,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const changeComposeLanguage = language => ({
|
||||
type: COMPOSE_LANGUAGE_CHANGE,
|
||||
|
|
@ -675,21 +717,21 @@ 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 insertEmojiCompose(position, emoji, needsSpace) {
|
||||
return {
|
||||
|
|
@ -698,33 +740,33 @@ export function insertEmojiCompose(position, emoji, needsSpace) {
|
|||
emoji,
|
||||
needsSpace,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function changeComposing(value) {
|
||||
return {
|
||||
type: COMPOSE_COMPOSING_CHANGE,
|
||||
value,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
@ -732,14 +774,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 {
|
||||
|
|
@ -747,4 +789,4 @@ export function changePollSettings(expiresIn, isMultiple) {
|
|||
expiresIn,
|
||||
isMultiple,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
|
||||
export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
|
||||
|
||||
export function openDropdownMenu(id, placement, keyboard, scroll_key) {
|
||||
return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard, scroll_key };
|
||||
export function openDropdownMenu(id, keyboard, scroll_key) {
|
||||
return { type: DROPDOWN_MENU_OPEN, id, keyboard, scroll_key };
|
||||
}
|
||||
|
||||
export function closeDropdownMenu(id) {
|
||||
|
|
|
|||
|
|
@ -11,4 +11,4 @@ export function useEmoji(emoji) {
|
|||
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ export function setHeight (key, id, height) {
|
|||
id,
|
||||
height,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function clearHeight () {
|
||||
return {
|
||||
type: HEIGHT_CACHE_CLEAR,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export function reblog(status, visibility) {
|
|||
dispatch(reblogFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unreblog(status) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -67,7 +67,7 @@ export function unreblog(status) {
|
|||
dispatch(unreblogFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function reblogRequest(status) {
|
||||
return {
|
||||
|
|
@ -75,7 +75,7 @@ export function reblogRequest(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function reblogSuccess(status) {
|
||||
return {
|
||||
|
|
@ -83,7 +83,7 @@ export function reblogSuccess(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function reblogFail(status, error) {
|
||||
return {
|
||||
|
|
@ -92,7 +92,7 @@ export function reblogFail(status, error) {
|
|||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unreblogRequest(status) {
|
||||
return {
|
||||
|
|
@ -100,7 +100,7 @@ export function unreblogRequest(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unreblogSuccess(status) {
|
||||
return {
|
||||
|
|
@ -108,7 +108,7 @@ export function unreblogSuccess(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unreblogFail(status, error) {
|
||||
return {
|
||||
|
|
@ -117,7 +117,7 @@ export function unreblogFail(status, error) {
|
|||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function favourite(status) {
|
||||
return function (dispatch, getState) {
|
||||
|
|
@ -130,7 +130,7 @@ export function favourite(status) {
|
|||
dispatch(favouriteFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfavourite(status) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -143,7 +143,7 @@ export function unfavourite(status) {
|
|||
dispatch(unfavouriteFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function favouriteRequest(status) {
|
||||
return {
|
||||
|
|
@ -151,7 +151,7 @@ export function favouriteRequest(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function favouriteSuccess(status) {
|
||||
return {
|
||||
|
|
@ -159,7 +159,7 @@ export function favouriteSuccess(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function favouriteFail(status, error) {
|
||||
return {
|
||||
|
|
@ -168,7 +168,7 @@ export function favouriteFail(status, error) {
|
|||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfavouriteRequest(status) {
|
||||
return {
|
||||
|
|
@ -176,7 +176,7 @@ export function unfavouriteRequest(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfavouriteSuccess(status) {
|
||||
return {
|
||||
|
|
@ -184,7 +184,7 @@ export function unfavouriteSuccess(status) {
|
|||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unfavouriteFail(status, error) {
|
||||
return {
|
||||
|
|
@ -193,7 +193,7 @@ export function unfavouriteFail(status, error) {
|
|||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function bookmark(status) {
|
||||
return function (dispatch, getState) {
|
||||
|
|
@ -206,7 +206,7 @@ export function bookmark(status) {
|
|||
dispatch(bookmarkFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unbookmark(status) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -219,14 +219,14 @@ export function unbookmark(status) {
|
|||
dispatch(unbookmarkFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function bookmarkRequest(status) {
|
||||
return {
|
||||
type: BOOKMARK_REQUEST,
|
||||
status: status,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function bookmarkSuccess(status, response) {
|
||||
return {
|
||||
|
|
@ -234,7 +234,7 @@ export function bookmarkSuccess(status, response) {
|
|||
status: status,
|
||||
response: response,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function bookmarkFail(status, error) {
|
||||
return {
|
||||
|
|
@ -242,14 +242,14 @@ export function bookmarkFail(status, error) {
|
|||
status: status,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unbookmarkRequest(status) {
|
||||
return {
|
||||
type: UNBOOKMARK_REQUEST,
|
||||
status: status,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unbookmarkSuccess(status, response) {
|
||||
return {
|
||||
|
|
@ -257,7 +257,7 @@ export function unbookmarkSuccess(status, response) {
|
|||
status: status,
|
||||
response: response,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unbookmarkFail(status, error) {
|
||||
return {
|
||||
|
|
@ -265,7 +265,7 @@ export function unbookmarkFail(status, error) {
|
|||
status: status,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchReblogs(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -278,14 +278,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 {
|
||||
|
|
@ -293,14 +293,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) => {
|
||||
|
|
@ -313,14 +313,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 {
|
||||
|
|
@ -328,14 +328,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) => {
|
||||
|
|
@ -348,7 +348,7 @@ export function pin(status) {
|
|||
dispatch(pinFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinRequest(status) {
|
||||
return {
|
||||
|
|
@ -356,7 +356,7 @@ export function pinRequest(status) {
|
|||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinSuccess(status) {
|
||||
return {
|
||||
|
|
@ -364,7 +364,7 @@ export function pinSuccess(status) {
|
|||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function pinFail(status, error) {
|
||||
return {
|
||||
|
|
@ -373,7 +373,7 @@ export function pinFail(status, error) {
|
|||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpin (status) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -386,7 +386,7 @@ export function unpin (status) {
|
|||
dispatch(unpinFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinRequest(status) {
|
||||
return {
|
||||
|
|
@ -394,7 +394,7 @@ export function unpinRequest(status) {
|
|||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinSuccess(status) {
|
||||
return {
|
||||
|
|
@ -402,7 +402,7 @@ export function unpinSuccess(status) {
|
|||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unpinFail(status, error) {
|
||||
return {
|
||||
|
|
@ -411,4 +411,4 @@ export function unpinFail(status, error) {
|
|||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
|
|||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
|
||||
|
||||
|
|
@ -197,14 +197,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 {
|
||||
|
|
@ -215,7 +215,7 @@ export function expandNotificationsSuccess(notifications, next, isLoadingMore, i
|
|||
usePendingItems,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandNotificationsFail(error, isLoadingMore) {
|
||||
return {
|
||||
|
|
@ -224,7 +224,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
|
|||
skipLoading: !isLoadingMore,
|
||||
skipAlert: !isLoadingMore || error.name === 'AbortError',
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function clearNotifications() {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -234,14 +234,14 @@ export function clearNotifications() {
|
|||
|
||||
api(getState).post('/api/v1/notifications/clear');
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function scrollTopNotifications(top) {
|
||||
return {
|
||||
type: NOTIFICATIONS_SCROLL_TOP,
|
||||
top,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function setFilter (filterType) {
|
||||
return dispatch => {
|
||||
|
|
@ -253,7 +253,7 @@ export function setFilter (filterType) {
|
|||
dispatch(expandNotifications({ forceLoad: true }));
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const mountNotifications = () => ({
|
||||
type: NOTIFICATIONS_MOUNT,
|
||||
|
|
@ -291,7 +291,7 @@ export function requestBrowserPermission(callback = noOp) {
|
|||
callback(permission);
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function setBrowserSupport (value) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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']);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
return {
|
||||
|
|
@ -91,7 +91,7 @@ export function redraft(status, raw_text) {
|
|||
status,
|
||||
raw_text,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const editStatus = (id, routerHistory) => (dispatch, getState) => {
|
||||
let status = getState().getIn(['statuses', id]);
|
||||
|
|
@ -147,21 +147,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 {
|
||||
|
|
@ -169,7 +169,7 @@ export function deleteStatusFail(id, error) {
|
|||
id: id,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const updateStatus = status => dispatch =>
|
||||
dispatch(importFetchedStatus(status));
|
||||
|
|
@ -190,14 +190,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 {
|
||||
|
|
@ -207,7 +207,7 @@ export function fetchContextSuccess(id, ancestors, descendants) {
|
|||
descendants,
|
||||
statuses: ancestors.concat(descendants),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchContextFail(id, error) {
|
||||
return {
|
||||
|
|
@ -216,7 +216,7 @@ export function fetchContextFail(id, error) {
|
|||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function muteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -228,21 +228,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 {
|
||||
|
|
@ -250,7 +250,7 @@ export function muteStatusFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function unmuteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -262,21 +262,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 {
|
||||
|
|
@ -284,7 +284,7 @@ export function unmuteStatusFail(id, error) {
|
|||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function hideStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
|
|
@ -295,7 +295,7 @@ export function hideStatus(ids) {
|
|||
type: STATUS_HIDE,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function revealStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
|
|
@ -306,7 +306,7 @@ export function revealStatus(ids) {
|
|||
type: STATUS_REVEAL,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleStatusCollapse(id, isCollapsed) {
|
||||
return {
|
||||
|
|
@ -314,7 +314,7 @@ export function toggleStatusCollapse(id, isCollapsed) {
|
|||
id,
|
||||
isCollapsed,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const translateStatus = id => (dispatch, getState) => {
|
||||
dispatch(translateStatusRequest(id));
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@ export function hydrateStore(rawState) {
|
|||
dispatch(hydrateCompose());
|
||||
dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export function updateTimeline(timeline, status, accept) {
|
|||
dispatch(submitMarkers());
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteFromTimelines(id) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -67,13 +67,13 @@ export function deleteFromTimelines(id) {
|
|||
reblogOf,
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function clearTimeline(timeline) {
|
||||
return (dispatch) => {
|
||||
dispatch({ type: TIMELINE_CLEAR, timeline });
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const noOp = () => {};
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
|||
done();
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function fillTimelineGaps(timelineId, path, params = {}, done = noOp) {
|
||||
return (dispatch, getState) => {
|
||||
|
|
@ -168,7 +168,7 @@ export function expandTimelineRequest(timeline, isLoadingMore) {
|
|||
timeline,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) {
|
||||
return {
|
||||
|
|
@ -181,7 +181,7 @@ export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadi
|
|||
usePendingItems,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function expandTimelineFail(timeline, error, isLoadingMore) {
|
||||
return {
|
||||
|
|
@ -191,7 +191,7 @@ export function expandTimelineFail(timeline, error, isLoadingMore) {
|
|||
skipLoading: !isLoadingMore,
|
||||
skipNotFound: timeline.startsWith('account:'),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function scrollTopTimeline(timeline, top) {
|
||||
return {
|
||||
|
|
@ -199,7 +199,7 @@ export function scrollTopTimeline(timeline, top) {
|
|||
timeline,
|
||||
top,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function connectTimeline(timeline) {
|
||||
return {
|
||||
|
|
@ -207,7 +207,7 @@ export function connectTimeline(timeline) {
|
|||
timeline,
|
||||
usePendingItems: preferPendingItems,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export const disconnectTimeline = timeline => ({
|
||||
type: TIMELINE_DISCONNECT,
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ export function start() {
|
|||
} catch (e) {
|
||||
// If called twice
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ export default function compareId (id1, id2) {
|
|||
} else {
|
||||
return id1.length > id2.length ? 1 : -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,27 +47,27 @@ class Account extends ImmutablePureComponent {
|
|||
|
||||
handleFollow = () => {
|
||||
this.props.onFollow(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleBlock = () => {
|
||||
this.props.onBlock(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMute = () => {
|
||||
this.props.onMute(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteNotifications = () => {
|
||||
this.props.onMuteNotifications(this.props.account, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleUnmuteNotifications = () => {
|
||||
this.props.onMuteNotifications(this.props.account, false);
|
||||
}
|
||||
};
|
||||
|
||||
handleAction = () => {
|
||||
this.props.onActionClick(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, intl, hidden, onActionClick, actionIcon, actionTitle, defaultAction, size } = this.props;
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ export default class Retention extends React.PureComponent {
|
|||
break;
|
||||
default:
|
||||
title = <FormattedMessage id='admin.dashboard.monthly_retention' defaultMessage='User retention rate by month after sign-up' />;
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='retention'>
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default class Trends extends React.PureComponent {
|
|||
<Hashtag
|
||||
key={hashtag.name}
|
||||
name={hashtag.name}
|
||||
to={`/admin/tags/${hashtag.id}`}
|
||||
to={hashtag.id === undefined ? undefined : `/admin/tags/${hashtag.id}`}
|
||||
people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1}
|
||||
uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1}
|
||||
history={hashtag.history.reverse().map(day => day.uses)}
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ export default class AnimatedNumber extends React.PureComponent {
|
|||
const { direction } = this.state;
|
||||
|
||||
return { y: -1 * direction };
|
||||
}
|
||||
};
|
||||
|
||||
willLeave = () => {
|
||||
const { direction } = this.state;
|
||||
|
||||
return { y: spring(1 * direction, { damping: 35, stiffness: 400 }) };
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, obfuscate } = this.props;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
id: PropTypes.string,
|
||||
searchTokens: PropTypes.arrayOf(PropTypes.string),
|
||||
maxLength: PropTypes.number,
|
||||
lang: PropTypes.string,
|
||||
spellCheck: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -77,7 +79,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
onKeyDown = (e) => {
|
||||
const { suggestions, disabled } = this.props;
|
||||
|
|
@ -135,22 +137,22 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
this.setState({ suggestionsHidden: true, focused: false });
|
||||
}
|
||||
};
|
||||
|
||||
onFocus = () => {
|
||||
this.setState({ focused: true });
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionClick = (e) => {
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||
e.preventDefault();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||
this.input.focus();
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
|
||||
|
|
@ -160,7 +162,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
|
||||
setInput = (c) => {
|
||||
this.input = c;
|
||||
}
|
||||
};
|
||||
|
||||
renderSuggestion = (suggestion, i) => {
|
||||
const { selectedSuggestion } = this.state;
|
||||
|
|
@ -182,10 +184,10 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
{inner}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props;
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, lang, spellCheck } = this.props;
|
||||
const { suggestionsHidden } = this.state;
|
||||
|
||||
return (
|
||||
|
|
@ -210,6 +212,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
|||
id={id}
|
||||
className={className}
|
||||
maxLength={maxLength}
|
||||
lang={lang}
|
||||
spellCheck={spellCheck}
|
||||
/>
|
||||
</label>
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
onKeyDown: PropTypes.func,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
autoFocus: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -74,7 +75,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onChange(e);
|
||||
}
|
||||
};
|
||||
|
||||
onKeyDown = (e) => {
|
||||
const { suggestions, disabled } = this.props;
|
||||
|
|
@ -132,25 +133,25 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
this.setState({ suggestionsHidden: true, focused: false });
|
||||
}
|
||||
};
|
||||
|
||||
onFocus = (e) => {
|
||||
this.setState({ focused: true });
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionClick = (e) => {
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||
e.preventDefault();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||
this.textarea.focus();
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
|
||||
|
|
@ -160,14 +161,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
|
||||
setTextarea = (c) => {
|
||||
this.textarea = c;
|
||||
}
|
||||
};
|
||||
|
||||
onPaste = (e) => {
|
||||
if (e.clipboardData && e.clipboardData.files.length === 1) {
|
||||
this.props.onPaste(e.clipboardData.files);
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renderSuggestion = (suggestion, i) => {
|
||||
const { selectedSuggestion } = this.state;
|
||||
|
|
@ -189,10 +190,10 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
{inner}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
|
||||
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, lang, children } = this.props;
|
||||
const { suggestionsHidden } = this.state;
|
||||
|
||||
return [
|
||||
|
|
@ -216,6 +217,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
|||
onPaste={this.onPaste}
|
||||
dir='auto'
|
||||
aria-autocomplete='list'
|
||||
lang={lang}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ export default class Avatar extends React.PureComponent {
|
|||
handleMouseEnter = () => {
|
||||
if (this.props.animate) return;
|
||||
this.setState({ hovering: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
if (this.props.animate) return;
|
||||
this.setState({ hovering: false });
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, size, animate, inline } = this.props;
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ export default class AvatarOverlay extends React.PureComponent {
|
|||
handleMouseEnter = () => {
|
||||
if (this.props.animate) return;
|
||||
this.setState({ hovering: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
if (this.props.animate) return;
|
||||
this.setState({ hovering: false });
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { account, friend, animate, size, baseSize, overlaySize } = this.props;
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ export default class Button extends React.PureComponent {
|
|||
if (!this.props.disabled && this.props.onClick) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.node.focus();
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ export default class Column extends React.PureComponent {
|
|||
}
|
||||
|
||||
this._interruptScrollAnimation();
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
if (this.props.bindToDocument) {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ export default class ColumnBackButton extends React.PureComponent {
|
|||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (window.history && window.history.length === 1) {
|
||||
this.context.router.history.push('/');
|
||||
} else {
|
||||
if (window.history && window.history.state) {
|
||||
this.context.router.history.goBack();
|
||||
} else {
|
||||
this.context.router.history.push('/');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { multiColumn } = this.props;
|
||||
|
|
|
|||
|
|
@ -43,38 +43,34 @@ class ColumnHeader extends React.PureComponent {
|
|||
animating: false,
|
||||
};
|
||||
|
||||
historyBack = () => {
|
||||
if (window.history && window.history.length === 1) {
|
||||
this.context.router.history.push('/');
|
||||
} else {
|
||||
this.context.router.history.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleClick = (e) => {
|
||||
e.stopPropagation();
|
||||
this.setState({ collapsed: !this.state.collapsed, animating: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleTitleClick = () => {
|
||||
this.props.onClick?.();
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveLeft = () => {
|
||||
this.props.onMove(-1);
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveRight = () => {
|
||||
this.props.onMove(1);
|
||||
}
|
||||
};
|
||||
|
||||
handleBackClick = () => {
|
||||
this.historyBack();
|
||||
}
|
||||
if (window.history && window.history.state) {
|
||||
this.context.router.history.goBack();
|
||||
} else {
|
||||
this.context.router.history.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
handleTransitionEnd = () => {
|
||||
this.setState({ animating: false });
|
||||
}
|
||||
};
|
||||
|
||||
handlePin = () => {
|
||||
if (!this.props.pinned) {
|
||||
|
|
@ -82,7 +78,7 @@ class ColumnHeader extends React.PureComponent {
|
|||
}
|
||||
|
||||
this.props.onPin();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class DismissableBanner extends React.PureComponent {
|
|||
handleDismiss = () => {
|
||||
const { id } = this.props;
|
||||
this.setState({ visible: false }, () => bannerSettings.set(id, true));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { visible } = this.state;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default class DisplayName extends React.PureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = ({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
|
|
@ -36,7 +36,7 @@ export default class DisplayName extends React.PureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-static');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { others, localDomain } = this.props;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class Account extends ImmutablePureComponent {
|
|||
|
||||
handleDomainUnblock = () => {
|
||||
this.props.onUnblockDomain(this.props.domain);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { domain, intl } = this.props;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import IconButton from './icon_button';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Motion from '../features/ui/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import classNames from 'classnames';
|
||||
import { CircularProgress } from 'mastodon/components/loading_indicator';
|
||||
|
|
@ -24,9 +22,6 @@ class DropdownMenu extends React.PureComponent {
|
|||
scrollable: PropTypes.bool,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
placement: PropTypes.string,
|
||||
arrowOffsetLeft: PropTypes.string,
|
||||
arrowOffsetTop: PropTypes.string,
|
||||
openedViaKeyboard: PropTypes.bool,
|
||||
renderItem: PropTypes.func,
|
||||
renderHeader: PropTypes.func,
|
||||
|
|
@ -35,18 +30,13 @@ class DropdownMenu extends React.PureComponent {
|
|||
|
||||
static defaultProps = {
|
||||
style: {},
|
||||
placement: 'bottom',
|
||||
};
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
};
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
|
|
@ -56,8 +46,6 @@ class DropdownMenu extends React.PureComponent {
|
|||
if (this.focusedItem && this.props.openedViaKeyboard) {
|
||||
this.focusedItem.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
this.setState({ mounted: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
|
|
@ -68,11 +56,11 @@ class DropdownMenu extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
setFocusRef = c => {
|
||||
this.focusedItem = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
const items = Array.from(this.node.querySelectorAll('a, button'));
|
||||
|
|
@ -109,18 +97,18 @@ class DropdownMenu extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleItemKeyPress = e => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this.handleClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = e => {
|
||||
const { onItemClick } = this.props;
|
||||
onItemClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
renderItem = (option, i) => {
|
||||
if (option === null) {
|
||||
|
|
@ -136,43 +124,31 @@ class DropdownMenu extends React.PureComponent {
|
|||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { items, style, placement, arrowOffsetLeft, arrowOffsetTop, scrollable, renderHeader, loading } = this.props;
|
||||
const { mounted } = this.state;
|
||||
const { items, scrollable, renderHeader, loading } = this.props;
|
||||
|
||||
let renderItem = this.props.renderItem || this.renderItem;
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
||||
<div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })} ref={this.setRef}>
|
||||
{loading && (
|
||||
<CircularProgress size={30} strokeWidth={3.5} />
|
||||
)}
|
||||
|
||||
<div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })}>
|
||||
{loading && (
|
||||
<CircularProgress size={30} strokeWidth={3.5} />
|
||||
)}
|
||||
|
||||
{!loading && renderHeader && (
|
||||
<div className='dropdown-menu__container__header'>
|
||||
{renderHeader(items)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && (
|
||||
<ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
|
||||
{items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
{!loading && renderHeader && (
|
||||
<div className='dropdown-menu__container__header'>
|
||||
{renderHeader(items)}
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
|
||||
{!loading && (
|
||||
<ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
|
||||
{items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +173,6 @@ export default class Dropdown extends React.PureComponent {
|
|||
isUserTouching: PropTypes.func,
|
||||
onOpen: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
dropdownPlacement: PropTypes.string,
|
||||
openDropdownId: PropTypes.number,
|
||||
openedViaKeyboard: PropTypes.bool,
|
||||
renderItem: PropTypes.func,
|
||||
|
|
@ -213,15 +188,13 @@ export default class Dropdown extends React.PureComponent {
|
|||
id: id++,
|
||||
};
|
||||
|
||||
handleClick = ({ target, type }) => {
|
||||
handleClick = ({ type }) => {
|
||||
if (this.state.id === this.props.openDropdownId) {
|
||||
this.handleClose();
|
||||
} else {
|
||||
const { top } = target.getBoundingClientRect();
|
||||
const placement = top * 2 < innerHeight ? 'bottom' : 'top';
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
if (this.activeElement) {
|
||||
|
|
@ -229,13 +202,13 @@ export default class Dropdown extends React.PureComponent {
|
|||
this.activeElement = null;
|
||||
}
|
||||
this.props.onClose(this.state.id);
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = () => {
|
||||
if (!this.state.open) {
|
||||
this.activeElement = document.activeElement;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleButtonKeyDown = (e) => {
|
||||
switch(e.key) {
|
||||
|
|
@ -244,7 +217,7 @@ export default class Dropdown extends React.PureComponent {
|
|||
this.handleMouseDown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
switch(e.key) {
|
||||
|
|
@ -255,7 +228,7 @@ export default class Dropdown extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleItemClick = e => {
|
||||
const { onItemClick } = this.props;
|
||||
|
|
@ -274,25 +247,25 @@ export default class Dropdown extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
this.context.router.history.push(item.to);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setTargetRef = c => {
|
||||
this.target = c;
|
||||
}
|
||||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.target;
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
if (this.state.id === this.props.openDropdownId) {
|
||||
this.handleClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
close = () => {
|
||||
this.handleClose();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const {
|
||||
|
|
@ -303,7 +276,6 @@ export default class Dropdown extends React.PureComponent {
|
|||
disabled,
|
||||
loading,
|
||||
scrollable,
|
||||
dropdownPlacement,
|
||||
openDropdownId,
|
||||
openedViaKeyboard,
|
||||
children,
|
||||
|
|
@ -314,7 +286,6 @@ export default class Dropdown extends React.PureComponent {
|
|||
const open = this.state.id === openDropdownId;
|
||||
|
||||
const button = children ? React.cloneElement(React.Children.only(children), {
|
||||
ref: this.setTargetRef,
|
||||
onClick: this.handleClick,
|
||||
onMouseDown: this.handleMouseDown,
|
||||
onKeyDown: this.handleButtonKeyDown,
|
||||
|
|
@ -326,7 +297,6 @@ export default class Dropdown extends React.PureComponent {
|
|||
active={open}
|
||||
disabled={disabled}
|
||||
size={size}
|
||||
ref={this.setTargetRef}
|
||||
onClick={this.handleClick}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleButtonKeyDown}
|
||||
|
|
@ -336,19 +306,27 @@ export default class Dropdown extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{button}
|
||||
|
||||
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
|
||||
<DropdownMenu
|
||||
items={items}
|
||||
loading={loading}
|
||||
scrollable={scrollable}
|
||||
onClose={this.handleClose}
|
||||
openedViaKeyboard={openedViaKeyboard}
|
||||
renderItem={renderItem}
|
||||
renderHeader={renderHeader}
|
||||
onItemClick={this.handleItemClick}
|
||||
/>
|
||||
<span ref={this.setTargetRef}>
|
||||
{button}
|
||||
</span>
|
||||
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props, arrowProps, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation dropdown-menu ${placement}`}>
|
||||
<div className={`dropdown-menu__arrow ${placement}`} {...arrowProps} />
|
||||
<DropdownMenu
|
||||
items={items}
|
||||
loading={loading}
|
||||
scrollable={scrollable}
|
||||
onClose={this.handleClose}
|
||||
openedViaKeyboard={openedViaKeyboard}
|
||||
renderItem={renderItem}
|
||||
renderHeader={renderHeader}
|
||||
onItemClick={this.handleItemClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { fetchHistory } from 'mastodon/actions/history';
|
|||
import DropdownMenu from 'mastodon/components/dropdown_menu';
|
||||
|
||||
const mapStateToProps = (state, { statusId }) => ({
|
||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
||||
items: state.getIn(['history', statusId, 'items']),
|
||||
|
|
@ -13,9 +12,9 @@ const mapStateToProps = (state, { statusId }) => ({
|
|||
|
||||
const mapDispatchToProps = (dispatch, { statusId }) => ({
|
||||
|
||||
onOpen (id, onItemClick, dropdownPlacement, keyboard) {
|
||||
onOpen (id, onItemClick, keyboard) {
|
||||
dispatch(fetchHistory(statusId));
|
||||
dispatch(openDropdownMenu(id, dropdownPlacement, keyboard));
|
||||
dispatch(openDropdownMenu(id, keyboard));
|
||||
},
|
||||
|
||||
onClose (id) {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class EditedTimestamp extends React.PureComponent {
|
|||
return (
|
||||
<FormattedMessage id='status.edited_x_times' defaultMessage='Edited {count, plural, one {{count} time} other {{count} times}}' values={{ count: items.size - 1 }} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderItem = (item, index, { onClick, onKeyPress }) => {
|
||||
const formattedDate = <RelativeTimestamp timestamp={item.get('created_at')} short={false} />;
|
||||
|
|
@ -53,7 +53,7 @@ class EditedTimestamp extends React.PureComponent {
|
|||
<button data-index={index} onClick={onClick} onKeyPress={onKeyPress}>{label}</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { timestamp, intl, statusId } = this.props;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||
|
||||
this.setState({ copied: true });
|
||||
setTimeout(() => this.setState({ copied: false }), 700);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { hasError, copied, errorMessage } = this.state;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default class GIFV extends React.PureComponent {
|
|||
|
||||
handleLoadedData = () => {
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.src !== this.props.src) {
|
||||
|
|
@ -32,7 +32,7 @@ export default class GIFV extends React.PureComponent {
|
|||
e.stopPropagation();
|
||||
onClick();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { src, width, height, alt } = this.props;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export default class IconButton extends React.PureComponent {
|
|||
counter: PropTypes.number,
|
||||
obfuscateCount: PropTypes.bool,
|
||||
href: PropTypes.string,
|
||||
ariaHidden: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
|
@ -36,12 +37,13 @@ export default class IconButton extends React.PureComponent {
|
|||
animate: false,
|
||||
overlay: false,
|
||||
tabIndex: '0',
|
||||
ariaHidden: false,
|
||||
};
|
||||
|
||||
state = {
|
||||
activate: false,
|
||||
deactivate: false,
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!nextProps.animate) return;
|
||||
|
|
@ -59,25 +61,25 @@ export default class IconButton extends React.PureComponent {
|
|||
if (!this.props.disabled) {
|
||||
this.props.onClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (this.props.onKeyPress && !this.props.disabled) {
|
||||
this.props.onKeyPress(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
if (!this.props.disabled && this.props.onMouseDown) {
|
||||
this.props.onMouseDown(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (!this.props.disabled && this.props.onKeyDown) {
|
||||
this.props.onKeyDown(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const style = {
|
||||
|
|
@ -102,6 +104,7 @@ export default class IconButton extends React.PureComponent {
|
|||
counter,
|
||||
obfuscateCount,
|
||||
href,
|
||||
ariaHidden,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
|
@ -142,6 +145,7 @@ export default class IconButton extends React.PureComponent {
|
|||
type='button'
|
||||
aria-label={title}
|
||||
aria-expanded={expanded}
|
||||
aria-hidden={ariaHidden}
|
||||
title={title}
|
||||
className={classes}
|
||||
onClick={this.handleClick}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
|||
|
||||
state = {
|
||||
isHidden: false, // set to true in requestIdleCallback to trigger un-render
|
||||
}
|
||||
};
|
||||
|
||||
shouldComponentUpdate (nextProps, nextState) {
|
||||
const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight);
|
||||
|
|
@ -62,7 +62,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
|||
|
||||
scheduleIdleTask(this.calculateHeight);
|
||||
this.setState(this.updateStateAfterIntersection);
|
||||
}
|
||||
};
|
||||
|
||||
updateStateAfterIntersection = (prevState) => {
|
||||
if (prevState.isIntersecting !== false && !this.entry.isIntersecting) {
|
||||
|
|
@ -72,7 +72,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
|||
isIntersecting: this.entry.isIntersecting,
|
||||
isHidden: false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
calculateHeight = () => {
|
||||
const { onHeightChange, saveHeightKey, id } = this.props;
|
||||
|
|
@ -83,7 +83,7 @@ export default class IntersectionObserverArticle extends React.Component {
|
|||
if (onHeightChange && saveHeightKey) {
|
||||
onHeightChange(saveHeightKey, id, this.height);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
hideIfNotIntersecting = () => {
|
||||
if (!this.componentMounted) {
|
||||
|
|
@ -95,11 +95,11 @@ export default class IntersectionObserverArticle extends React.Component {
|
|||
// this is to save DOM nodes and avoid using up too much memory.
|
||||
// See: https://github.com/mastodon/mastodon/issues/2900
|
||||
this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
|
||||
}
|
||||
};
|
||||
|
||||
handleRef = (node) => {
|
||||
this.node = node;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { children, id, index, listLength, cachedHeight } = this.props;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class LoadGap extends React.PureComponent {
|
|||
|
||||
handleClick = () => {
|
||||
this.props.onClick(this.props.maxId);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { disabled, intl } = this.props;
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ export default class LoadMore extends React.PureComponent {
|
|||
onClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
visible: PropTypes.bool,
|
||||
}
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
visible: true,
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { disabled, visible } = this.props;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default class LoadPending extends React.PureComponent {
|
|||
static propTypes = {
|
||||
onClick: PropTypes.func,
|
||||
count: PropTypes.number,
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { count } = this.props;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='media-gallery' style={{ height, width }} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderLoadingVideoPlayer = () => {
|
||||
const { height, width } = this.props;
|
||||
|
|
@ -37,7 +37,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='video-player' style={{ height, width }} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderLoadingAudioPlayer = () => {
|
||||
const { height, width } = this.props;
|
||||
|
|
@ -45,7 +45,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='audio-player' style={{ height, width }} />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, width, height } = this.props;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
|
|||
import { is } from 'immutable';
|
||||
import IconButton from './icon_button';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { isIOS } from '../is_mobile';
|
||||
import classNames from 'classnames';
|
||||
import { autoPlayGif, cropImages, displayMedia, useBlurhash } from '../initial_state';
|
||||
import { debounce } from 'lodash';
|
||||
|
|
@ -41,14 +40,14 @@ class Item extends React.PureComponent {
|
|||
if (this.hoverToPlay()) {
|
||||
e.target.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = (e) => {
|
||||
if (this.hoverToPlay()) {
|
||||
e.target.pause();
|
||||
e.target.currentTime = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getAutoPlay() {
|
||||
return this.props.autoplay || autoPlayGif;
|
||||
|
|
@ -72,11 +71,11 @@ class Item extends React.PureComponent {
|
|||
}
|
||||
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
handleImageLoad = () => {
|
||||
this.setState({ loaded: true });
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { attachment, index, size, standalone, displayWidth, visible } = this.props;
|
||||
|
|
@ -181,7 +180,7 @@ class Item extends React.PureComponent {
|
|||
</a>
|
||||
);
|
||||
} else if (attachment.get('type') === 'gifv') {
|
||||
const autoPlay = !isIOS() && this.getAutoPlay();
|
||||
const autoPlay = this.getAutoPlay();
|
||||
|
||||
thumbnail = (
|
||||
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
|
||||
|
|
@ -195,6 +194,7 @@ class Item extends React.PureComponent {
|
|||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
autoPlay={autoPlay}
|
||||
playsInline
|
||||
loop
|
||||
muted
|
||||
/>
|
||||
|
|
@ -277,11 +277,11 @@ class MediaGallery extends React.PureComponent {
|
|||
} else {
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = (index) => {
|
||||
this.props.onOpenMedia(this.props.media, index);
|
||||
}
|
||||
};
|
||||
|
||||
handleRef = c => {
|
||||
this.node = c;
|
||||
|
|
@ -289,7 +289,7 @@ class MediaGallery extends React.PureComponent {
|
|||
if (this.node) {
|
||||
this._setDimensions();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_setDimensions () {
|
||||
const width = this.node.offsetWidth;
|
||||
|
|
@ -345,7 +345,7 @@ class MediaGallery extends React.PureComponent {
|
|||
</button>
|
||||
);
|
||||
} else if (visible) {
|
||||
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} />;
|
||||
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />;
|
||||
} else {
|
||||
spoilerButton = (
|
||||
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export default class ModalRoot extends React.PureComponent {
|
|||
&& !!this.props.children) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.key === 'Tab') {
|
||||
|
|
@ -49,7 +49,7 @@ export default class ModalRoot extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
window.addEventListener('keyup', this.handleKeyUp, false);
|
||||
|
|
@ -122,11 +122,11 @@ export default class ModalRoot extends React.PureComponent {
|
|||
|
||||
getSiblings = () => {
|
||||
return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
|
||||
}
|
||||
};
|
||||
|
||||
setRef = ref => {
|
||||
this.node = ref;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { children, onClose } = this.props;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
|
|||
handleClick = () => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(removePictureInPicture());
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
|
|
@ -30,7 +30,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
|
|||
if (this.node) {
|
||||
this._setDimensions();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_setDimensions () {
|
||||
const width = this.node.offsetWidth;
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ class Poll extends ImmutablePureComponent {
|
|||
tmp[value] = true;
|
||||
this.setState({ selected: tmp });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleOptionChange = ({ target: { value } }) => {
|
||||
this._toggleOption(value);
|
||||
|
|
@ -107,7 +107,7 @@ class Poll extends ImmutablePureComponent {
|
|||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleVote = () => {
|
||||
if (this.props.disabled) {
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class ScrollableList extends PureComponent {
|
|||
} else {
|
||||
return this.node;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setScrollTop = newScrollTop => {
|
||||
if (this.getScrollTop() !== newScrollTop) {
|
||||
|
|
@ -143,7 +143,7 @@ class ScrollableList extends PureComponent {
|
|||
|
||||
this.mouseMovedRecently = false;
|
||||
this.scrollToTopOnMouseIdle = false;
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this.attachScrollListener();
|
||||
|
|
@ -161,25 +161,25 @@ class ScrollableList extends PureComponent {
|
|||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getScrollTop = () => {
|
||||
return this._getScrollingElement().scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
getScrollHeight = () => {
|
||||
return this._getScrollingElement().scrollHeight;
|
||||
}
|
||||
};
|
||||
|
||||
getClientHeight = () => {
|
||||
return this._getScrollingElement().clientHeight;
|
||||
}
|
||||
};
|
||||
|
||||
updateScrollBottom = (snapshot) => {
|
||||
const newScrollTop = this.getScrollHeight() - snapshot;
|
||||
|
||||
this.setScrollTop(newScrollTop);
|
||||
}
|
||||
};
|
||||
|
||||
getSnapshotBeforeUpdate (prevProps) {
|
||||
const someItemInserted = React.Children.count(prevProps.children) > 0 &&
|
||||
|
|
@ -206,7 +206,7 @@ class ScrollableList extends PureComponent {
|
|||
if (width && this.state.cachedMediaWidth !== width) {
|
||||
this.setState({ cachedMediaWidth: width });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
this.clearMouseIdleTimer();
|
||||
|
|
@ -218,7 +218,7 @@ class ScrollableList extends PureComponent {
|
|||
|
||||
onFullScreenChange = () => {
|
||||
this.setState({ fullscreen: isFullscreen() });
|
||||
}
|
||||
};
|
||||
|
||||
attachIntersectionObserver () {
|
||||
let nodeOptions = {
|
||||
|
|
@ -269,12 +269,12 @@ class ScrollableList extends PureComponent {
|
|||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadMore = e => {
|
||||
e.preventDefault();
|
||||
this.props.onLoadMore();
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadPending = e => {
|
||||
e.preventDefault();
|
||||
|
|
@ -286,7 +286,7 @@ class ScrollableList extends PureComponent {
|
|||
this.clearMouseIdleTimer();
|
||||
this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
|
||||
this.mouseMovedRecently = true;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleToggleMediaVisibility = () => {
|
||||
this.setState({ showMedia: !this.state.showMedia });
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = e => {
|
||||
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
|
||||
|
|
@ -147,11 +147,11 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.handleHotkeyOpen();
|
||||
}
|
||||
};
|
||||
|
||||
handlePrependAccountClick = e => {
|
||||
this.handleAccountClick(e, false);
|
||||
}
|
||||
};
|
||||
|
||||
handleAccountClick = (e, proper = true) => {
|
||||
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
|
||||
|
|
@ -163,19 +163,19 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this._openProfile(proper);
|
||||
}
|
||||
};
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleCollapsedToggle = isCollapsed => {
|
||||
this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
|
||||
}
|
||||
};
|
||||
|
||||
handleTranslate = () => {
|
||||
this.props.onTranslate(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
renderLoadingMediaGallery () {
|
||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
||||
|
|
@ -192,11 +192,11 @@ class Status extends ImmutablePureComponent {
|
|||
handleOpenVideo = (options) => {
|
||||
const status = this._properStatus();
|
||||
this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options);
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenMedia = (media, index) => {
|
||||
this.props.onOpenMedia(this._properStatus().get('id'), media, index);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenMedia = e => {
|
||||
const { onOpenMedia, onOpenVideo } = this.props;
|
||||
|
|
@ -211,32 +211,32 @@ class Status extends ImmutablePureComponent {
|
|||
onOpenMedia(status.get('id'), status.get('media_attachments'), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleDeployPictureInPicture = (type, mediaProps) => {
|
||||
const { deployPictureInPicture } = this.props;
|
||||
const status = this._properStatus();
|
||||
|
||||
deployPictureInPicture(status, type, mediaProps);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyReply = e => {
|
||||
e.preventDefault();
|
||||
this.props.onReply(this._properStatus(), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyFavourite = () => {
|
||||
this.props.onFavourite(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyBoost = e => {
|
||||
this.props.onReblog(this._properStatus(), e);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.props.onMention(this._properStatus().get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpen = () => {
|
||||
if (this.props.onClick) {
|
||||
|
|
@ -252,11 +252,11 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this._openProfile();
|
||||
}
|
||||
};
|
||||
|
||||
_openProfile = (proper = true) => {
|
||||
const { router } = this.context;
|
||||
|
|
@ -267,32 +267,32 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
router.history.push(`/@${status.getIn(['account', 'acct'])}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = e => {
|
||||
this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = e => {
|
||||
this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleHidden = () => {
|
||||
this.props.onToggleHidden(this._properStatus());
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyToggleSensitive = () => {
|
||||
this.handleToggleMediaVisibility();
|
||||
}
|
||||
};
|
||||
|
||||
handleUnfilterClick = e => {
|
||||
this.setState({ forceFilter: false });
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
handleFilterClick = () => {
|
||||
this.setState({ forceFilter: true });
|
||||
}
|
||||
};
|
||||
|
||||
_properStatus () {
|
||||
const { status } = this.props;
|
||||
|
|
@ -306,7 +306,7 @@ class Status extends ImmutablePureComponent {
|
|||
|
||||
handleRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
let media = null;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { defineMessages, injectIntl } from 'react-intl';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { me } from '../initial_state';
|
||||
import classNames from 'classnames';
|
||||
import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
|
|
@ -37,9 +37,10 @@ const messages = defineMessages({
|
|||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this status in the moderation interface' },
|
||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to status' },
|
||||
hide: { id: 'status.hide', defaultMessage: 'Hide toot' },
|
||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
||||
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
||||
hide: { id: 'status.hide', defaultMessage: 'Hide post' },
|
||||
blockDomain: { id: 'account.block_domain', defaultMessage: 'Block domain {domain}' },
|
||||
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
|
||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||
|
|
@ -96,7 +97,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
'status',
|
||||
'relationship',
|
||||
'withDismiss',
|
||||
]
|
||||
];
|
||||
|
||||
handleReplyClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
|
|
@ -106,7 +107,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.onInteractionModal('reply', this.props.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleShareClick = () => {
|
||||
navigator.share({
|
||||
|
|
@ -115,7 +116,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}).catch((e) => {
|
||||
if (e.name !== 'AbortError') console.error(e);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleFavouriteClick = () => {
|
||||
const { signedIn } = this.context.identity;
|
||||
|
|
@ -125,7 +126,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.onInteractionModal('favourite', this.props.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleReblogClick = e => {
|
||||
const { signedIn } = this.context.identity;
|
||||
|
|
@ -135,35 +136,35 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
} else {
|
||||
this.props.onInteractionModal('reblog', this.props.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBookmarkClick = () => {
|
||||
this.props.onBookmark(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleDeleteClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleRedraftClick = () => {
|
||||
this.props.onDelete(this.props.status, this.context.router.history, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleEditClick = () => {
|
||||
this.props.onEdit(this.props.status, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handlePinClick = () => {
|
||||
this.props.onPin(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleMentionClick = () => {
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleDirectClick = () => {
|
||||
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleMuteClick = () => {
|
||||
const { status, relationship, onMute, onUnmute } = this.props;
|
||||
|
|
@ -174,7 +175,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
} else {
|
||||
onMute(account);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockClick = () => {
|
||||
const { status, relationship, onBlock, onUnblock } = this.props;
|
||||
|
|
@ -185,54 +186,54 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
} else {
|
||||
onBlock(status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockDomain = () => {
|
||||
const { status, onBlockDomain } = this.props;
|
||||
const account = status.get('account');
|
||||
|
||||
onBlockDomain(account.get('acct').split('@')[1]);
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockDomain = () => {
|
||||
const { status, onUnblockDomain } = this.props;
|
||||
const account = status.get('account');
|
||||
|
||||
onUnblockDomain(account.get('acct').split('@')[1]);
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleEmbed = () => {
|
||||
this.props.onEmbed(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleReport = () => {
|
||||
this.props.onReport(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMuteClick = () => {
|
||||
this.props.onMuteConversation(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleFilterClick = () => {
|
||||
this.props.onAddFilter(this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleCopy = () => {
|
||||
const url = this.props.status.get('url');
|
||||
navigator.clipboard.writeText(url);
|
||||
}
|
||||
};
|
||||
|
||||
handleHideClick = () => {
|
||||
this.props.onFilter();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
|
||||
const anonymousAccess = !signedIn;
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
|
|
@ -246,12 +247,13 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
|
||||
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
|
||||
|
||||
if (publicStatus) {
|
||||
if (isRemote) {
|
||||
menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get('url') });
|
||||
}
|
||||
if (publicStatus && isRemote) {
|
||||
menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get('url') });
|
||||
}
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
|
||||
menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
|
||||
|
||||
if (publicStatus) {
|
||||
menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed });
|
||||
}
|
||||
|
||||
|
|
@ -311,10 +313,16 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if ((this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${status.getIn(['account', 'id'])}` });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: `/admin/accounts/${status.getIn(['account', 'id'])}/statuses/${status.get('id')}` });
|
||||
}
|
||||
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||
const domain = account.get('acct').split('@')[1];
|
||||
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class StatusContent extends React.PureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = ({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
|
|
@ -143,7 +143,7 @@ class StatusContent extends React.PureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-static');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._updateStatusLinks();
|
||||
|
|
@ -158,7 +158,7 @@ class StatusContent extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
this.context.router.history.push(`/@${mention.get('acct')}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onHashtagClick = (hashtag, e) => {
|
||||
hashtag = hashtag.replace(/^#/, '');
|
||||
|
|
@ -167,11 +167,11 @@ class StatusContent extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
this.context.router.history.push(`/tags/${hashtag}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
this.startXY = [e.clientX, e.clientY];
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseUp = (e) => {
|
||||
if (!this.startXY) {
|
||||
|
|
@ -194,7 +194,7 @@ class StatusContent extends React.PureComponent {
|
|||
}
|
||||
|
||||
this.startXY = null;
|
||||
}
|
||||
};
|
||||
|
||||
handleSpoilerClick = (e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -205,15 +205,15 @@ class StatusContent extends React.PureComponent {
|
|||
} else {
|
||||
this.setState({ hidden: !this.state.hidden });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleTranslate = () => {
|
||||
this.props.onTranslate();
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, intl } = this.props;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default class StatusList extends ImmutablePureComponent {
|
|||
|
||||
getFeaturedStatusCount = () => {
|
||||
return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
|
||||
}
|
||||
};
|
||||
|
||||
getCurrentStatusIndex = (id, featured) => {
|
||||
if (featured) {
|
||||
|
|
@ -42,21 +42,21 @@ export default class StatusList extends ImmutablePureComponent {
|
|||
} else {
|
||||
return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveUp = (id, featured) => {
|
||||
const elementIndex = this.getCurrentStatusIndex(id, featured) - 1;
|
||||
this._selectChild(elementIndex, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveDown = (id, featured) => {
|
||||
const elementIndex = this.getCurrentStatusIndex(id, featured) + 1;
|
||||
this._selectChild(elementIndex, false);
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadOlder = debounce(() => {
|
||||
this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
|
||||
}, 300, { leading: true })
|
||||
}, 300, { leading: true });
|
||||
|
||||
_selectChild (index, align_top) {
|
||||
const container = this.node.node;
|
||||
|
|
@ -74,7 +74,7 @@ export default class StatusList extends ImmutablePureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,12 @@ import DropdownMenu from '../components/dropdown_menu';
|
|||
import { isUserTouching } from '../is_mobile';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
dropdownPlacement: state.getIn(['dropdown_menu', 'placement']),
|
||||
openDropdownId: state.getIn(['dropdown_menu', 'openId']),
|
||||
openedViaKeyboard: state.getIn(['dropdown_menu', 'keyboard']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
|
||||
onOpen(id, onItemClick, dropdownPlacement, keyboard) {
|
||||
onOpen(id, onItemClick, keyboard) {
|
||||
if (status) {
|
||||
dispatch(fetchRelationships([status.getIn(['account', 'id'])]));
|
||||
}
|
||||
|
|
@ -21,7 +20,7 @@ const mapDispatchToProps = (dispatch, { status, items, scrollKey }) => ({
|
|||
status,
|
||||
actions: items,
|
||||
onClick: onItemClick,
|
||||
}) : openDropdownMenu(id, dropdownPlacement, keyboard, scrollKey));
|
||||
}) : openDropdownMenu(id, keyboard, scrollKey));
|
||||
},
|
||||
|
||||
onClose(id) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ export const store = configureStore();
|
|||
const hydrateAction = hydrateStore(initialState);
|
||||
|
||||
store.dispatch(hydrateAction);
|
||||
store.dispatch(fetchCustomEmojis());
|
||||
if (initialState.meta.me) {
|
||||
store.dispatch(fetchCustomEmojis());
|
||||
}
|
||||
|
||||
const createIdentityContext = state => ({
|
||||
signedIn: !!state.meta.me,
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default class MediaContainer extends PureComponent {
|
|||
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
|
||||
|
||||
this.setState({ media, index });
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenVideo = (options) => {
|
||||
const { components } = this.props;
|
||||
|
|
@ -50,7 +50,7 @@ export default class MediaContainer extends PureComponent {
|
|||
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
|
||||
|
||||
this.setState({ media: mediaList, options });
|
||||
}
|
||||
};
|
||||
|
||||
handleCloseMedia = () => {
|
||||
document.body.classList.remove('with-modals--active');
|
||||
|
|
@ -63,11 +63,11 @@ export default class MediaContainer extends PureComponent {
|
|||
backgroundColor: null,
|
||||
options: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
setBackgroundColor = color => {
|
||||
this.setState({ backgroundColor: color });
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { locale, components } = this.props;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ const messages = defineMessages({
|
|||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||
editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
|
||||
editMessage: { id: 'confirmations.edit.message', defaultMessage: 'Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||
});
|
||||
|
||||
|
|
@ -149,7 +151,18 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
|||
},
|
||||
|
||||
onEdit (status, history) {
|
||||
dispatch(editStatus(status.get('id'), history));
|
||||
dispatch((_, getState) => {
|
||||
let state = getState();
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(messages.editMessage),
|
||||
confirm: intl.formatMessage(messages.editConfirm),
|
||||
onConfirm: () => dispatch(editStatus(status.get('id'), history)),
|
||||
}));
|
||||
} else {
|
||||
dispatch(editStatus(status.get('id'), history));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onTranslate (status) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,2 @@
|
|||
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
|
||||
import 'intersection-observer';
|
||||
import 'requestidlecallback';
|
||||
import objectFitImages from 'object-fit-images';
|
||||
|
||||
objectFitImages();
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class Section extends React.PureComponent {
|
|||
const { collapsed } = this.state;
|
||||
|
||||
this.setState({ collapsed: !collapsed }, () => onOpen && onOpen());
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { title, children } = this.props;
|
||||
|
|
@ -106,7 +106,7 @@ class About extends React.PureComponent {
|
|||
handleDomainBlocksOpen = () => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(fetchDomainBlocks());
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { multiColumn, intl, server, extendedDescription, domainBlocks } = this.props;
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class AccountNote extends ImmutablePureComponent {
|
|||
|
||||
setTextareaRef = c => {
|
||||
this.textarea = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
this.setState({ value: e.target.value, saving: false });
|
||||
|
|
@ -114,13 +114,13 @@ class AccountNote extends ImmutablePureComponent {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlur = () => {
|
||||
if (this._isDirty()) {
|
||||
this._save();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_save (showMessage = true) {
|
||||
this.setState({ saving: true }, () => this.props.onSave(this.state.value));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
||||
export default class FollowRequestNote extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, onAuthorize, onReject } = this.props;
|
||||
|
||||
return (
|
||||
<div className='follow-request-banner'>
|
||||
<div className='follow-request-banner__message'>
|
||||
<FormattedMessage id='account.requested_follow' defaultMessage='{name} has requested to follow you' values={{ name: <bdi><strong dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }} /></bdi> }} />
|
||||
</div>
|
||||
|
||||
<div className='follow-request-banner__action'>
|
||||
<button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}>
|
||||
<Icon id='check' fixedWidth />
|
||||
<FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
|
||||
</button>
|
||||
|
||||
<button type='button' className='button button-tertiary button--destructive' onClick={onReject}>
|
||||
<Icon id='times' fixedWidth />
|
||||
<FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,8 @@ import ShortNumber from 'mastodon/components/short_number';
|
|||
import { NavLink } from 'react-router-dom';
|
||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||
import AccountNoteContainer from '../containers/account_note_container';
|
||||
import { PERMISSION_MANAGE_USERS } from 'mastodon/permissions';
|
||||
import FollowRequestNoteContainer from '../containers/follow_request_note_container';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
|
@ -45,6 +46,7 @@ const messages = defineMessages({
|
|||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
|
|
@ -52,6 +54,7 @@ const messages = defineMessages({
|
|||
unendorse: { id: 'account.unendorse', defaultMessage: 'Don\'t feature on profile' },
|
||||
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||
languages: { id: 'account.languages', defaultMessage: 'Change subscribed languages' },
|
||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||
});
|
||||
|
|
@ -106,7 +109,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
openEditProfile = () => {
|
||||
window.open('/settings/profile', '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
isStatusesPageActive = (match, location) => {
|
||||
if (!match) {
|
||||
|
|
@ -114,7 +117,7 @@ class Header extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
return !location.pathname.match(/\/(followers|following)\/?$/);
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseEnter = ({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
|
|
@ -127,7 +130,7 @@ class Header extends ImmutablePureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = ({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
|
|
@ -140,18 +143,29 @@ class Header extends ImmutablePureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-static');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleAvatarClick = e => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.props.onOpenAvatar();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleShare = () => {
|
||||
const { account } = this.props;
|
||||
|
||||
navigator.share({
|
||||
text: `${titleFromAccount(account)}\n${account.get('note_plain')}`,
|
||||
url: account.get('url'),
|
||||
}).catch((e) => {
|
||||
if (e.name !== 'AbortError') console.error(e);
|
||||
});
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, hidden, intl, domain } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
const { signedIn, permissions } = this.context.identity;
|
||||
|
||||
if (!account) {
|
||||
return null;
|
||||
|
|
@ -180,7 +194,7 @@ class Header extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
|
||||
bellBtn = <IconButton icon='bell-o' size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
|
||||
}
|
||||
|
||||
if (me !== account.get('id')) {
|
||||
|
|
@ -229,6 +243,7 @@ class Header extends ImmutablePureComponent {
|
|||
menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
|
||||
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||
|
|
@ -276,9 +291,14 @@ class Header extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
if (account.get('id') !== me && (this.context.identity.permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
if ((account.get('id') !== me && (permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
|
||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
|
||||
}
|
||||
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: remoteDomain }), href: `/admin/instances/${remoteDomain}` });
|
||||
}
|
||||
}
|
||||
|
||||
const content = { __html: account.get('note_emojified') };
|
||||
|
|
@ -300,6 +320,8 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
{!(suspended || hidden || account.get('moved')) && account.getIn(['relationship', 'requested_by']) && <FollowRequestNoteContainer account={account} />}
|
||||
|
||||
<div className='account__header__image'>
|
||||
<div className='account__header__info'>
|
||||
{!suspended && info}
|
||||
|
|
@ -331,7 +353,9 @@ class Header extends ImmutablePureComponent {
|
|||
<div className='account__header__tabs__name'>
|
||||
<h1>
|
||||
<span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
|
||||
<small>@{acct} {lockedIcon}</small>
|
||||
<small>
|
||||
<span>@{acct}</span> {lockedIcon}
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { connect } from 'react-redux';
|
||||
import FollowRequestNote from '../components/follow_request_note';
|
||||
import { authorizeFollowRequest, rejectFollowRequest } from 'mastodon/actions/accounts';
|
||||
|
||||
const mapDispatchToProps = (dispatch, { account }) => ({
|
||||
onAuthorize () {
|
||||
dispatch(authorizeFollowRequest(account.get('id')));
|
||||
},
|
||||
|
||||
onReject () {
|
||||
dispatch(rejectFollowRequest(account.get('id')));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(FollowRequestNote);
|
||||
|
|
@ -2,7 +2,6 @@ import Blurhash from 'mastodon/components/blurhash';
|
|||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
|
||||
import { isIOS } from 'mastodon/is_mobile';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
|
@ -23,20 +22,20 @@ export default class MediaItem extends ImmutablePureComponent {
|
|||
|
||||
handleImageLoad = () => {
|
||||
this.setState({ loaded: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseEnter = e => {
|
||||
if (this.hoverToPlay()) {
|
||||
e.target.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = e => {
|
||||
if (this.hoverToPlay()) {
|
||||
e.target.pause();
|
||||
e.target.currentTime = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
hoverToPlay () {
|
||||
return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
|
||||
|
|
@ -52,7 +51,7 @@ export default class MediaItem extends ImmutablePureComponent {
|
|||
this.setState({ visible: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { attachment, displayWidth } = this.props;
|
||||
|
|
@ -105,11 +104,13 @@ export default class MediaItem extends ImmutablePureComponent {
|
|||
<video
|
||||
className='media-gallery__item-gifv-thumbnail'
|
||||
aria-label={attachment.get('description')}
|
||||
title={attachment.get('description')}
|
||||
role='application'
|
||||
src={attachment.get('url')}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
autoPlay={!isIOS() && autoPlayGif}
|
||||
autoPlay={autoPlayGif}
|
||||
playsInline
|
||||
loop
|
||||
muted
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class LoadMoreMedia extends ImmutablePureComponent {
|
|||
|
||||
handleLoadMore = () => {
|
||||
this.props.onLoadMore(this.props.maxId);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
return (
|
||||
|
|
@ -114,7 +114,7 @@ class AccountGallery extends ImmutablePureComponent {
|
|||
if (this.props.hasMore) {
|
||||
this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleScroll = e => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
||||
|
|
@ -123,7 +123,7 @@ class AccountGallery extends ImmutablePureComponent {
|
|||
if (150 > offset && !this.props.isLoading) {
|
||||
this.handleScrollToBottom();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { maxId }));
|
||||
|
|
@ -132,7 +132,7 @@ class AccountGallery extends ImmutablePureComponent {
|
|||
handleLoadOlder = e => {
|
||||
e.preventDefault();
|
||||
this.handleScrollToBottom();
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenMedia = attachment => {
|
||||
const { dispatch } = this.props;
|
||||
|
|
@ -148,13 +148,13 @@ class AccountGallery extends ImmutablePureComponent {
|
|||
|
||||
dispatch(openModal('MEDIA', { media, index, statusId }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleRef = c => {
|
||||
if (c) {
|
||||
this.setState({ width: c.offsetWidth });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
|
||||
|
|
|
|||
|
|
@ -36,35 +36,35 @@ export default class Header extends ImmutablePureComponent {
|
|||
|
||||
handleFollow = () => {
|
||||
this.props.onFollow(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleBlock = () => {
|
||||
this.props.onBlock(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMention = () => {
|
||||
this.props.onMention(this.props.account, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleDirect = () => {
|
||||
this.props.onDirect(this.props.account, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleReport = () => {
|
||||
this.props.onReport(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleReblogToggle = () => {
|
||||
this.props.onReblogToggle(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleNotifyToggle = () => {
|
||||
this.props.onNotifyToggle(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleMute = () => {
|
||||
this.props.onMute(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleBlockDomain = () => {
|
||||
const domain = this.props.account.get('acct').split('@')[1];
|
||||
|
|
@ -72,7 +72,7 @@ export default class Header extends ImmutablePureComponent {
|
|||
if (!domain) return;
|
||||
|
||||
this.props.onBlockDomain(domain);
|
||||
}
|
||||
};
|
||||
|
||||
handleUnblockDomain = () => {
|
||||
const domain = this.props.account.get('acct').split('@')[1];
|
||||
|
|
@ -80,31 +80,31 @@ export default class Header extends ImmutablePureComponent {
|
|||
if (!domain) return;
|
||||
|
||||
this.props.onUnblockDomain(domain);
|
||||
}
|
||||
};
|
||||
|
||||
handleEndorseToggle = () => {
|
||||
this.props.onEndorseToggle(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleAddToList = () => {
|
||||
this.props.onAddToList(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleEditAccountNote = () => {
|
||||
this.props.onEditAccountNote(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeLanguages = () => {
|
||||
this.props.onChangeLanguages(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleInteractionModal = () => {
|
||||
this.props.onInteractionModal(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenAvatar = () => {
|
||||
this.props.onOpenAvatar(this.props.account);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, hidden, hideTabs } = this.props;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class LimitedAccountHint extends React.PureComponent {
|
|||
static propTypes = {
|
||||
accountId: PropTypes.string.isRequired,
|
||||
reveal: PropTypes.func,
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { reveal } = this.props;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,13 @@ const emptyList = ImmutableList();
|
|||
const mapStateToProps = (state, { params: { acct, id, tagged }, withReplies = false }) => {
|
||||
const accountId = id || state.getIn(['accounts_map', normalizeForLookup(acct)]);
|
||||
|
||||
if (!accountId) {
|
||||
if (accountId === null) {
|
||||
return {
|
||||
isLoading: false,
|
||||
isAccount: false,
|
||||
statusIds: emptyList,
|
||||
};
|
||||
} else if (!accountId) {
|
||||
return {
|
||||
isLoading: true,
|
||||
statusIds: emptyList,
|
||||
|
|
@ -139,7 +145,7 @@ class AccountTimeline extends ImmutablePureComponent {
|
|||
|
||||
handleLoadMore = maxId => {
|
||||
this.props.dispatch(expandAccountTimeline(this.props.accountId, { maxId, withReplies: this.props.withReplies, tagged: this.props.params.tagged }));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { accountId, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, hidden, multiColumn, remote, remoteUrl } = this.props;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class Audio extends React.PureComponent {
|
|||
duration: null,
|
||||
paused: true,
|
||||
muted: false,
|
||||
volume: 0.5,
|
||||
volume: 1,
|
||||
dragging: false,
|
||||
revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
||||
};
|
||||
|
|
@ -75,13 +75,13 @@ class Audio extends React.PureComponent {
|
|||
if (this.player) {
|
||||
this._setDimensions();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_pack() {
|
||||
return {
|
||||
src: this.props.src,
|
||||
volume: this.audio.volume,
|
||||
muted: this.audio.muted,
|
||||
volume: this.state.volume,
|
||||
muted: this.state.muted,
|
||||
currentTime: this.audio.currentTime,
|
||||
poster: this.props.poster,
|
||||
backgroundColor: this.props.backgroundColor,
|
||||
|
|
@ -105,25 +105,26 @@ class Audio extends React.PureComponent {
|
|||
|
||||
setSeekRef = c => {
|
||||
this.seek = c;
|
||||
}
|
||||
};
|
||||
|
||||
setVolumeRef = c => {
|
||||
this.volume = c;
|
||||
}
|
||||
};
|
||||
|
||||
setAudioRef = c => {
|
||||
this.audio = c;
|
||||
|
||||
if (this.audio) {
|
||||
this.setState({ volume: this.audio.volume, muted: this.audio.muted });
|
||||
this.audio.volume = 1;
|
||||
this.audio.muted = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setCanvasRef = c => {
|
||||
this.canvas = c;
|
||||
|
||||
this.visualizer.setCanvas(c);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
|
|
@ -162,7 +163,7 @@ class Audio extends React.PureComponent {
|
|||
} else {
|
||||
this.setState({ paused: true }, () => this.audio.pause());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleResize = debounce(() => {
|
||||
if (this.player) {
|
||||
|
|
@ -180,7 +181,7 @@ class Audio extends React.PureComponent {
|
|||
}
|
||||
|
||||
this._renderCanvas();
|
||||
}
|
||||
};
|
||||
|
||||
handlePause = () => {
|
||||
this.setState({ paused: true });
|
||||
|
|
@ -188,7 +189,7 @@ class Audio extends React.PureComponent {
|
|||
if (this.audioContext) {
|
||||
this.audioContext.suspend();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleProgress = () => {
|
||||
const lastTimeRange = this.audio.buffered.length - 1;
|
||||
|
|
@ -196,15 +197,17 @@ class Audio extends React.PureComponent {
|
|||
if (lastTimeRange > -1) {
|
||||
this.setState({ buffer: Math.ceil(this.audio.buffered.end(lastTimeRange) / this.audio.duration * 100) });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
toggleMute = () => {
|
||||
const muted = !this.state.muted;
|
||||
|
||||
this.setState({ muted }, () => {
|
||||
this.audio.muted = muted;
|
||||
if (this.gainNode) {
|
||||
this.gainNode.gain.value = muted ? 0 : this.state.volume;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
toggleReveal = () => {
|
||||
if (this.props.onToggleVisibility) {
|
||||
|
|
@ -212,7 +215,7 @@ class Audio extends React.PureComponent {
|
|||
} else {
|
||||
this.setState({ revealed: !this.state.revealed });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleVolumeMouseDown = e => {
|
||||
document.addEventListener('mousemove', this.handleMouseVolSlide, true);
|
||||
|
|
@ -224,14 +227,14 @@ class Audio extends React.PureComponent {
|
|||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
handleVolumeMouseUp = () => {
|
||||
document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
|
||||
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
|
||||
document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
|
||||
document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = e => {
|
||||
document.addEventListener('mousemove', this.handleMouseMove, true);
|
||||
|
|
@ -245,7 +248,7 @@ class Audio extends React.PureComponent {
|
|||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseUp = () => {
|
||||
document.removeEventListener('mousemove', this.handleMouseMove, true);
|
||||
|
|
@ -255,7 +258,7 @@ class Audio extends React.PureComponent {
|
|||
|
||||
this.setState({ dragging: false });
|
||||
this.audio.play();
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseMove = throttle(e => {
|
||||
const { x } = getPointerPosition(this.seek, e);
|
||||
|
|
@ -273,14 +276,16 @@ class Audio extends React.PureComponent {
|
|||
currentTime: this.audio.currentTime,
|
||||
duration: this.audio.duration,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseVolSlide = throttle(e => {
|
||||
const { x } = getPointerPosition(this.volume, e);
|
||||
|
||||
if(!isNaN(x)) {
|
||||
this.setState({ volume: x }, () => {
|
||||
this.audio.volume = x;
|
||||
if (this.gainNode) {
|
||||
this.gainNode.gain.value = this.state.muted ? 0 : x;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 15);
|
||||
|
|
@ -306,41 +311,38 @@ class Audio extends React.PureComponent {
|
|||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ hovered: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ hovered: false });
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadedData = () => {
|
||||
const { autoPlay, currentTime, volume, muted } = this.props;
|
||||
const { autoPlay, currentTime } = this.props;
|
||||
|
||||
if (currentTime) {
|
||||
this.audio.currentTime = currentTime;
|
||||
}
|
||||
|
||||
if (volume !== undefined) {
|
||||
this.audio.volume = volume;
|
||||
}
|
||||
|
||||
if (muted !== undefined) {
|
||||
this.audio.muted = muted;
|
||||
}
|
||||
|
||||
if (autoPlay) {
|
||||
this.togglePlay();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_initAudioContext () {
|
||||
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
const context = new AudioContext();
|
||||
const source = context.createMediaElementSource(this.audio);
|
||||
const gainNode = context.createGain();
|
||||
|
||||
gainNode.gain.value = this.state.muted ? 0 : this.state.volume;
|
||||
|
||||
this.visualizer.setAudioContext(context, source);
|
||||
source.connect(context.destination);
|
||||
source.connect(gainNode);
|
||||
gainNode.connect(context.destination);
|
||||
|
||||
this.audioContext = context;
|
||||
this.gainNode = gainNode;
|
||||
}
|
||||
|
||||
handleDownload = () => {
|
||||
|
|
@ -359,7 +361,7 @@ class Audio extends React.PureComponent {
|
|||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_renderCanvas () {
|
||||
requestAnimationFrame(() => {
|
||||
|
|
@ -430,7 +432,7 @@ class Audio extends React.PureComponent {
|
|||
e.stopPropagation();
|
||||
this.togglePlay();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
switch(e.key) {
|
||||
|
|
@ -455,7 +457,7 @@ class Audio extends React.PureComponent {
|
|||
this.seekBy(10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { src, intl, alt, editable, autoPlay, sensitive, blurhash } = this.props;
|
||||
|
|
|
|||
|
|
@ -48,24 +48,24 @@ class Bookmarks extends ImmutablePureComponent {
|
|||
} else {
|
||||
dispatch(addColumn('BOOKMARKS', {}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMove = (dir) => {
|
||||
const { columnId, dispatch } = this.props;
|
||||
dispatch(moveColumn(columnId, dir));
|
||||
}
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadMore = debounce(() => {
|
||||
this.props.dispatch(expandBookmarkedStatuses());
|
||||
}, 300, { leading: true })
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
|
||||
|
|
|
|||
|
|
@ -72,4 +72,4 @@ class ClosedRegistrationsModal extends ImmutablePureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,16 +60,16 @@ class CommunityTimeline extends React.PureComponent {
|
|||
} else {
|
||||
dispatch(addColumn('COMMUNITY', { other: { onlyMedia } }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMove = (dir) => {
|
||||
const { columnId, dispatch } = this.props;
|
||||
dispatch(moveColumn(columnId, dir));
|
||||
}
|
||||
};
|
||||
|
||||
handleHeaderClick = () => {
|
||||
this.column.scrollTop();
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
|
|
@ -109,13 +109,13 @@ class CommunityTimeline extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.column = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadMore = maxId => {
|
||||
const { dispatch, onlyMedia } = this.props;
|
||||
|
||||
dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ const messages = defineMessages({
|
|||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||
followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
|
||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||
domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
|
|
@ -30,7 +31,7 @@ class ActionBar extends React.PureComponent {
|
|||
|
||||
handleLogout = () => {
|
||||
this.props.onLogout();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
|
|
@ -45,6 +46,7 @@ class ActionBar extends React.PureComponent {
|
|||
menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
|
||||
menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
|
||||
menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
|
||||
menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
|
||||
menu.push(null);
|
||||
menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
|
||||
menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import PollFormContainer from '../containers/poll_form_container';
|
|||
import UploadFormContainer from '../containers/upload_form_container';
|
||||
import WarningContainer from '../containers/warning_container';
|
||||
import LanguageDropdown from '../containers/language_dropdown_container';
|
||||
import { isMobile } from '../../../is_mobile';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { length } from 'stringz';
|
||||
import { countableText } from '../util/counter';
|
||||
|
|
@ -61,29 +60,30 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onChangeSpoilerText: PropTypes.func.isRequired,
|
||||
onPaste: PropTypes.func.isRequired,
|
||||
onPickEmoji: PropTypes.func.isRequired,
|
||||
showSearch: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
isInReply: PropTypes.bool,
|
||||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
showSearch: false,
|
||||
autoFocus: false,
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
this.handleSubmit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
getFulltextForCharacterCounting = () => {
|
||||
return [this.props.spoiler? this.props.spoilerText: '', countableText(this.props.text)].join('');
|
||||
}
|
||||
};
|
||||
|
||||
canSubmit = () => {
|
||||
const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
|
||||
|
|
@ -91,7 +91,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
|
||||
|
||||
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 640 || (isOnlyWhitespace && !anyMedia));
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmit = (e) => {
|
||||
if (this.props.text !== this.autosuggestTextarea.textarea.value) {
|
||||
|
|
@ -109,27 +109,27 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
|
||||
}
|
||||
};
|
||||
|
||||
onSpoilerSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeSpoilerText = (e) => {
|
||||
this.props.onChangeSpoilerText(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
if (this.composeForm && !this.props.singleColumn) {
|
||||
|
|
@ -138,7 +138,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
this.composeForm.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
this._updateFocusAndSelection({ });
|
||||
|
|
@ -154,7 +154,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
// - Replying to zero or one users, places the cursor at the end of the textbox.
|
||||
// - Replying to more than one user, selects any usernames past the first;
|
||||
// this provides a convenient shortcut to drop everyone else from the conversation.
|
||||
if (this.props.focusDate !== prevProps.focusDate) {
|
||||
if (this.props.focusDate && this.props.focusDate !== prevProps.focusDate) {
|
||||
let selectionEnd, selectionStart;
|
||||
|
||||
if (this.props.preselectDate !== prevProps.preselectDate && this.props.isInReply) {
|
||||
|
|
@ -180,19 +180,19 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
} else if (this.props.spoiler !== prevProps.spoiler) {
|
||||
if (this.props.spoiler) {
|
||||
this.spoilerText.input.focus();
|
||||
} else {
|
||||
} else if (prevProps.spoiler) {
|
||||
this.autosuggestTextarea.textarea.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setAutosuggestTextarea = (c) => {
|
||||
this.autosuggestTextarea = c;
|
||||
}
|
||||
};
|
||||
|
||||
setSpoilerText = (c) => {
|
||||
this.spoilerText = c;
|
||||
}
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.composeForm = c;
|
||||
|
|
@ -204,10 +204,10 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
|
||||
|
||||
this.props.onPickEmoji(position, data, needsSpace);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, onPaste, showSearch } = this.props;
|
||||
const { intl, onPaste, autoFocus } = this.props;
|
||||
const disabled = this.props.isSubmitting;
|
||||
|
||||
let publishText = '';
|
||||
|
|
@ -226,7 +226,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
|
||||
<ReplyIndicatorContainer />
|
||||
|
||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}>
|
||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef} aria-hidden={!this.props.spoiler}>
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
|
||||
value={this.props.spoilerText}
|
||||
|
|
@ -241,6 +241,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
searchTokens={[':']}
|
||||
id='cw-spoiler-input'
|
||||
className='spoiler-input__input'
|
||||
lang={this.props.lang}
|
||||
spellCheck
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -257,7 +259,8 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth)}
|
||||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import classNames from 'classnames';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
|
|
@ -57,7 +57,7 @@ class ModifierPickerMenu extends React.PureComponent {
|
|||
|
||||
handleClick = e => {
|
||||
this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1);
|
||||
}
|
||||
};
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.active) {
|
||||
|
|
@ -75,7 +75,7 @@ class ModifierPickerMenu extends React.PureComponent {
|
|||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
attachListeners () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
|
|
@ -89,7 +89,7 @@ class ModifierPickerMenu extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { active } = this.props;
|
||||
|
|
@ -124,12 +124,12 @@ class ModifierPicker extends React.PureComponent {
|
|||
} else {
|
||||
this.props.onOpen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleSelect = modifier => {
|
||||
this.props.onChange(modifier);
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { active, modifier } = this.props;
|
||||
|
|
@ -154,9 +154,6 @@ class EmojiPickerMenu extends React.PureComponent {
|
|||
onClose: PropTypes.func.isRequired,
|
||||
onPick: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
placement: PropTypes.string,
|
||||
arrowOffsetLeft: PropTypes.string,
|
||||
arrowOffsetTop: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
skinTone: PropTypes.number.isRequired,
|
||||
onSkinTone: PropTypes.func.isRequired,
|
||||
|
|
@ -177,7 +174,7 @@ class EmojiPickerMenu extends React.PureComponent {
|
|||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
|
|
@ -201,7 +198,7 @@ class EmojiPickerMenu extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
getI18n = () => {
|
||||
const { intl } = this.props;
|
||||
|
|
@ -222,7 +219,7 @@ class EmojiPickerMenu extends React.PureComponent {
|
|||
custom: intl.formatMessage(messages.custom),
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = (emoji, event) => {
|
||||
if (!emoji.native) {
|
||||
|
|
@ -232,19 +229,19 @@ class EmojiPickerMenu extends React.PureComponent {
|
|||
this.props.onClose();
|
||||
}
|
||||
this.props.onPick(emoji);
|
||||
}
|
||||
};
|
||||
|
||||
handleModifierOpen = () => {
|
||||
this.setState({ modifierOpen: true });
|
||||
}
|
||||
};
|
||||
|
||||
handleModifierClose = () => {
|
||||
this.setState({ modifierOpen: false });
|
||||
}
|
||||
};
|
||||
|
||||
handleModifierChange = modifier => {
|
||||
this.props.onSkinTone(modifier);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { loading, style, intl, custom_emojis, skinTone, frequentlyUsedEmojis } = this.props;
|
||||
|
|
@ -324,14 +321,13 @@ class EmojiPickerDropdown extends React.PureComponent {
|
|||
state = {
|
||||
active: false,
|
||||
loading: false,
|
||||
placement: null,
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.dropdown = c;
|
||||
}
|
||||
};
|
||||
|
||||
onShowDropdown = ({ target }) => {
|
||||
onShowDropdown = () => {
|
||||
this.setState({ active: true });
|
||||
|
||||
if (!EmojiPicker) {
|
||||
|
|
@ -346,14 +342,11 @@ class EmojiPickerDropdown extends React.PureComponent {
|
|||
this.setState({ loading: false, active: false });
|
||||
});
|
||||
}
|
||||
|
||||
const { top } = target.getBoundingClientRect();
|
||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
|
||||
}
|
||||
};
|
||||
|
||||
onHideDropdown = () => {
|
||||
this.setState({ active: false });
|
||||
}
|
||||
};
|
||||
|
||||
onToggle = (e) => {
|
||||
if (!this.state.loading && (!e.key || e.key === 'Enter')) {
|
||||
|
|
@ -363,26 +356,26 @@ class EmojiPickerDropdown extends React.PureComponent {
|
|||
this.onShowDropdown(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
if (e.key === 'Escape') {
|
||||
this.onHideDropdown();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setTargetRef = c => {
|
||||
this.target = c;
|
||||
}
|
||||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.target;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
|
||||
const title = intl.formatMessage(messages.emoji);
|
||||
const { active, loading, placement } = this.state;
|
||||
const { active, loading } = this.state;
|
||||
|
||||
return (
|
||||
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
|
||||
|
|
@ -394,16 +387,22 @@ class EmojiPickerDropdown extends React.PureComponent {
|
|||
/>}
|
||||
</div>
|
||||
|
||||
<Overlay show={active} placement={placement} target={this.findTarget}>
|
||||
<EmojiPickerMenu
|
||||
custom_emojis={this.props.custom_emojis}
|
||||
loading={loading}
|
||||
onClose={this.onHideDropdown}
|
||||
onPick={onPickEmoji}
|
||||
onSkinTone={onSkinTone}
|
||||
skinTone={skinTone}
|
||||
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
||||
/>
|
||||
<Overlay show={active} placement={'bottom'} target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props, placement })=> (
|
||||
<div {...props} style={{ ...props.style, width: 299 }}>
|
||||
<div className={`dropdown-animation ${placement}`}>
|
||||
<EmojiPickerMenu
|
||||
custom_emojis={this.props.custom_emojis}
|
||||
loading={loading}
|
||||
onClose={this.onHideDropdown}
|
||||
onPick={onPickEmoji}
|
||||
onSkinTone={onSkinTone}
|
||||
skinTone={skinTone}
|
||||
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import TextIconButton from './text_icon_button';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Motion from 'mastodon/features/ui/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import classNames from 'classnames';
|
||||
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||
|
|
@ -22,10 +20,8 @@ const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
|||
class LanguageDropdownMenu extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
style: PropTypes.object,
|
||||
value: PropTypes.string.isRequired,
|
||||
frequentlyUsedLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
placement: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
languages: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
|
||||
|
|
@ -37,7 +33,6 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
};
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
searchValue: '',
|
||||
};
|
||||
|
||||
|
|
@ -45,12 +40,11 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
this.setState({ mounted: true });
|
||||
|
||||
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
|
||||
// to wait for a frame before focusing
|
||||
|
|
@ -69,15 +63,15 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
setListRef = c => {
|
||||
this.listNode = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleSearchChange = ({ target }) => {
|
||||
this.setState({ searchValue: target.value });
|
||||
}
|
||||
};
|
||||
|
||||
search () {
|
||||
const { languages, value, frequentlyUsedLanguages } = this.props;
|
||||
|
|
@ -128,7 +122,7 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
|
||||
this.props.onClose();
|
||||
this.props.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
const { onClose } = this.props;
|
||||
|
|
@ -169,7 +163,7 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleSearchKeyDown = e => {
|
||||
const { onChange, onClose } = this.props;
|
||||
|
|
@ -205,11 +199,11 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClear = () => {
|
||||
this.setState({ searchValue: '' });
|
||||
}
|
||||
};
|
||||
|
||||
renderItem = lang => {
|
||||
const { value } = this.props;
|
||||
|
|
@ -219,32 +213,25 @@ class LanguageDropdownMenu extends React.PureComponent {
|
|||
<span className='language-dropdown__dropdown__results__item__native-name' lang={lang[0]}>{lang[2]}</span> <span className='language-dropdown__dropdown__results__item__common-name'>({lang[1]})</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { style, placement, intl } = this.props;
|
||||
const { mounted, searchValue } = this.state;
|
||||
const { intl } = this.props;
|
||||
const { searchValue } = this.state;
|
||||
const isSearching = searchValue !== '';
|
||||
const results = this.search();
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className={`language-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
||||
<div className='emoji-mart-search'>
|
||||
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
|
||||
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
|
||||
</div>
|
||||
<div ref={this.setRef}>
|
||||
<div className='emoji-mart-search'>
|
||||
<input type='search' value={searchValue} onChange={this.handleSearchChange} onKeyDown={this.handleSearchKeyDown} placeholder={intl.formatMessage(messages.search)} />
|
||||
<button type='button' className='emoji-mart-search-icon' disabled={!isSearching} aria-label={intl.formatMessage(messages.clear)} onClick={this.handleClear}>{!isSearching ? loupeIcon : deleteIcon}</button>
|
||||
</div>
|
||||
|
||||
<div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
|
||||
{results.map(this.renderItem)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
<div className='language-dropdown__dropdown__results emoji-mart-scroll' role='listbox' ref={this.setListRef}>
|
||||
{results.map(this.renderItem)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -266,16 +253,13 @@ class LanguageDropdown extends React.PureComponent {
|
|||
placement: 'bottom',
|
||||
};
|
||||
|
||||
handleToggle = ({ target }) => {
|
||||
const { top } = target.getBoundingClientRect();
|
||||
|
||||
handleToggle = () => {
|
||||
if (this.state.open && this.activeElement) {
|
||||
this.activeElement.focus({ preventScroll: true });
|
||||
}
|
||||
|
||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
|
||||
this.setState({ open: !this.state.open });
|
||||
}
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
const { value, onClose } = this.props;
|
||||
|
|
@ -286,20 +270,32 @@ class LanguageDropdown extends React.PureComponent {
|
|||
|
||||
this.setState({ open: false });
|
||||
onClose(value);
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = value => {
|
||||
const { onChange } = this.props;
|
||||
onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
setTargetRef = c => {
|
||||
this.target = c;
|
||||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.target;
|
||||
};
|
||||
|
||||
handleOverlayEnter = (state) => {
|
||||
this.setState({ placement: state.placement });
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, intl, frequentlyUsedLanguages } = this.props;
|
||||
const { open, placement } = this.state;
|
||||
|
||||
return (
|
||||
<div className={classNames('privacy-dropdown', { active: open })}>
|
||||
<div className='privacy-dropdown__value'>
|
||||
<div className={classNames('privacy-dropdown', placement, { active: open })}>
|
||||
<div className='privacy-dropdown__value' ref={this.setTargetRef} >
|
||||
<TextIconButton
|
||||
className='privacy-dropdown__value-icon'
|
||||
label={value && value.toUpperCase()}
|
||||
|
|
@ -309,15 +305,20 @@ class LanguageDropdown extends React.PureComponent {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<Overlay show={open} placement={placement} target={this}>
|
||||
<LanguageDropdownMenu
|
||||
value={value}
|
||||
frequentlyUsedLanguages={frequentlyUsedLanguages}
|
||||
onClose={this.handleClose}
|
||||
onChange={this.handleChange}
|
||||
placement={placement}
|
||||
intl={intl}
|
||||
/>
|
||||
<Overlay show={open} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
{({ props, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation language-dropdown__dropdown ${placement}`} >
|
||||
<LanguageDropdownMenu
|
||||
value={value}
|
||||
frequentlyUsedLanguages={frequentlyUsedLanguages}
|
||||
onClose={this.handleClose}
|
||||
onChange={this.handleChange}
|
||||
intl={intl}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class PollButton extends React.PureComponent {
|
|||
|
||||
handleClick = () => {
|
||||
this.props.onClick();
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, active, unavailable, disabled } = this.props;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Option extends React.PureComponent {
|
|||
|
||||
static propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
lang: PropTypes.string,
|
||||
index: PropTypes.number.isRequired,
|
||||
isPollMultiple: PropTypes.bool,
|
||||
autoFocus: PropTypes.bool,
|
||||
|
|
@ -57,22 +58,22 @@ class Option extends React.PureComponent {
|
|||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this.handleToggleMultiple(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsClearRequested = () => {
|
||||
this.props.onClearSuggestions();
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionsFetchRequested = (token) => {
|
||||
this.props.onFetchSuggestions(token);
|
||||
}
|
||||
};
|
||||
|
||||
onSuggestionSelected = (tokenStart, token, value) => {
|
||||
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { isPollMultiple, title, index, autoFocus, intl } = this.props;
|
||||
const { isPollMultiple, title, lang, index, autoFocus, intl } = this.props;
|
||||
|
||||
return (
|
||||
<li>
|
||||
|
|
@ -91,6 +92,8 @@ class Option extends React.PureComponent {
|
|||
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
|
||||
maxLength={50}
|
||||
value={title}
|
||||
lang={lang}
|
||||
spellCheck
|
||||
onChange={this.handleOptionTitleChange}
|
||||
suggestions={this.props.suggestions}
|
||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||
|
|
@ -116,6 +119,7 @@ class PollForm extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
options: ImmutablePropTypes.list,
|
||||
lang: PropTypes.string,
|
||||
expiresIn: PropTypes.number,
|
||||
isMultiple: PropTypes.bool,
|
||||
onChangeOption: PropTypes.func.isRequired,
|
||||
|
|
@ -142,7 +146,7 @@ class PollForm extends ImmutablePureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
|
||||
const { options, lang, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
|
||||
|
||||
if (!options) {
|
||||
return null;
|
||||
|
|
@ -153,7 +157,7 @@ class PollForm extends ImmutablePureComponent {
|
|||
return (
|
||||
<div className='compose-form__poll-wrapper'>
|
||||
<ul>
|
||||
{options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
|
||||
{options.map((title, i) => <Option title={title} lang={lang} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
|
||||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
|
|
@ -165,6 +169,7 @@ class PollForm extends ImmutablePureComponent {
|
|||
<option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
|
||||
<option value={3600}>{intl.formatMessage(messages.hours, { number: 1 })}</option>
|
||||
<option value={21600}>{intl.formatMessage(messages.hours, { number: 6 })}</option>
|
||||
<option value={43200}>{intl.formatMessage(messages.hours, { number: 12 })}</option>
|
||||
<option value={86400}>{intl.formatMessage(messages.days, { number: 1 })}</option>
|
||||
<option value={259200}>{intl.formatMessage(messages.days, { number: 3 })}</option>
|
||||
<option value={604800}>{intl.formatMessage(messages.days, { number: 7 })}</option>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import IconButton from '../../../components/icon_button';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Motion from '../../ui/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import classNames from 'classnames';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
|
@ -29,20 +27,15 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
|||
style: PropTypes.object,
|
||||
items: PropTypes.array.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
placement: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
mounted: false,
|
||||
};
|
||||
|
||||
handleDocumentClick = e => {
|
||||
if (this.node && !this.node.contains(e.target)) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
const { items } = this.props;
|
||||
|
|
@ -86,7 +79,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = e => {
|
||||
const value = e.currentTarget.getAttribute('data-index');
|
||||
|
|
@ -95,13 +88,12 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
|||
|
||||
this.props.onClose();
|
||||
this.props.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, false);
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
|
||||
this.setState({ mounted: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
|
|
@ -111,38 +103,30 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
setFocusRef = c => {
|
||||
this.focusedItem = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { mounted } = this.state;
|
||||
const { style, items, placement, value } = this.props;
|
||||
const { style, items, value } = this.props;
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
// It should not be transformed when mounting because the resulting
|
||||
// size will be used to determine the coordinate of the menu by
|
||||
// react-overlays
|
||||
<div className={`privacy-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} role='listbox' ref={this.setRef}>
|
||||
{items.map(item => (
|
||||
<div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
|
||||
<div className='privacy-dropdown__option__icon'>
|
||||
<Icon id={item.icon} fixedWidth />
|
||||
</div>
|
||||
<div style={{ ...style }} role='listbox' ref={this.setRef}>
|
||||
{items.map(item => (
|
||||
<div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
|
||||
<div className='privacy-dropdown__option__icon'>
|
||||
<Icon id={item.icon} fixedWidth />
|
||||
</div>
|
||||
|
||||
<div className='privacy-dropdown__option__content'>
|
||||
<strong>{item.text}</strong>
|
||||
{item.meta}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className='privacy-dropdown__option__content'>
|
||||
<strong>{item.text}</strong>
|
||||
{item.meta}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +152,7 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
placement: 'bottom',
|
||||
};
|
||||
|
||||
handleToggle = ({ target }) => {
|
||||
handleToggle = () => {
|
||||
if (this.props.isUserTouching && this.props.isUserTouching()) {
|
||||
if (this.state.open) {
|
||||
this.props.onModalClose();
|
||||
|
|
@ -179,14 +163,12 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
const { top } = target.getBoundingClientRect();
|
||||
if (this.state.open && this.activeElement) {
|
||||
this.activeElement.focus({ preventScroll: true });
|
||||
}
|
||||
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
|
||||
this.setState({ open: !this.state.open });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleModalActionClick = (e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -195,7 +177,7 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
|
||||
this.props.onModalClose();
|
||||
this.props.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
switch(e.key) {
|
||||
|
|
@ -203,13 +185,13 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
this.handleClose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown = () => {
|
||||
if (!this.state.open) {
|
||||
this.activeElement = document.activeElement;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleButtonKeyDown = (e) => {
|
||||
switch(e.key) {
|
||||
|
|
@ -218,18 +200,18 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
this.handleMouseDown();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
if (this.state.open && this.activeElement) {
|
||||
this.activeElement.focus({ preventScroll: true });
|
||||
}
|
||||
this.setState({ open: false });
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = value => {
|
||||
this.props.onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
const { intl: { formatMessage } } = this.props;
|
||||
|
|
@ -247,6 +229,18 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
setTargetRef = c => {
|
||||
this.target = c;
|
||||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.target;
|
||||
};
|
||||
|
||||
handleOverlayEnter = (state) => {
|
||||
this.setState({ placement: state.placement });
|
||||
};
|
||||
|
||||
render () {
|
||||
const { value, container, disabled, intl } = this.props;
|
||||
const { open, placement } = this.state;
|
||||
|
|
@ -255,7 +249,7 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}>
|
||||
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })}>
|
||||
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })} ref={this.setTargetRef}>
|
||||
<IconButton
|
||||
className='privacy-dropdown__value-icon'
|
||||
icon={valueOption.icon}
|
||||
|
|
@ -272,14 +266,19 @@ class PrivacyDropdown extends React.PureComponent {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<Overlay show={open} placement={placement} target={this} container={container}>
|
||||
<PrivacyDropdownMenu
|
||||
items={this.options}
|
||||
value={value}
|
||||
onClose={this.handleClose}
|
||||
onChange={this.handleChange}
|
||||
placement={placement}
|
||||
/>
|
||||
<Overlay show={open} placement={'bottom'} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}>
|
||||
{({ props, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}>
|
||||
<PrivacyDropdownMenu
|
||||
items={this.options}
|
||||
value={value}
|
||||
onClose={this.handleClose}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,14 +27,14 @@ class ReplyIndicator extends ImmutablePureComponent {
|
|||
|
||||
handleClick = () => {
|
||||
this.props.onCancel();
|
||||
}
|
||||
};
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { status, intl } = this.props;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import Overlay from 'react-overlays/lib/Overlay';
|
||||
import Motion from '../../ui/util/optional_motion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import { searchEnabled } from '../../../initial_state';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
|
||||
|
|
@ -14,31 +12,20 @@ const messages = defineMessages({
|
|||
|
||||
class SearchPopout extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
render () {
|
||||
const { style } = this.props;
|
||||
const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
|
||||
return (
|
||||
<div style={{ ...style, position: 'absolute', width: 285, zIndex: 2 }}>
|
||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
<h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
|
||||
<div className='search-popout'>
|
||||
<h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
|
||||
|
||||
<ul>
|
||||
<li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
|
||||
<li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
||||
<li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
||||
<li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
|
||||
<li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
||||
<li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
|
||||
<li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
|
||||
</ul>
|
||||
|
||||
{extraInformation}
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
{extraInformation}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -71,11 +58,11 @@ class Search extends React.PureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.searchForm = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
this.props.onChange(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
handleClear = (e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -83,7 +70,7 @@ class Search extends React.PureComponent {
|
|||
if (this.props.value.length > 0 || this.props.submitted) {
|
||||
this.props.onClear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyUp = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
|
|
@ -97,7 +84,7 @@ class Search extends React.PureComponent {
|
|||
} else if (e.key === 'Escape') {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleFocus = () => {
|
||||
this.setState({ expanded: true });
|
||||
|
|
@ -109,11 +96,15 @@ class Search extends React.PureComponent {
|
|||
this.searchForm.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleBlur = () => {
|
||||
this.setState({ expanded: false });
|
||||
}
|
||||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.searchForm;
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, value, submitted } = this.props;
|
||||
|
|
@ -123,28 +114,31 @@ class Search extends React.PureComponent {
|
|||
|
||||
return (
|
||||
<div className='search'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{intl.formatMessage(messages.placeholder)}</span>
|
||||
<input
|
||||
ref={this.setRef}
|
||||
className='search__input'
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
ref={this.setRef}
|
||||
className='search__input'
|
||||
type='text'
|
||||
placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
|
||||
aria-label={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
onKeyUp={this.handleKeyUp}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
/>
|
||||
|
||||
<div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
|
||||
<Icon id='search' className={hasValue ? '' : 'active'} />
|
||||
<Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
|
||||
</div>
|
||||
|
||||
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
|
||||
<SearchPopout />
|
||||
<Overlay show={expanded && !hasValue} placement='bottom' target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||
{({ props, placement }) => (
|
||||
<div {...props} style={{ ...props.style, width: 285, zIndex: 2 }}>
|
||||
<div className={`dropdown-animation ${placement}`}>
|
||||
<SearchPopout />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Overlay>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,15 +22,20 @@ export default class Upload extends ImmutablePureComponent {
|
|||
handleUndoClick = e => {
|
||||
e.stopPropagation();
|
||||
this.props.onUndo(this.props.media.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
handleFocalPointClick = e => {
|
||||
e.stopPropagation();
|
||||
this.props.onOpenFocalPoint(this.props.media.get('id'));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { media } = this.props;
|
||||
|
||||
if (!media) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const focusX = media.getIn(['meta', 'focus', 'x']);
|
||||
const focusY = media.getIn(['meta', 'focus', 'y']);
|
||||
const x = ((focusX / 2) + .5) * 100;
|
||||
|
|
@ -43,10 +48,10 @@ export default class Upload extends ImmutablePureComponent {
|
|||
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
|
||||
<div className='compose-form__upload__actions'>
|
||||
<button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
|
||||
{!!media.get('unattached') && (<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>)}
|
||||
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
|
||||
</div>
|
||||
|
||||
{(media.get('description') || '').length === 0 && !!media.get('unattached') && (
|
||||
{(media.get('description') || '').length === 0 && (
|
||||
<div className='compose-form__upload__warning'>
|
||||
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -41,15 +41,15 @@ class UploadButton extends ImmutablePureComponent {
|
|||
if (e.target.files.length > 0) {
|
||||
this.props.onSelectFile(e.target.files);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
this.fileElement.click();
|
||||
}
|
||||
};
|
||||
|
||||
setRef = (c) => {
|
||||
this.fileElement = c;
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { intl, resetFileKey, unavailable, disabled, acceptContentTypes } = this.props;
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ const mapStateToProps = state => ({
|
|||
isEditing: state.getIn(['compose', 'id']) !== null,
|
||||
isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
|
||||
isUploading: state.getIn(['compose', 'is_uploading']),
|
||||
showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
|
||||
anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
|
||||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
const mapStateToProps = state => ({
|
||||
suggestions: state.getIn(['compose', 'suggestions']),
|
||||
options: state.getIn(['compose', 'poll', 'options']),
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
expiresIn: state.getIn(['compose', 'poll', 'expires_in']),
|
||||
isMultiple: state.getIn(['compose', 'poll', 'multiple']),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import Icon from 'mastodon/components/icon';
|
|||
import { logOut } from 'mastodon/utils/log_out';
|
||||
import Column from 'mastodon/components/column';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { isMobile } from '../../is_mobile';
|
||||
|
||||
const messages = defineMessages({
|
||||
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||
|
|
@ -73,15 +74,15 @@ class Compose extends React.PureComponent {
|
|||
}));
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
onFocus = () => {
|
||||
this.props.dispatch(changeComposing(true));
|
||||
}
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
this.props.dispatch(changeComposing(false));
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { multiColumn, showSearch, intl } = this.props;
|
||||
|
|
@ -115,7 +116,7 @@ class Compose extends React.PureComponent {
|
|||
<div className='drawer__inner' onFocus={this.onFocus}>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
|
||||
<ComposeFormContainer />
|
||||
<ComposeFormContainer autoFocus={!isMobile(window.innerWidth)} />
|
||||
|
||||
<div className='drawer__inner__mastodon'>
|
||||
<img alt='' draggable='false' src={mascot || elephantUIPlane} />
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ export function countableText(inputText) {
|
|||
return inputText
|
||||
.replace(urlRegex, urlPlaceholder)
|
||||
.replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3');
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-original');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseLeave = ({ currentTarget }) => {
|
||||
if (autoPlayGif) {
|
||||
|
|
@ -68,7 +68,7 @@ class Conversation extends ImmutablePureComponent {
|
|||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-static');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (!this.context.router) {
|
||||
|
|
@ -82,35 +82,35 @@ class Conversation extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
this.context.router.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
|
||||
}
|
||||
};
|
||||
|
||||
handleMarkAsRead = () => {
|
||||
this.props.markRead();
|
||||
}
|
||||
};
|
||||
|
||||
handleReply = () => {
|
||||
this.props.reply(this.props.lastStatus, this.context.router.history);
|
||||
}
|
||||
};
|
||||
|
||||
handleDelete = () => {
|
||||
this.props.delete();
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = () => {
|
||||
this.props.onMoveUp(this.props.conversationId);
|
||||
}
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = () => {
|
||||
this.props.onMoveDown(this.props.conversationId);
|
||||
}
|
||||
};
|
||||
|
||||
handleConversationMute = () => {
|
||||
this.props.onMute(this.props.lastStatus);
|
||||
}
|
||||
};
|
||||
|
||||
handleShowMore = () => {
|
||||
this.props.onToggleHidden(this.props.lastStatus);
|
||||
}
|
||||
};
|
||||
|
||||
render () {
|
||||
const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ export default class ConversationsList extends ImmutablePureComponent {
|
|||
onLoadMore: PropTypes.func,
|
||||
};
|
||||
|
||||
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)
|
||||
getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id);
|
||||
|
||||
handleMoveUp = id => {
|
||||
const elementIndex = this.getCurrentIndex(id) - 1;
|
||||
this._selectChild(elementIndex, true);
|
||||
}
|
||||
};
|
||||
|
||||
handleMoveDown = id => {
|
||||
const elementIndex = this.getCurrentIndex(id) + 1;
|
||||
this._selectChild(elementIndex, false);
|
||||
}
|
||||
};
|
||||
|
||||
_selectChild (index, align_top) {
|
||||
const container = this.node.node;
|
||||
|
|
@ -44,7 +44,7 @@ export default class ConversationsList extends ImmutablePureComponent {
|
|||
|
||||
setRef = c => {
|
||||
this.node = c;
|
||||
}
|
||||
};
|
||||
|
||||
handleLoadOlder = debounce(() => {
|
||||
const last = this.props.conversations.last();
|
||||
|
|
@ -52,7 +52,7 @@ export default class ConversationsList extends ImmutablePureComponent {
|
|||
if (last && last.get('last_status')) {
|
||||
this.props.onLoadMore(last.get('last_status'));
|
||||
}
|
||||
}, 300, { leading: true })
|
||||
}, 300, { leading: true });
|
||||
|
||||
render () {
|
||||
const { conversations, onLoadMore, ...other } = this.props;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue