Gab Social. All are welcome.
This commit is contained in:
833
app/javascript/gabsocial/actions/accounts.js
Normal file
833
app/javascript/gabsocial/actions/accounts.js
Normal file
@@ -0,0 +1,833 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import openDB from '../storage/db';
|
||||
import {
|
||||
importAccount,
|
||||
importFetchedAccount,
|
||||
importFetchedAccounts,
|
||||
importErrorWhileFetchingAccountByUsername,
|
||||
} from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
|
||||
export const ACCOUNT_FETCH_SUCCESS = 'ACCOUNT_FETCH_SUCCESS';
|
||||
export const ACCOUNT_FETCH_FAIL = 'ACCOUNT_FETCH_FAIL';
|
||||
|
||||
export const ACCOUNT_FOLLOW_REQUEST = 'ACCOUNT_FOLLOW_REQUEST';
|
||||
export const ACCOUNT_FOLLOW_SUCCESS = 'ACCOUNT_FOLLOW_SUCCESS';
|
||||
export const ACCOUNT_FOLLOW_FAIL = 'ACCOUNT_FOLLOW_FAIL';
|
||||
|
||||
export const ACCOUNT_UNFOLLOW_REQUEST = 'ACCOUNT_UNFOLLOW_REQUEST';
|
||||
export const ACCOUNT_UNFOLLOW_SUCCESS = 'ACCOUNT_UNFOLLOW_SUCCESS';
|
||||
export const ACCOUNT_UNFOLLOW_FAIL = 'ACCOUNT_UNFOLLOW_FAIL';
|
||||
|
||||
export const ACCOUNT_BLOCK_REQUEST = 'ACCOUNT_BLOCK_REQUEST';
|
||||
export const ACCOUNT_BLOCK_SUCCESS = 'ACCOUNT_BLOCK_SUCCESS';
|
||||
export const ACCOUNT_BLOCK_FAIL = 'ACCOUNT_BLOCK_FAIL';
|
||||
|
||||
export const ACCOUNT_UNBLOCK_REQUEST = 'ACCOUNT_UNBLOCK_REQUEST';
|
||||
export const ACCOUNT_UNBLOCK_SUCCESS = 'ACCOUNT_UNBLOCK_SUCCESS';
|
||||
export const ACCOUNT_UNBLOCK_FAIL = 'ACCOUNT_UNBLOCK_FAIL';
|
||||
|
||||
export const ACCOUNT_MUTE_REQUEST = 'ACCOUNT_MUTE_REQUEST';
|
||||
export const ACCOUNT_MUTE_SUCCESS = 'ACCOUNT_MUTE_SUCCESS';
|
||||
export const ACCOUNT_MUTE_FAIL = 'ACCOUNT_MUTE_FAIL';
|
||||
|
||||
export const ACCOUNT_UNMUTE_REQUEST = 'ACCOUNT_UNMUTE_REQUEST';
|
||||
export const ACCOUNT_UNMUTE_SUCCESS = 'ACCOUNT_UNMUTE_SUCCESS';
|
||||
export const ACCOUNT_UNMUTE_FAIL = 'ACCOUNT_UNMUTE_FAIL';
|
||||
|
||||
export const ACCOUNT_PIN_REQUEST = 'ACCOUNT_PIN_REQUEST';
|
||||
export const ACCOUNT_PIN_SUCCESS = 'ACCOUNT_PIN_SUCCESS';
|
||||
export const ACCOUNT_PIN_FAIL = 'ACCOUNT_PIN_FAIL';
|
||||
|
||||
export const ACCOUNT_UNPIN_REQUEST = 'ACCOUNT_UNPIN_REQUEST';
|
||||
export const ACCOUNT_UNPIN_SUCCESS = 'ACCOUNT_UNPIN_SUCCESS';
|
||||
export const ACCOUNT_UNPIN_FAIL = 'ACCOUNT_UNPIN_FAIL';
|
||||
|
||||
export const FOLLOWERS_FETCH_REQUEST = 'FOLLOWERS_FETCH_REQUEST';
|
||||
export const FOLLOWERS_FETCH_SUCCESS = 'FOLLOWERS_FETCH_SUCCESS';
|
||||
export const FOLLOWERS_FETCH_FAIL = 'FOLLOWERS_FETCH_FAIL';
|
||||
|
||||
export const FOLLOWERS_EXPAND_REQUEST = 'FOLLOWERS_EXPAND_REQUEST';
|
||||
export const FOLLOWERS_EXPAND_SUCCESS = 'FOLLOWERS_EXPAND_SUCCESS';
|
||||
export const FOLLOWERS_EXPAND_FAIL = 'FOLLOWERS_EXPAND_FAIL';
|
||||
|
||||
export const FOLLOWING_FETCH_REQUEST = 'FOLLOWING_FETCH_REQUEST';
|
||||
export const FOLLOWING_FETCH_SUCCESS = 'FOLLOWING_FETCH_SUCCESS';
|
||||
export const FOLLOWING_FETCH_FAIL = 'FOLLOWING_FETCH_FAIL';
|
||||
|
||||
export const FOLLOWING_EXPAND_REQUEST = 'FOLLOWING_EXPAND_REQUEST';
|
||||
export const FOLLOWING_EXPAND_SUCCESS = 'FOLLOWING_EXPAND_SUCCESS';
|
||||
export const FOLLOWING_EXPAND_FAIL = 'FOLLOWING_EXPAND_FAIL';
|
||||
|
||||
export const RELATIONSHIPS_FETCH_REQUEST = 'RELATIONSHIPS_FETCH_REQUEST';
|
||||
export const RELATIONSHIPS_FETCH_SUCCESS = 'RELATIONSHIPS_FETCH_SUCCESS';
|
||||
export const RELATIONSHIPS_FETCH_FAIL = 'RELATIONSHIPS_FETCH_FAIL';
|
||||
|
||||
export const FOLLOW_REQUESTS_FETCH_REQUEST = 'FOLLOW_REQUESTS_FETCH_REQUEST';
|
||||
export const FOLLOW_REQUESTS_FETCH_SUCCESS = 'FOLLOW_REQUESTS_FETCH_SUCCESS';
|
||||
export const FOLLOW_REQUESTS_FETCH_FAIL = 'FOLLOW_REQUESTS_FETCH_FAIL';
|
||||
|
||||
export const FOLLOW_REQUESTS_EXPAND_REQUEST = 'FOLLOW_REQUESTS_EXPAND_REQUEST';
|
||||
export const FOLLOW_REQUESTS_EXPAND_SUCCESS = 'FOLLOW_REQUESTS_EXPAND_SUCCESS';
|
||||
export const FOLLOW_REQUESTS_EXPAND_FAIL = 'FOLLOW_REQUESTS_EXPAND_FAIL';
|
||||
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_REQUEST = 'FOLLOW_REQUEST_AUTHORIZE_REQUEST';
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_SUCCESS = 'FOLLOW_REQUEST_AUTHORIZE_SUCCESS';
|
||||
export const FOLLOW_REQUEST_AUTHORIZE_FAIL = 'FOLLOW_REQUEST_AUTHORIZE_FAIL';
|
||||
|
||||
export const FOLLOW_REQUEST_REJECT_REQUEST = 'FOLLOW_REQUEST_REJECT_REQUEST';
|
||||
export const FOLLOW_REQUEST_REJECT_SUCCESS = 'FOLLOW_REQUEST_REJECT_SUCCESS';
|
||||
export const FOLLOW_REQUEST_REJECT_FAIL = 'FOLLOW_REQUEST_REJECT_FAIL';
|
||||
|
||||
function getFromDB(dispatch, getState, index, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = index.get(id);
|
||||
|
||||
request.onerror = reject;
|
||||
|
||||
request.onsuccess = () => {
|
||||
if (!request.result) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(importAccount(request.result));
|
||||
resolve(request.result.moved && getFromDB(dispatch, getState, index, request.result.moved));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchRelationships([id]));
|
||||
|
||||
if (getState().getIn(['accounts', id], null) !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchAccountRequest(id));
|
||||
|
||||
openDB().then(db => getFromDB(
|
||||
dispatch,
|
||||
getState,
|
||||
db.transaction('accounts', 'read').objectStore('accounts').index('id'),
|
||||
id
|
||||
).then(() => db.close(), error => {
|
||||
db.close();
|
||||
throw error;
|
||||
})).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then(response => {
|
||||
dispatch(importFetchedAccount(response.data));
|
||||
})).then(() => {
|
||||
dispatch(fetchAccountSuccess());
|
||||
}).catch(error => {
|
||||
dispatch(fetchAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchAccountByUsername(username) {
|
||||
return (dispatch, getState) => {
|
||||
api(getState).get(`/api/v1/account_by_username/${username}`).then(response => {
|
||||
dispatch(importFetchedAccount(response.data));
|
||||
}).then(() => {
|
||||
dispatch(fetchAccountSuccess());
|
||||
}).catch(error => {
|
||||
dispatch(fetchAccountFail(null, error));
|
||||
dispatch(importErrorWhileFetchingAccountByUsername(username));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchAccountSuccess() {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_SUCCESS,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchAccountFail(id, error) {
|
||||
return {
|
||||
type: ACCOUNT_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function followAccount(id, reblogs = true) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const alreadyFollowing = getState().getIn(['relationships', id, 'following']);
|
||||
const locked = getState().getIn(['accounts', id, 'locked'], false);
|
||||
|
||||
dispatch(followAccountRequest(id, locked));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/follow`, { reblogs }).then(response => {
|
||||
dispatch(followAccountSuccess(response.data, alreadyFollowing));
|
||||
}).catch(error => {
|
||||
dispatch(followAccountFail(error, locked));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unfollowAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unfollowAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => {
|
||||
dispatch(unfollowAccountSuccess(response.data, getState().get('statuses')));
|
||||
}).catch(error => {
|
||||
dispatch(unfollowAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function followAccountRequest(id, locked) {
|
||||
return {
|
||||
type: ACCOUNT_FOLLOW_REQUEST,
|
||||
id,
|
||||
locked,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function followAccountSuccess(relationship, alreadyFollowing) {
|
||||
return {
|
||||
type: ACCOUNT_FOLLOW_SUCCESS,
|
||||
relationship,
|
||||
alreadyFollowing,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function followAccountFail(error, locked) {
|
||||
return {
|
||||
type: ACCOUNT_FOLLOW_FAIL,
|
||||
error,
|
||||
locked,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unfollowAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_UNFOLLOW_REQUEST,
|
||||
id,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unfollowAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
type: ACCOUNT_UNFOLLOW_SUCCESS,
|
||||
relationship,
|
||||
statuses,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unfollowAccountFail(error) {
|
||||
return {
|
||||
type: ACCOUNT_UNFOLLOW_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function blockAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(blockAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/block`).then(response => {
|
||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||
dispatch(blockAccountSuccess(response.data, getState().get('statuses')));
|
||||
}).catch(error => {
|
||||
dispatch(blockAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unblockAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unblockAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => {
|
||||
dispatch(unblockAccountSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(unblockAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function blockAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_BLOCK_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function blockAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
type: ACCOUNT_BLOCK_SUCCESS,
|
||||
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) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(muteAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/mute`, { notifications }).then(response => {
|
||||
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
|
||||
dispatch(muteAccountSuccess(response.data, getState().get('statuses')));
|
||||
}).catch(error => {
|
||||
dispatch(muteAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unmuteAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unmuteAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/unmute`).then(response => {
|
||||
dispatch(unmuteAccountSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(unmuteAccountFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function muteAccountRequest(id) {
|
||||
return {
|
||||
type: ACCOUNT_MUTE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function muteAccountSuccess(relationship, statuses) {
|
||||
return {
|
||||
type: ACCOUNT_MUTE_SUCCESS,
|
||||
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) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchFollowersRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${id}/followers`).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchFollowersSuccess(id, response.data, next ? next.uri : null));
|
||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFollowersFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowersRequest(id) {
|
||||
return {
|
||||
type: FOLLOWERS_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowersSuccess(id, accounts, next) {
|
||||
return {
|
||||
type: FOLLOWERS_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowersFail(id, error) {
|
||||
return {
|
||||
type: FOLLOWERS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowers(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['user_lists', 'followers', id, 'next']);
|
||||
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandFollowersRequest(id));
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(expandFollowersSuccess(id, response.data, next ? next.uri : null));
|
||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(expandFollowersFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowersRequest(id) {
|
||||
return {
|
||||
type: FOLLOWERS_EXPAND_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowersSuccess(id, accounts, next) {
|
||||
return {
|
||||
type: FOLLOWERS_EXPAND_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowersFail(id, error) {
|
||||
return {
|
||||
type: FOLLOWERS_EXPAND_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowing(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchFollowingRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${id}/following`).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchFollowingSuccess(id, response.data, next ? next.uri : null));
|
||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFollowingFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowingRequest(id) {
|
||||
return {
|
||||
type: FOLLOWING_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowingSuccess(id, accounts, next) {
|
||||
return {
|
||||
type: FOLLOWING_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowingFail(id, error) {
|
||||
return {
|
||||
type: FOLLOWING_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowing(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['user_lists', 'following', id, 'next']);
|
||||
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandFollowingRequest(id));
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(expandFollowingSuccess(id, response.data, next ? next.uri : null));
|
||||
dispatch(fetchRelationships(response.data.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(expandFollowingFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowingRequest(id) {
|
||||
return {
|
||||
type: FOLLOWING_EXPAND_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowingSuccess(id, accounts, next) {
|
||||
return {
|
||||
type: FOLLOWING_EXPAND_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowingFail(id, error) {
|
||||
return {
|
||||
type: FOLLOWING_EXPAND_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchRelationships(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const loadedRelationships = getState().get('relationships');
|
||||
const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null);
|
||||
|
||||
if (newAccountIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchRelationshipsRequest(newAccountIds));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/relationships?${newAccountIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
|
||||
dispatch(fetchRelationshipsSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchRelationshipsFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchRelationshipsRequest(ids) {
|
||||
return {
|
||||
type: RELATIONSHIPS_FETCH_REQUEST,
|
||||
ids,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchRelationshipsSuccess(relationships) {
|
||||
return {
|
||||
type: RELATIONSHIPS_FETCH_SUCCESS,
|
||||
relationships,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchRelationshipsFail(error) {
|
||||
return {
|
||||
type: RELATIONSHIPS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowRequests() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchFollowRequestsRequest());
|
||||
|
||||
api(getState).get('/api/v1/follow_requests').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
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 {
|
||||
type: FOLLOW_REQUESTS_FETCH_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFollowRequestsFail(error) {
|
||||
return {
|
||||
type: FOLLOW_REQUESTS_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowRequests() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['user_lists', 'follow_requests', 'next']);
|
||||
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandFollowRequestsRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
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 {
|
||||
type: FOLLOW_REQUESTS_EXPAND_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFollowRequestsFail(error) {
|
||||
return {
|
||||
type: FOLLOW_REQUESTS_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function authorizeFollowRequest(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(authorizeFollowRequestRequest(id));
|
||||
|
||||
api(getState)
|
||||
.post(`/api/v1/follow_requests/${id}/authorize`)
|
||||
.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 {
|
||||
type: FOLLOW_REQUEST_AUTHORIZE_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export function rejectFollowRequest(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(rejectFollowRequestRequest(id));
|
||||
|
||||
api(getState)
|
||||
.post(`/api/v1/follow_requests/${id}/reject`)
|
||||
.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 {
|
||||
type: FOLLOW_REQUEST_REJECT_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function pinAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(pinAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/pin`).then(response => {
|
||||
dispatch(pinAccountSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(pinAccountFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unpinAccount(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unpinAccountRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/accounts/${id}/unpin`).then(response => {
|
||||
dispatch(unpinAccountSuccess(response.data));
|
||||
}).catch(error => {
|
||||
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,
|
||||
};
|
||||
};
|
||||
55
app/javascript/gabsocial/actions/alerts.js
Normal file
55
app/javascript/gabsocial/actions/alerts.js
Normal file
@@ -0,0 +1,55 @@
|
||||
//test
|
||||
import { defineMessages } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' },
|
||||
unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' },
|
||||
});
|
||||
|
||||
export const ALERT_SHOW = 'ALERT_SHOW';
|
||||
export const ALERT_DISMISS = 'ALERT_DISMISS';
|
||||
export const ALERT_CLEAR = 'ALERT_CLEAR';
|
||||
|
||||
export function dismissAlert(alert) {
|
||||
return {
|
||||
type: ALERT_DISMISS,
|
||||
alert,
|
||||
};
|
||||
};
|
||||
|
||||
export function clearAlert() {
|
||||
return {
|
||||
type: ALERT_CLEAR,
|
||||
};
|
||||
};
|
||||
|
||||
export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage) {
|
||||
return {
|
||||
type: ALERT_SHOW,
|
||||
title,
|
||||
message,
|
||||
};
|
||||
};
|
||||
|
||||
export function showAlertForError(error) {
|
||||
if (error.response) {
|
||||
const { data, status, statusText } = error.response;
|
||||
|
||||
if (status === 404 || status === 410) {
|
||||
// Skip these errors as they are reflected in the UI
|
||||
return {};
|
||||
}
|
||||
|
||||
let message = statusText;
|
||||
let title = `${status}`;
|
||||
|
||||
if (data.error) {
|
||||
message = data.error;
|
||||
}
|
||||
|
||||
return showAlert(title, message);
|
||||
} else {
|
||||
console.error(error);
|
||||
return showAlert();
|
||||
}
|
||||
}
|
||||
90
app/javascript/gabsocial/actions/blocks.js
Normal file
90
app/javascript/gabsocial/actions/blocks.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const BLOCKS_FETCH_REQUEST = 'BLOCKS_FETCH_REQUEST';
|
||||
export const BLOCKS_FETCH_SUCCESS = 'BLOCKS_FETCH_SUCCESS';
|
||||
export const BLOCKS_FETCH_FAIL = 'BLOCKS_FETCH_FAIL';
|
||||
|
||||
export const BLOCKS_EXPAND_REQUEST = 'BLOCKS_EXPAND_REQUEST';
|
||||
export const BLOCKS_EXPAND_SUCCESS = 'BLOCKS_EXPAND_SUCCESS';
|
||||
export const BLOCKS_EXPAND_FAIL = 'BLOCKS_EXPAND_FAIL';
|
||||
|
||||
export function fetchBlocks() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchBlocksRequest());
|
||||
|
||||
api(getState).get('/api/v1/blocks').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null));
|
||||
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 {
|
||||
type: BLOCKS_FETCH_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchBlocksFail(error) {
|
||||
return {
|
||||
type: BLOCKS_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandBlocks() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['user_lists', 'blocks', 'next']);
|
||||
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandBlocksRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(expandBlocksSuccess(response.data, next ? next.uri : null));
|
||||
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 {
|
||||
type: BLOCKS_EXPAND_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandBlocksFail(error) {
|
||||
return {
|
||||
type: BLOCKS_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
25
app/javascript/gabsocial/actions/bundles.js
Normal file
25
app/javascript/gabsocial/actions/bundles.js
Normal file
@@ -0,0 +1,25 @@
|
||||
export const BUNDLE_FETCH_REQUEST = 'BUNDLE_FETCH_REQUEST';
|
||||
export const BUNDLE_FETCH_SUCCESS = 'BUNDLE_FETCH_SUCCESS';
|
||||
export const BUNDLE_FETCH_FAIL = 'BUNDLE_FETCH_FAIL';
|
||||
|
||||
export function fetchBundleRequest(skipLoading) {
|
||||
return {
|
||||
type: BUNDLE_FETCH_REQUEST,
|
||||
skipLoading,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBundleSuccess(skipLoading) {
|
||||
return {
|
||||
type: BUNDLE_FETCH_SUCCESS,
|
||||
skipLoading,
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBundleFail(error, skipLoading) {
|
||||
return {
|
||||
type: BUNDLE_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading,
|
||||
};
|
||||
}
|
||||
16
app/javascript/gabsocial/actions/columns.js
Normal file
16
app/javascript/gabsocial/actions/columns.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { saveSettings } from './settings';
|
||||
|
||||
export const COLUMN_PARAMS_CHANGE = 'COLUMN_PARAMS_CHANGE';
|
||||
|
||||
export function changeColumnParams(uuid, path, value) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: COLUMN_PARAMS_CHANGE,
|
||||
uuid,
|
||||
path,
|
||||
value,
|
||||
});
|
||||
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
}
|
||||
560
app/javascript/gabsocial/actions/compose.js
Normal file
560
app/javascript/gabsocial/actions/compose.js
Normal file
@@ -0,0 +1,560 @@
|
||||
import api from '../api';
|
||||
import { CancelToken, isCancel } from 'axios';
|
||||
import { throttle } from 'lodash';
|
||||
import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light';
|
||||
import { tagHistory } from '../settings';
|
||||
import { useEmoji } from './emojis';
|
||||
import resizeImage from '../utils/resize_image';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { updateTimeline } from './timelines';
|
||||
import { showAlertForError } from './alerts';
|
||||
import { showAlert } from './alerts';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import { openModal, closeModal } from './modal';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
let cancelFetchComposeSuggestionsAccounts;
|
||||
|
||||
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
|
||||
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
|
||||
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
|
||||
export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
|
||||
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
||||
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
||||
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
||||
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
||||
export const COMPOSE_RESET = 'COMPOSE_RESET';
|
||||
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
||||
export const COMPOSE_UPLOAD_SUCCESS = 'COMPOSE_UPLOAD_SUCCESS';
|
||||
export const COMPOSE_UPLOAD_FAIL = 'COMPOSE_UPLOAD_FAIL';
|
||||
export const COMPOSE_UPLOAD_PROGRESS = 'COMPOSE_UPLOAD_PROGRESS';
|
||||
export const COMPOSE_UPLOAD_UNDO = 'COMPOSE_UPLOAD_UNDO';
|
||||
|
||||
export const COMPOSE_SUGGESTIONS_CLEAR = 'COMPOSE_SUGGESTIONS_CLEAR';
|
||||
export const COMPOSE_SUGGESTIONS_READY = 'COMPOSE_SUGGESTIONS_READY';
|
||||
export const COMPOSE_SUGGESTION_SELECT = 'COMPOSE_SUGGESTION_SELECT';
|
||||
export const COMPOSE_SUGGESTION_TAGS_UPDATE = 'COMPOSE_SUGGESTION_TAGS_UPDATE';
|
||||
|
||||
export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
|
||||
|
||||
export const COMPOSE_MOUNT = 'COMPOSE_MOUNT';
|
||||
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
|
||||
|
||||
export const COMPOSE_SENSITIVITY_CHANGE = 'COMPOSE_SENSITIVITY_CHANGE';
|
||||
export const COMPOSE_SPOILERNESS_CHANGE = 'COMPOSE_SPOILERNESS_CHANGE';
|
||||
export const COMPOSE_SPOILER_TEXT_CHANGE = 'COMPOSE_SPOILER_TEXT_CHANGE';
|
||||
export const COMPOSE_VISIBILITY_CHANGE = 'COMPOSE_VISIBILITY_CHANGE';
|
||||
export const COMPOSE_LISTABILITY_CHANGE = 'COMPOSE_LISTABILITY_CHANGE';
|
||||
export const COMPOSE_COMPOSING_CHANGE = 'COMPOSE_COMPOSING_CHANGE';
|
||||
|
||||
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
|
||||
|
||||
export const COMPOSE_UPLOAD_CHANGE_REQUEST = 'COMPOSE_UPLOAD_UPDATE_REQUEST';
|
||||
export const COMPOSE_UPLOAD_CHANGE_SUCCESS = 'COMPOSE_UPLOAD_UPDATE_SUCCESS';
|
||||
export const COMPOSE_UPLOAD_CHANGE_FAIL = 'COMPOSE_UPLOAD_UPDATE_FAIL';
|
||||
|
||||
export const COMPOSE_POLL_ADD = 'COMPOSE_POLL_ADD';
|
||||
export const COMPOSE_POLL_REMOVE = 'COMPOSE_POLL_REMOVE';
|
||||
export const COMPOSE_POLL_OPTION_ADD = 'COMPOSE_POLL_OPTION_ADD';
|
||||
export const COMPOSE_POLL_OPTION_CHANGE = 'COMPOSE_POLL_OPTION_CHANGE';
|
||||
export const COMPOSE_POLL_OPTION_REMOVE = 'COMPOSE_POLL_OPTION_REMOVE';
|
||||
export const COMPOSE_POLL_SETTINGS_CHANGE = 'COMPOSE_POLL_SETTINGS_CHANGE';
|
||||
|
||||
const messages = defineMessages({
|
||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
||||
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||
});
|
||||
|
||||
const COMPOSE_PANEL_BREAKPOINT = 600 + (285 * 1) + (10 * 1);
|
||||
|
||||
export const ensureComposeIsVisible = (getState, routerHistory) => {
|
||||
if (!getState().getIn(['compose', 'mounted']) && window.innerWidth < COMPOSE_PANEL_BREAKPOINT) {
|
||||
routerHistory.push('/posts/new');
|
||||
}
|
||||
};
|
||||
|
||||
export function changeCompose(text) {
|
||||
return {
|
||||
type: COMPOSE_CHANGE,
|
||||
text: text,
|
||||
};
|
||||
};
|
||||
|
||||
export function replyCompose(status, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_REPLY,
|
||||
status: status,
|
||||
});
|
||||
|
||||
dispatch(openModal('COMPOSE'));
|
||||
};
|
||||
};
|
||||
|
||||
export function cancelReplyCompose() {
|
||||
return {
|
||||
type: COMPOSE_REPLY_CANCEL,
|
||||
};
|
||||
};
|
||||
|
||||
export function resetCompose() {
|
||||
return {
|
||||
type: COMPOSE_RESET,
|
||||
};
|
||||
};
|
||||
|
||||
export function mentionCompose(account, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_MENTION,
|
||||
account: account,
|
||||
});
|
||||
|
||||
dispatch(openModal('COMPOSE'));
|
||||
};
|
||||
};
|
||||
|
||||
export function directCompose(account, routerHistory) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: COMPOSE_DIRECT,
|
||||
account: account,
|
||||
});
|
||||
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
};
|
||||
};
|
||||
|
||||
export function submitCompose(routerHistory) {
|
||||
return function (dispatch, getState) {
|
||||
if (!me) return;
|
||||
|
||||
const status = getState().getIn(['compose', 'text'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
|
||||
if ((!status || !status.length) && media.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(submitComposeRequest());
|
||||
dispatch(closeModal());
|
||||
|
||||
api(getState).post('/api/v1/statuses', {
|
||||
status,
|
||||
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
|
||||
media_ids: media.map(item => item.get('id')),
|
||||
sensitive: getState().getIn(['compose', 'sensitive']),
|
||||
spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
|
||||
visibility: getState().getIn(['compose', 'privacy']),
|
||||
poll: getState().getIn(['compose', 'poll'], null),
|
||||
}, {
|
||||
headers: {
|
||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||
},
|
||||
}).then(function (response) {
|
||||
if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
|
||||
routerHistory.push('/messages');
|
||||
} else if (routerHistory && routerHistory.location.pathname === '/posts/new' && window.history.state) {
|
||||
routerHistory.goBack();
|
||||
}
|
||||
|
||||
dispatch(insertIntoTagHistory(response.data.tags, status));
|
||||
dispatch(submitComposeSuccess({ ...response.data }));
|
||||
|
||||
// To make the app more responsive, immediately push the status
|
||||
// into the columns
|
||||
|
||||
const insertIfOnline = timelineId => {
|
||||
const timeline = getState().getIn(['timelines', timelineId]);
|
||||
|
||||
if (timeline && timeline.get('items').size > 0 && timeline.getIn(['items', 0]) !== null && timeline.get('online')) {
|
||||
dispatch(updateTimeline(timelineId, { ...response.data }));
|
||||
}
|
||||
};
|
||||
|
||||
if (response.data.visibility !== 'direct') {
|
||||
insertIfOnline('home');
|
||||
}
|
||||
|
||||
if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
|
||||
insertIfOnline('community');
|
||||
insertIfOnline('public');
|
||||
}
|
||||
}).catch(function (error) {
|
||||
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) {
|
||||
if (!me) return;
|
||||
|
||||
const uploadLimit = 4;
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const progress = new Array(files.length).fill(0);
|
||||
let total = Array.from(files).reduce((a, v) => a + v.size, 0);
|
||||
|
||||
if (files.length + media.size > uploadLimit) {
|
||||
dispatch(showAlert(undefined, messages.uploadErrorLimit));
|
||||
return;
|
||||
}
|
||||
|
||||
if (getState().getIn(['compose', 'poll'])) {
|
||||
dispatch(showAlert(undefined, messages.uploadErrorPoll));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(uploadComposeRequest());
|
||||
|
||||
for (const [i, f] of Array.from(files).entries()) {
|
||||
if (media.size + i > 3) break;
|
||||
|
||||
resizeImage(f).then(file => {
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
// Account for disparity in size of original image and resized data
|
||||
total += file.size - f.size;
|
||||
|
||||
return api(getState).post('/api/v1/media', data, {
|
||||
onUploadProgress: function({ loaded }){
|
||||
progress[i] = loaded;
|
||||
dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
|
||||
},
|
||||
}).then(({ data }) => dispatch(uploadComposeSuccess(data)));
|
||||
}).catch(error => dispatch(uploadComposeFail(error)));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export function changeUploadCompose(id, params) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(changeUploadComposeRequest());
|
||||
|
||||
api(getState).put(`/api/v1/media/${id}`, params).then(response => {
|
||||
dispatch(changeUploadComposeSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(changeUploadComposeFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function changeUploadComposeRequest() {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_CHANGE_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
export function changeUploadComposeSuccess(media) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
|
||||
media: media,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function changeUploadComposeFail(error) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_CHANGE_FAIL,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function uploadComposeRequest() {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function uploadComposeProgress(loaded, total) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_PROGRESS,
|
||||
loaded: loaded,
|
||||
total: total,
|
||||
};
|
||||
};
|
||||
|
||||
export function uploadComposeSuccess(media) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_SUCCESS,
|
||||
media: media,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function uploadComposeFail(error) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_FAIL,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function undoUploadCompose(media_id) {
|
||||
return {
|
||||
type: COMPOSE_UPLOAD_UNDO,
|
||||
media_id: media_id,
|
||||
};
|
||||
};
|
||||
|
||||
export function clearComposeSuggestions() {
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
cancelFetchComposeSuggestionsAccounts();
|
||||
}
|
||||
return {
|
||||
type: COMPOSE_SUGGESTIONS_CLEAR,
|
||||
};
|
||||
};
|
||||
|
||||
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
|
||||
if (cancelFetchComposeSuggestionsAccounts) {
|
||||
cancelFetchComposeSuggestionsAccounts();
|
||||
}
|
||||
api(getState).get('/api/v1/accounts/search', {
|
||||
cancelToken: new CancelToken(cancel => {
|
||||
cancelFetchComposeSuggestionsAccounts = cancel;
|
||||
}),
|
||||
params: {
|
||||
q: token.slice(1),
|
||||
resolve: false,
|
||||
limit: 4,
|
||||
},
|
||||
}).then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(readyComposeSuggestionsAccounts(token, response.data));
|
||||
}).catch(error => {
|
||||
if (!isCancel(error)) {
|
||||
dispatch(showAlertForError(error));
|
||||
}
|
||||
});
|
||||
}, 200, { leading: true, trailing: true });
|
||||
|
||||
const fetchComposeSuggestionsEmojis = (dispatch, getState, token) => {
|
||||
const results = emojiSearch(token.replace(':', ''), { maxResults: 5 });
|
||||
dispatch(readyComposeSuggestionsEmojis(token, results));
|
||||
};
|
||||
|
||||
const fetchComposeSuggestionsTags = (dispatch, getState, token) => {
|
||||
dispatch(updateSuggestionTags(token));
|
||||
};
|
||||
|
||||
export function fetchComposeSuggestions(token) {
|
||||
return (dispatch, getState) => {
|
||||
switch (token[0]) {
|
||||
case ':':
|
||||
fetchComposeSuggestionsEmojis(dispatch, getState, token);
|
||||
break;
|
||||
case '#':
|
||||
fetchComposeSuggestionsTags(dispatch, getState, token);
|
||||
break;
|
||||
default:
|
||||
fetchComposeSuggestionsAccounts(dispatch, getState, token);
|
||||
break;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function readyComposeSuggestionsEmojis(token, emojis) {
|
||||
return {
|
||||
type: COMPOSE_SUGGESTIONS_READY,
|
||||
token,
|
||||
emojis,
|
||||
};
|
||||
};
|
||||
|
||||
export function readyComposeSuggestionsAccounts(token, accounts) {
|
||||
return {
|
||||
type: COMPOSE_SUGGESTIONS_READY,
|
||||
token,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
|
||||
export function selectComposeSuggestion(position, token, suggestion, path) {
|
||||
return (dispatch, getState) => {
|
||||
let completion, startPosition;
|
||||
|
||||
if (typeof suggestion === 'object' && suggestion.id) {
|
||||
completion = suggestion.native || suggestion.colons;
|
||||
startPosition = position - 1;
|
||||
|
||||
dispatch(useEmoji(suggestion));
|
||||
} else if (suggestion[0] === '#') {
|
||||
completion = suggestion;
|
||||
startPosition = position - 1;
|
||||
} else {
|
||||
completion = getState().getIn(['accounts', suggestion, 'acct']);
|
||||
startPosition = position;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: COMPOSE_SUGGESTION_SELECT,
|
||||
position: startPosition,
|
||||
token,
|
||||
completion,
|
||||
path,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function updateSuggestionTags(token) {
|
||||
return {
|
||||
type: COMPOSE_SUGGESTION_TAGS_UPDATE,
|
||||
token,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateTagHistory(tags) {
|
||||
return {
|
||||
type: COMPOSE_TAG_HISTORY_UPDATE,
|
||||
tags,
|
||||
};
|
||||
}
|
||||
|
||||
export function hydrateCompose() {
|
||||
return (dispatch, getState) => {
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
const history = tagHistory.get(me);
|
||||
|
||||
if (history !== null) {
|
||||
dispatch(updateTagHistory(history));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function insertIntoTagHistory(recognizedTags, text) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const oldHistory = state.getIn(['compose', 'tagHistory']);
|
||||
const me = state.getIn(['meta', 'me']);
|
||||
const names = recognizedTags.map(tag => text.match(new RegExp(`#${tag.name}`, 'i'))[0].slice(1));
|
||||
const intersectedOldHistory = oldHistory.filter(name => names.findIndex(newName => newName.toLowerCase() === name.toLowerCase()) === -1);
|
||||
|
||||
names.push(...intersectedOldHistory.toJS());
|
||||
|
||||
const newHistory = names.slice(0, 1000);
|
||||
|
||||
tagHistory.set(me, newHistory);
|
||||
dispatch(updateTagHistory(newHistory));
|
||||
};
|
||||
}
|
||||
|
||||
export function mountCompose() {
|
||||
return {
|
||||
type: COMPOSE_MOUNT,
|
||||
};
|
||||
};
|
||||
|
||||
export function unmountCompose() {
|
||||
return {
|
||||
type: COMPOSE_UNMOUNT,
|
||||
};
|
||||
};
|
||||
|
||||
export function changeComposeSensitivity() {
|
||||
return {
|
||||
type: COMPOSE_SENSITIVITY_CHANGE,
|
||||
};
|
||||
};
|
||||
|
||||
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 {
|
||||
type: COMPOSE_EMOJI_INSERT,
|
||||
position,
|
||||
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 {
|
||||
type: COMPOSE_POLL_OPTION_CHANGE,
|
||||
index,
|
||||
title,
|
||||
};
|
||||
};
|
||||
|
||||
export function removePollOption(index) {
|
||||
return {
|
||||
type: COMPOSE_POLL_OPTION_REMOVE,
|
||||
index,
|
||||
};
|
||||
};
|
||||
|
||||
export function changePollSettings(expiresIn, isMultiple) {
|
||||
return {
|
||||
type: COMPOSE_POLL_SETTINGS_CHANGE,
|
||||
expiresIn,
|
||||
isMultiple,
|
||||
};
|
||||
};
|
||||
89
app/javascript/gabsocial/actions/conversations.js
Normal file
89
app/javascript/gabsocial/actions/conversations.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import {
|
||||
importFetchedAccounts,
|
||||
importFetchedStatuses,
|
||||
importFetchedStatus,
|
||||
} from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const CONVERSATIONS_MOUNT = 'CONVERSATIONS_MOUNT';
|
||||
export const CONVERSATIONS_UNMOUNT = 'CONVERSATIONS_UNMOUNT';
|
||||
|
||||
export const CONVERSATIONS_FETCH_REQUEST = 'CONVERSATIONS_FETCH_REQUEST';
|
||||
export const CONVERSATIONS_FETCH_SUCCESS = 'CONVERSATIONS_FETCH_SUCCESS';
|
||||
export const CONVERSATIONS_FETCH_FAIL = 'CONVERSATIONS_FETCH_FAIL';
|
||||
export const CONVERSATIONS_UPDATE = 'CONVERSATIONS_UPDATE';
|
||||
|
||||
export const CONVERSATIONS_READ = 'CONVERSATIONS_READ';
|
||||
|
||||
export const mountConversations = () => ({
|
||||
type: CONVERSATIONS_MOUNT,
|
||||
});
|
||||
|
||||
export const unmountConversations = () => ({
|
||||
type: CONVERSATIONS_UNMOUNT,
|
||||
});
|
||||
|
||||
export const markConversationRead = conversationId => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch({
|
||||
type: CONVERSATIONS_READ,
|
||||
id: conversationId,
|
||||
});
|
||||
|
||||
api(getState).post(`/api/v1/conversations/${conversationId}/read`);
|
||||
};
|
||||
|
||||
export const expandConversations = ({ maxId } = {}) => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(expandConversationsRequest());
|
||||
|
||||
const params = { max_id: maxId };
|
||||
|
||||
if (!maxId) {
|
||||
params.since_id = getState().getIn(['conversations', 'items', 0, 'last_status']);
|
||||
}
|
||||
|
||||
const isLoadingRecent = !!params.since_id;
|
||||
|
||||
api(getState).get('/api/v1/conversations', { params })
|
||||
.then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data.reduce((aggr, item) => aggr.concat(item.accounts), [])));
|
||||
dispatch(importFetchedStatuses(response.data.map(item => item.last_status).filter(x => !!x)));
|
||||
dispatch(expandConversationsSuccess(response.data, next ? next.uri : null, isLoadingRecent));
|
||||
})
|
||||
.catch(err => dispatch(expandConversationsFail(err)));
|
||||
};
|
||||
|
||||
export const expandConversationsRequest = () => ({
|
||||
type: CONVERSATIONS_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const expandConversationsSuccess = (conversations, next, isLoadingRecent) => ({
|
||||
type: CONVERSATIONS_FETCH_SUCCESS,
|
||||
conversations,
|
||||
next,
|
||||
isLoadingRecent,
|
||||
});
|
||||
|
||||
export const expandConversationsFail = error => ({
|
||||
type: CONVERSATIONS_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const updateConversations = conversation => dispatch => {
|
||||
dispatch(importFetchedAccounts(conversation.accounts));
|
||||
|
||||
if (conversation.last_status) {
|
||||
dispatch(importFetchedStatus(conversation.last_status));
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: CONVERSATIONS_UPDATE,
|
||||
conversation,
|
||||
});
|
||||
};
|
||||
40
app/javascript/gabsocial/actions/custom_emojis.js
Normal file
40
app/javascript/gabsocial/actions/custom_emojis.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import api from '../api';
|
||||
|
||||
export const CUSTOM_EMOJIS_FETCH_REQUEST = 'CUSTOM_EMOJIS_FETCH_REQUEST';
|
||||
export const CUSTOM_EMOJIS_FETCH_SUCCESS = 'CUSTOM_EMOJIS_FETCH_SUCCESS';
|
||||
export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL';
|
||||
|
||||
export function fetchCustomEmojis() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchCustomEmojisRequest());
|
||||
|
||||
api(getState).get('/api/v1/custom_emojis').then(response => {
|
||||
dispatch(fetchCustomEmojisSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchCustomEmojisFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchCustomEmojisRequest() {
|
||||
return {
|
||||
type: CUSTOM_EMOJIS_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchCustomEmojisSuccess(custom_emojis) {
|
||||
return {
|
||||
type: CUSTOM_EMOJIS_FETCH_SUCCESS,
|
||||
custom_emojis,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchCustomEmojisFail(error) {
|
||||
return {
|
||||
type: CUSTOM_EMOJIS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
174
app/javascript/gabsocial/actions/domain_blocks.js
Normal file
174
app/javascript/gabsocial/actions/domain_blocks.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST';
|
||||
export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS';
|
||||
export const DOMAIN_BLOCK_FAIL = 'DOMAIN_BLOCK_FAIL';
|
||||
|
||||
export const DOMAIN_UNBLOCK_REQUEST = 'DOMAIN_UNBLOCK_REQUEST';
|
||||
export const DOMAIN_UNBLOCK_SUCCESS = 'DOMAIN_UNBLOCK_SUCCESS';
|
||||
export const DOMAIN_UNBLOCK_FAIL = 'DOMAIN_UNBLOCK_FAIL';
|
||||
|
||||
export const DOMAIN_BLOCKS_FETCH_REQUEST = 'DOMAIN_BLOCKS_FETCH_REQUEST';
|
||||
export const DOMAIN_BLOCKS_FETCH_SUCCESS = 'DOMAIN_BLOCKS_FETCH_SUCCESS';
|
||||
export const DOMAIN_BLOCKS_FETCH_FAIL = 'DOMAIN_BLOCKS_FETCH_FAIL';
|
||||
|
||||
export const DOMAIN_BLOCKS_EXPAND_REQUEST = 'DOMAIN_BLOCKS_EXPAND_REQUEST';
|
||||
export const DOMAIN_BLOCKS_EXPAND_SUCCESS = 'DOMAIN_BLOCKS_EXPAND_SUCCESS';
|
||||
export const DOMAIN_BLOCKS_EXPAND_FAIL = 'DOMAIN_BLOCKS_EXPAND_FAIL';
|
||||
|
||||
export function blockDomain(domain) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(blockDomainRequest(domain));
|
||||
|
||||
api(getState).post('/api/v1/domain_blocks', { domain }).then(() => {
|
||||
const at_domain = '@' + domain;
|
||||
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
||||
dispatch(blockDomainSuccess(domain, accounts));
|
||||
}).catch(err => {
|
||||
dispatch(blockDomainFail(domain, err));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function blockDomainRequest(domain) {
|
||||
return {
|
||||
type: DOMAIN_BLOCK_REQUEST,
|
||||
domain,
|
||||
};
|
||||
};
|
||||
|
||||
export function blockDomainSuccess(domain, accounts) {
|
||||
return {
|
||||
type: DOMAIN_BLOCK_SUCCESS,
|
||||
domain,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
|
||||
export function blockDomainFail(domain, error) {
|
||||
return {
|
||||
type: DOMAIN_BLOCK_FAIL,
|
||||
domain,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function unblockDomain(domain) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unblockDomainRequest(domain));
|
||||
|
||||
api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(() => {
|
||||
const at_domain = '@' + domain;
|
||||
const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id'));
|
||||
dispatch(unblockDomainSuccess(domain, accounts));
|
||||
}).catch(err => {
|
||||
dispatch(unblockDomainFail(domain, err));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unblockDomainRequest(domain) {
|
||||
return {
|
||||
type: DOMAIN_UNBLOCK_REQUEST,
|
||||
domain,
|
||||
};
|
||||
};
|
||||
|
||||
export function unblockDomainSuccess(domain, accounts) {
|
||||
return {
|
||||
type: DOMAIN_UNBLOCK_SUCCESS,
|
||||
domain,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
|
||||
export function unblockDomainFail(domain, error) {
|
||||
return {
|
||||
type: DOMAIN_UNBLOCK_FAIL,
|
||||
domain,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchDomainBlocks() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchDomainBlocksRequest());
|
||||
|
||||
api(getState).get('/api/v1/domain_blocks').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(err => {
|
||||
dispatch(fetchDomainBlocksFail(err));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchDomainBlocksRequest() {
|
||||
return {
|
||||
type: DOMAIN_BLOCKS_FETCH_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchDomainBlocksSuccess(domains, next) {
|
||||
return {
|
||||
type: DOMAIN_BLOCKS_FETCH_SUCCESS,
|
||||
domains,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchDomainBlocksFail(error) {
|
||||
return {
|
||||
type: DOMAIN_BLOCKS_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandDomainBlocks() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['domain_lists', 'blocks', 'next']);
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandDomainBlocksRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(err => {
|
||||
dispatch(expandDomainBlocksFail(err));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandDomainBlocksRequest() {
|
||||
return {
|
||||
type: DOMAIN_BLOCKS_EXPAND_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandDomainBlocksSuccess(domains, next) {
|
||||
return {
|
||||
type: DOMAIN_BLOCKS_EXPAND_SUCCESS,
|
||||
domains,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandDomainBlocksFail(error) {
|
||||
return {
|
||||
type: DOMAIN_BLOCKS_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
10
app/javascript/gabsocial/actions/dropdown_menu.js
Normal file
10
app/javascript/gabsocial/actions/dropdown_menu.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export const DROPDOWN_MENU_OPEN = 'DROPDOWN_MENU_OPEN';
|
||||
export const DROPDOWN_MENU_CLOSE = 'DROPDOWN_MENU_CLOSE';
|
||||
|
||||
export function openDropdownMenu(id, placement, keyboard) {
|
||||
return { type: DROPDOWN_MENU_OPEN, id, placement, keyboard };
|
||||
}
|
||||
|
||||
export function closeDropdownMenu(id) {
|
||||
return { type: DROPDOWN_MENU_CLOSE, id };
|
||||
}
|
||||
14
app/javascript/gabsocial/actions/emojis.js
Normal file
14
app/javascript/gabsocial/actions/emojis.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { saveSettings } from './settings';
|
||||
|
||||
export const EMOJI_USE = 'EMOJI_USE';
|
||||
|
||||
export function useEmoji(emoji) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: EMOJI_USE,
|
||||
emoji,
|
||||
});
|
||||
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
};
|
||||
98
app/javascript/gabsocial/actions/favourites.js
Normal file
98
app/javascript/gabsocial/actions/favourites.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
||||
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
|
||||
export const FAVOURITED_STATUSES_FETCH_FAIL = 'FAVOURITED_STATUSES_FETCH_FAIL';
|
||||
|
||||
export const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_REQUEST';
|
||||
export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS';
|
||||
export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL';
|
||||
|
||||
export function fetchFavouritedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
if (getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchFavouritedStatusesRequest());
|
||||
|
||||
api(getState).get('/api/v1/favourites').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFavouritedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritedStatusesRequest() {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritedStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritedStatusesFail(error) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['status_lists', 'favourites', 'next'], null);
|
||||
|
||||
if (url === null || getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandFavouritedStatusesRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(expandFavouritedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatusesRequest() {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_EXPAND_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_EXPAND_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandFavouritedStatusesFail(error) {
|
||||
return {
|
||||
type: FAVOURITED_STATUSES_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
29
app/javascript/gabsocial/actions/filters.js
Normal file
29
app/javascript/gabsocial/actions/filters.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import api from '../api';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const FILTERS_FETCH_REQUEST = 'FILTERS_FETCH_REQUEST';
|
||||
export const FILTERS_FETCH_SUCCESS = 'FILTERS_FETCH_SUCCESS';
|
||||
export const FILTERS_FETCH_FAIL = 'FILTERS_FETCH_FAIL';
|
||||
|
||||
export const fetchFilters = () => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch({
|
||||
type: FILTERS_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
api(getState)
|
||||
.get('/api/v1/filters')
|
||||
.then(({ data }) => dispatch({
|
||||
type: FILTERS_FETCH_SUCCESS,
|
||||
filters: data,
|
||||
skipLoading: true,
|
||||
}))
|
||||
.catch(err => dispatch({
|
||||
type: FILTERS_FETCH_FAIL,
|
||||
err,
|
||||
skipLoading: true,
|
||||
skipAlert: true,
|
||||
}));
|
||||
};
|
||||
193
app/javascript/gabsocial/actions/groups.js
Normal file
193
app/javascript/gabsocial/actions/groups.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import api from '../api';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const GROUP_FETCH_REQUEST = 'GROUP_FETCH_REQUEST';
|
||||
export const GROUP_FETCH_SUCCESS = 'GROUP_FETCH_SUCCESS';
|
||||
export const GROUP_FETCH_FAIL = 'GROUP_FETCH_FAIL';
|
||||
|
||||
export const GROUP_RELATIONSHIPS_FETCH_REQUEST = 'GROUP_RELATIONSHIPS_FETCH_REQUEST';
|
||||
export const GROUP_RELATIONSHIPS_FETCH_SUCCESS = 'GROUP_RELATIONSHIPS_FETCH_SUCCESS';
|
||||
export const GROUP_RELATIONSHIPS_FETCH_FAIL = 'GROUP_RELATIONSHIPS_FETCH_FAIL';
|
||||
|
||||
export const GROUPS_FETCH_REQUEST = 'GROUPS_FETCH_REQUEST';
|
||||
export const GROUPS_FETCH_SUCCESS = 'GROUPS_FETCH_SUCCESS';
|
||||
export const GROUPS_FETCH_FAIL = 'GROUPS_FETCH_FAIL';
|
||||
|
||||
export const GROUP_JOIN_REQUEST = 'GROUP_JOIN_REQUEST';
|
||||
export const GROUP_JOIN_SUCCESS = 'GROUP_JOIN_SUCCESS';
|
||||
export const GROUP_JOIN_FAIL = 'GROUP_JOIN_FAIL';
|
||||
|
||||
export const GROUP_LEAVE_REQUEST = 'GROUP_LEAVE_REQUEST';
|
||||
export const GROUP_LEAVE_SUCCESS = 'GROUP_LEAVE_SUCCESS';
|
||||
export const GROUP_LEAVE_FAIL = 'GROUP_LEAVE_FAIL';
|
||||
|
||||
export const fetchGroup = id => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchGroupRelationships([id]));
|
||||
|
||||
if (getState().getIn(['groups', id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchGroupRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/groups/${id}`)
|
||||
.then(({ data }) => dispatch(fetchGroupSuccess(data)))
|
||||
.catch(err => dispatch(fetchGroupFail(id, err)));
|
||||
};
|
||||
|
||||
export const fetchGroupRequest = id => ({
|
||||
type: GROUP_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchGroupSuccess = group => ({
|
||||
type: GROUP_FETCH_SUCCESS,
|
||||
group,
|
||||
});
|
||||
|
||||
export const fetchGroupFail = (id, error) => ({
|
||||
type: GROUP_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export function fetchGroupRelationships(groupIds) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const loadedRelationships = getState().get('group_relationships');
|
||||
const newGroupIds = groupIds.filter(id => loadedRelationships.get(id, null) === null);
|
||||
|
||||
if (newGroupIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchGroupRelationshipsRequest(newGroupIds));
|
||||
|
||||
api(getState).get(`/api/v1/groups/${newGroupIds[0]}/relationships?${newGroupIds.map(id => `id[]=${id}`).join('&')}`).then(response => {
|
||||
dispatch(fetchGroupRelationshipsSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchGroupRelationshipsFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchGroupRelationshipsRequest(ids) {
|
||||
return {
|
||||
type: GROUP_RELATIONSHIPS_FETCH_REQUEST,
|
||||
ids,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchGroupRelationshipsSuccess(relationships) {
|
||||
return {
|
||||
type: GROUP_RELATIONSHIPS_FETCH_SUCCESS,
|
||||
relationships,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchGroupRelationshipsFail(error) {
|
||||
return {
|
||||
type: GROUP_RELATIONSHIPS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchGroups = () => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchGroupsRequest());
|
||||
|
||||
api(getState).get('/api/v1/groups')
|
||||
.then(({ data }) => dispatch(fetchGroupsSuccess(data)))
|
||||
.catch(err => dispatch(fetchGroupsFail(err)));
|
||||
};
|
||||
|
||||
export const fetchGroupsRequest = () => ({
|
||||
type: GROUPS_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchGroupsSuccess = groups => ({
|
||||
type: GROUPS_FETCH_SUCCESS,
|
||||
groups,
|
||||
});
|
||||
|
||||
export const fetchGroupsFail = error => ({
|
||||
type: GROUPS_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export function joinGroup(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(joinGroupRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/groups/${id}/accounts`).then(response => {
|
||||
dispatch(joinGroupSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(joinGroupFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function leaveGroup(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(leaveGroupRequest(id));
|
||||
|
||||
api(getState).delete(`/api/v1/groups/${id}/accounts`).then(response => {
|
||||
dispatch(leaveGroupSuccess(response.data));
|
||||
}).catch(error => {
|
||||
dispatch(leaveGroupFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function joinGroupRequest(id) {
|
||||
return {
|
||||
type: GROUP_JOIN_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function joinGroupSuccess(relationship) {
|
||||
return {
|
||||
type: GROUP_JOIN_SUCCESS,
|
||||
relationship
|
||||
};
|
||||
};
|
||||
|
||||
export function joinGroupFail(error) {
|
||||
return {
|
||||
type: GROUP_JOIN_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function leaveGroupRequest(id) {
|
||||
return {
|
||||
type: GROUP_LEAVE_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function leaveGroupSuccess(relationship) {
|
||||
return {
|
||||
type: GROUP_LEAVE_SUCCESS,
|
||||
relationship,
|
||||
};
|
||||
};
|
||||
|
||||
export function leaveGroupFail(error) {
|
||||
return {
|
||||
type: GROUP_LEAVE_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
17
app/javascript/gabsocial/actions/height_cache.js
Normal file
17
app/javascript/gabsocial/actions/height_cache.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export const HEIGHT_CACHE_SET = 'HEIGHT_CACHE_SET';
|
||||
export const HEIGHT_CACHE_CLEAR = 'HEIGHT_CACHE_CLEAR';
|
||||
|
||||
export function setHeight (key, id, height) {
|
||||
return {
|
||||
type: HEIGHT_CACHE_SET,
|
||||
key,
|
||||
id,
|
||||
height,
|
||||
};
|
||||
};
|
||||
|
||||
export function clearHeight () {
|
||||
return {
|
||||
type: HEIGHT_CACHE_CLEAR,
|
||||
};
|
||||
};
|
||||
30
app/javascript/gabsocial/actions/identity_proofs.js
Normal file
30
app/javascript/gabsocial/actions/identity_proofs.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import api from '../api';
|
||||
|
||||
export const IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST = 'IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST';
|
||||
export const IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS = 'IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS';
|
||||
export const IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL = 'IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL';
|
||||
|
||||
export const fetchAccountIdentityProofs = accountId => (dispatch, getState) => {
|
||||
dispatch(fetchAccountIdentityProofsRequest(accountId));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${accountId}/identity_proofs`)
|
||||
.then(({ data }) => dispatch(fetchAccountIdentityProofsSuccess(accountId, data)))
|
||||
.catch(err => dispatch(fetchAccountIdentityProofsFail(accountId, err)));
|
||||
};
|
||||
|
||||
export const fetchAccountIdentityProofsRequest = id => ({
|
||||
type: IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchAccountIdentityProofsSuccess = (accountId, identity_proofs) => ({
|
||||
type: IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS,
|
||||
accountId,
|
||||
identity_proofs,
|
||||
});
|
||||
|
||||
export const fetchAccountIdentityProofsFail = (accountId, err) => ({
|
||||
type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL,
|
||||
accountId,
|
||||
err,
|
||||
});
|
||||
95
app/javascript/gabsocial/actions/importer/index.js
Normal file
95
app/javascript/gabsocial/actions/importer/index.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import { normalizeAccount, normalizeStatus, normalizePoll } from './normalizer';
|
||||
|
||||
export const ACCOUNT_IMPORT = 'ACCOUNT_IMPORT';
|
||||
export const ACCOUNTS_IMPORT = 'ACCOUNTS_IMPORT';
|
||||
export const STATUS_IMPORT = 'STATUS_IMPORT';
|
||||
export const STATUSES_IMPORT = 'STATUSES_IMPORT';
|
||||
export const POLLS_IMPORT = 'POLLS_IMPORT';
|
||||
export const ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP = 'ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP';
|
||||
|
||||
function pushUnique(array, object) {
|
||||
if (array.every(element => element.id !== object.id)) {
|
||||
array.push(object);
|
||||
}
|
||||
}
|
||||
|
||||
export function importAccount(account) {
|
||||
return { type: ACCOUNT_IMPORT, account };
|
||||
}
|
||||
|
||||
export function importAccounts(accounts) {
|
||||
return { type: ACCOUNTS_IMPORT, accounts };
|
||||
}
|
||||
|
||||
export function importStatus(status) {
|
||||
return { type: STATUS_IMPORT, status };
|
||||
}
|
||||
|
||||
export function importStatuses(statuses) {
|
||||
return { type: STATUSES_IMPORT, statuses };
|
||||
}
|
||||
|
||||
export function importPolls(polls) {
|
||||
return { type: POLLS_IMPORT, polls };
|
||||
}
|
||||
|
||||
export function importFetchedAccount(account) {
|
||||
return importFetchedAccounts([account]);
|
||||
}
|
||||
|
||||
export function importFetchedAccounts(accounts) {
|
||||
const normalAccounts = [];
|
||||
|
||||
function processAccount(account) {
|
||||
pushUnique(normalAccounts, normalizeAccount(account));
|
||||
|
||||
if (account.moved) {
|
||||
processAccount(account.moved);
|
||||
}
|
||||
}
|
||||
|
||||
accounts.forEach(processAccount);
|
||||
|
||||
return importAccounts(normalAccounts);
|
||||
}
|
||||
|
||||
export function importFetchedStatus(status) {
|
||||
return importFetchedStatuses([status]);
|
||||
}
|
||||
|
||||
export function importFetchedStatuses(statuses) {
|
||||
return (dispatch, getState) => {
|
||||
const accounts = [];
|
||||
const normalStatuses = [];
|
||||
const polls = [];
|
||||
|
||||
function processStatus(status) {
|
||||
pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id])));
|
||||
pushUnique(accounts, status.account);
|
||||
|
||||
if (status.reblog && status.reblog.id) {
|
||||
processStatus(status.reblog);
|
||||
}
|
||||
|
||||
if (status.poll && status.poll.id) {
|
||||
pushUnique(polls, normalizePoll(status.poll));
|
||||
}
|
||||
}
|
||||
|
||||
statuses.forEach(processStatus);
|
||||
|
||||
dispatch(importPolls(polls));
|
||||
dispatch(importFetchedAccounts(accounts));
|
||||
dispatch(importStatuses(normalStatuses));
|
||||
};
|
||||
}
|
||||
|
||||
export function importFetchedPoll(poll) {
|
||||
return dispatch => {
|
||||
dispatch(importPolls([normalizePoll(poll)]));
|
||||
};
|
||||
}
|
||||
|
||||
export function importErrorWhileFetchingAccountByUsername(username) {
|
||||
return { type: ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP, username };
|
||||
};
|
||||
82
app/javascript/gabsocial/actions/importer/normalizer.js
Normal file
82
app/javascript/gabsocial/actions/importer/normalizer.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import emojify from '../../features/emoji/emoji';
|
||||
import { unescapeHTML } from '../../utils/html';
|
||||
import { expandSpoilers } from '../../initial_state';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
|
||||
obj[`:${emoji.shortcode}:`] = emoji;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
export function normalizeAccount(account) {
|
||||
account = { ...account };
|
||||
|
||||
const emojiMap = makeEmojiMap(account);
|
||||
const displayName = account.display_name.trim().length === 0 ? account.username : account.display_name;
|
||||
|
||||
account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap);
|
||||
account.note_emojified = emojify(account.note, emojiMap);
|
||||
|
||||
if (account.fields) {
|
||||
account.fields = account.fields.map(pair => ({
|
||||
...pair,
|
||||
name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
|
||||
value_emojified: emojify(pair.value, emojiMap),
|
||||
value_plain: unescapeHTML(pair.value),
|
||||
}));
|
||||
}
|
||||
|
||||
if (account.moved) {
|
||||
account.moved = account.moved.id;
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
export function normalizeStatus(status, normalOldStatus) {
|
||||
const normalStatus = { ...status };
|
||||
normalStatus.account = status.account.id;
|
||||
|
||||
if (status.reblog && status.reblog.id) {
|
||||
normalStatus.reblog = status.reblog.id;
|
||||
}
|
||||
|
||||
if (status.poll && status.poll.id) {
|
||||
normalStatus.poll = status.poll.id;
|
||||
}
|
||||
|
||||
// Only calculate these values when status first encountered
|
||||
// Otherwise keep the ones already in the reducer
|
||||
if (normalOldStatus) {
|
||||
normalStatus.search_index = normalOldStatus.get('search_index');
|
||||
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
|
||||
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
|
||||
normalStatus.hidden = normalOldStatus.get('hidden');
|
||||
} else {
|
||||
const spoilerText = normalStatus.spoiler_text || '';
|
||||
const searchContent = [spoilerText, status.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
const emojiMap = makeEmojiMap(normalStatus);
|
||||
|
||||
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
|
||||
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
|
||||
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
||||
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
||||
}
|
||||
|
||||
return normalStatus;
|
||||
}
|
||||
|
||||
export function normalizePoll(poll) {
|
||||
const normalPoll = { ...poll };
|
||||
|
||||
const emojiMap = makeEmojiMap(normalPoll);
|
||||
|
||||
normalPoll.options = poll.options.map(option => ({
|
||||
...option,
|
||||
title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap),
|
||||
}));
|
||||
|
||||
return normalPoll;
|
||||
}
|
||||
351
app/javascript/gabsocial/actions/interactions.js
Normal file
351
app/javascript/gabsocial/actions/interactions.js
Normal file
@@ -0,0 +1,351 @@
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
|
||||
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
|
||||
export const REBLOG_FAIL = 'REBLOG_FAIL';
|
||||
|
||||
export const FAVOURITE_REQUEST = 'FAVOURITE_REQUEST';
|
||||
export const FAVOURITE_SUCCESS = 'FAVOURITE_SUCCESS';
|
||||
export const FAVOURITE_FAIL = 'FAVOURITE_FAIL';
|
||||
|
||||
export const UNREBLOG_REQUEST = 'UNREBLOG_REQUEST';
|
||||
export const UNREBLOG_SUCCESS = 'UNREBLOG_SUCCESS';
|
||||
export const UNREBLOG_FAIL = 'UNREBLOG_FAIL';
|
||||
|
||||
export const UNFAVOURITE_REQUEST = 'UNFAVOURITE_REQUEST';
|
||||
export const UNFAVOURITE_SUCCESS = 'UNFAVOURITE_SUCCESS';
|
||||
export const UNFAVOURITE_FAIL = 'UNFAVOURITE_FAIL';
|
||||
|
||||
export const REBLOGS_FETCH_REQUEST = 'REBLOGS_FETCH_REQUEST';
|
||||
export const REBLOGS_FETCH_SUCCESS = 'REBLOGS_FETCH_SUCCESS';
|
||||
export const REBLOGS_FETCH_FAIL = 'REBLOGS_FETCH_FAIL';
|
||||
|
||||
export const FAVOURITES_FETCH_REQUEST = 'FAVOURITES_FETCH_REQUEST';
|
||||
export const FAVOURITES_FETCH_SUCCESS = 'FAVOURITES_FETCH_SUCCESS';
|
||||
export const FAVOURITES_FETCH_FAIL = 'FAVOURITES_FETCH_FAIL';
|
||||
|
||||
export const PIN_REQUEST = 'PIN_REQUEST';
|
||||
export const PIN_SUCCESS = 'PIN_SUCCESS';
|
||||
export const PIN_FAIL = 'PIN_FAIL';
|
||||
|
||||
export const UNPIN_REQUEST = 'UNPIN_REQUEST';
|
||||
export const UNPIN_SUCCESS = 'UNPIN_SUCCESS';
|
||||
export const UNPIN_FAIL = 'UNPIN_FAIL';
|
||||
|
||||
export function reblog(status) {
|
||||
return function (dispatch, getState) {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(reblogRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`).then(function (response) {
|
||||
// The reblog API method returns a new status wrapped around the original. In this case we are only
|
||||
// interested in how the original is modified, hence passing it skipping the wrapper
|
||||
dispatch(importFetchedStatus(response.data.reblog));
|
||||
dispatch(reblogSuccess(status));
|
||||
}).catch(function (error) {
|
||||
dispatch(reblogFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblog(status) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unreblogRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(unreblogSuccess(status));
|
||||
}).catch(error => {
|
||||
dispatch(unreblogFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function reblogRequest(status) {
|
||||
return {
|
||||
type: REBLOG_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function reblogSuccess(status) {
|
||||
return {
|
||||
type: REBLOG_SUCCESS,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function reblogFail(status, error) {
|
||||
return {
|
||||
type: REBLOG_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblogRequest(status) {
|
||||
return {
|
||||
type: UNREBLOG_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblogSuccess(status) {
|
||||
return {
|
||||
type: UNREBLOG_SUCCESS,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unreblogFail(status, error) {
|
||||
return {
|
||||
type: UNREBLOG_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function favourite(status) {
|
||||
return function (dispatch, getState) {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(favouriteRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(favouriteSuccess(status));
|
||||
}).catch(function (error) {
|
||||
dispatch(favouriteFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavourite(status) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unfavouriteRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(unfavouriteSuccess(status));
|
||||
}).catch(error => {
|
||||
dispatch(unfavouriteFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function favouriteRequest(status) {
|
||||
return {
|
||||
type: FAVOURITE_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function favouriteSuccess(status) {
|
||||
return {
|
||||
type: FAVOURITE_SUCCESS,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function favouriteFail(status, error) {
|
||||
return {
|
||||
type: FAVOURITE_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavouriteRequest(status) {
|
||||
return {
|
||||
type: UNFAVOURITE_REQUEST,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavouriteSuccess(status) {
|
||||
return {
|
||||
type: UNFAVOURITE_SUCCESS,
|
||||
status: status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unfavouriteFail(status, error) {
|
||||
return {
|
||||
type: UNFAVOURITE_FAIL,
|
||||
status: status,
|
||||
error: error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogs(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchReblogsRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchReblogsSuccess(id, response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchReblogsFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogsRequest(id) {
|
||||
return {
|
||||
type: REBLOGS_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogsSuccess(id, accounts) {
|
||||
return {
|
||||
type: REBLOGS_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchReblogsFail(id, error) {
|
||||
return {
|
||||
type: REBLOGS_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavourites(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchFavouritesRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchFavouritesSuccess(id, response.data));
|
||||
}).catch(error => {
|
||||
dispatch(fetchFavouritesFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritesRequest(id) {
|
||||
return {
|
||||
type: FAVOURITES_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritesSuccess(id, accounts) {
|
||||
return {
|
||||
type: FAVOURITES_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchFavouritesFail(id, error) {
|
||||
return {
|
||||
type: FAVOURITES_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function pin(status) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(pinRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/pin`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(pinSuccess(status));
|
||||
}).catch(error => {
|
||||
dispatch(pinFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function pinRequest(status) {
|
||||
return {
|
||||
type: PIN_REQUEST,
|
||||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function pinSuccess(status) {
|
||||
return {
|
||||
type: PIN_SUCCESS,
|
||||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function pinFail(status, error) {
|
||||
return {
|
||||
type: PIN_FAIL,
|
||||
status,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unpin (status) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unpinRequest(status));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unpin`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(unpinSuccess(status));
|
||||
}).catch(error => {
|
||||
dispatch(unpinFail(status, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function unpinRequest(status) {
|
||||
return {
|
||||
type: UNPIN_REQUEST,
|
||||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unpinSuccess(status) {
|
||||
return {
|
||||
type: UNPIN_SUCCESS,
|
||||
status,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function unpinFail(status, error) {
|
||||
return {
|
||||
type: UNPIN_FAIL,
|
||||
status,
|
||||
error,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
392
app/javascript/gabsocial/actions/lists.js
Normal file
392
app/javascript/gabsocial/actions/lists.js
Normal file
@@ -0,0 +1,392 @@
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { showAlertForError } from './alerts';
|
||||
import { me } from 'gabsocial/initial_state'
|
||||
|
||||
export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST';
|
||||
export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS';
|
||||
export const LIST_FETCH_FAIL = 'LIST_FETCH_FAIL';
|
||||
|
||||
export const LISTS_FETCH_REQUEST = 'LISTS_FETCH_REQUEST';
|
||||
export const LISTS_FETCH_SUCCESS = 'LISTS_FETCH_SUCCESS';
|
||||
export const LISTS_FETCH_FAIL = 'LISTS_FETCH_FAIL';
|
||||
|
||||
export const LIST_EDITOR_TITLE_CHANGE = 'LIST_EDITOR_TITLE_CHANGE';
|
||||
export const LIST_EDITOR_RESET = 'LIST_EDITOR_RESET';
|
||||
export const LIST_EDITOR_SETUP = 'LIST_EDITOR_SETUP';
|
||||
|
||||
export const LIST_CREATE_REQUEST = 'LIST_CREATE_REQUEST';
|
||||
export const LIST_CREATE_SUCCESS = 'LIST_CREATE_SUCCESS';
|
||||
export const LIST_CREATE_FAIL = 'LIST_CREATE_FAIL';
|
||||
|
||||
export const LIST_UPDATE_REQUEST = 'LIST_UPDATE_REQUEST';
|
||||
export const LIST_UPDATE_SUCCESS = 'LIST_UPDATE_SUCCESS';
|
||||
export const LIST_UPDATE_FAIL = 'LIST_UPDATE_FAIL';
|
||||
|
||||
export const LIST_DELETE_REQUEST = 'LIST_DELETE_REQUEST';
|
||||
export const LIST_DELETE_SUCCESS = 'LIST_DELETE_SUCCESS';
|
||||
export const LIST_DELETE_FAIL = 'LIST_DELETE_FAIL';
|
||||
|
||||
export const LIST_ACCOUNTS_FETCH_REQUEST = 'LIST_ACCOUNTS_FETCH_REQUEST';
|
||||
export const LIST_ACCOUNTS_FETCH_SUCCESS = 'LIST_ACCOUNTS_FETCH_SUCCESS';
|
||||
export const LIST_ACCOUNTS_FETCH_FAIL = 'LIST_ACCOUNTS_FETCH_FAIL';
|
||||
|
||||
export const LIST_EDITOR_SUGGESTIONS_CHANGE = 'LIST_EDITOR_SUGGESTIONS_CHANGE';
|
||||
export const LIST_EDITOR_SUGGESTIONS_READY = 'LIST_EDITOR_SUGGESTIONS_READY';
|
||||
export const LIST_EDITOR_SUGGESTIONS_CLEAR = 'LIST_EDITOR_SUGGESTIONS_CLEAR';
|
||||
|
||||
export const LIST_EDITOR_ADD_REQUEST = 'LIST_EDITOR_ADD_REQUEST';
|
||||
export const LIST_EDITOR_ADD_SUCCESS = 'LIST_EDITOR_ADD_SUCCESS';
|
||||
export const LIST_EDITOR_ADD_FAIL = 'LIST_EDITOR_ADD_FAIL';
|
||||
|
||||
export const LIST_EDITOR_REMOVE_REQUEST = 'LIST_EDITOR_REMOVE_REQUEST';
|
||||
export const LIST_EDITOR_REMOVE_SUCCESS = 'LIST_EDITOR_REMOVE_SUCCESS';
|
||||
export const LIST_EDITOR_REMOVE_FAIL = 'LIST_EDITOR_REMOVE_FAIL';
|
||||
|
||||
export const LIST_ADDER_RESET = 'LIST_ADDER_RESET';
|
||||
export const LIST_ADDER_SETUP = 'LIST_ADDER_SETUP';
|
||||
|
||||
export const LIST_ADDER_LISTS_FETCH_REQUEST = 'LIST_ADDER_LISTS_FETCH_REQUEST';
|
||||
export const LIST_ADDER_LISTS_FETCH_SUCCESS = 'LIST_ADDER_LISTS_FETCH_SUCCESS';
|
||||
export const LIST_ADDER_LISTS_FETCH_FAIL = 'LIST_ADDER_LISTS_FETCH_FAIL';
|
||||
|
||||
export const fetchList = id => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
if (getState().getIn(['lists', id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchListRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/lists/${id}`)
|
||||
.then(({ data }) => dispatch(fetchListSuccess(data)))
|
||||
.catch(err => dispatch(fetchListFail(id, err)));
|
||||
};
|
||||
|
||||
export const fetchListRequest = id => ({
|
||||
type: LIST_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchListSuccess = list => ({
|
||||
type: LIST_FETCH_SUCCESS,
|
||||
list,
|
||||
});
|
||||
|
||||
export const fetchListFail = (id, error) => ({
|
||||
type: LIST_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchLists = () => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchListsRequest());
|
||||
|
||||
api(getState).get('/api/v1/lists')
|
||||
.then(({ data }) => dispatch(fetchListsSuccess(data)))
|
||||
.catch(err => dispatch(fetchListsFail(err)));
|
||||
};
|
||||
|
||||
export const fetchListsRequest = () => ({
|
||||
type: LISTS_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchListsSuccess = lists => ({
|
||||
type: LISTS_FETCH_SUCCESS,
|
||||
lists,
|
||||
});
|
||||
|
||||
export const fetchListsFail = error => ({
|
||||
type: LISTS_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const submitListEditor = shouldReset => (dispatch, getState) => {
|
||||
const listId = getState().getIn(['listEditor', 'listId']);
|
||||
const title = getState().getIn(['listEditor', 'title']);
|
||||
|
||||
if (listId === null) {
|
||||
dispatch(createList(title, shouldReset));
|
||||
} else {
|
||||
dispatch(updateList(listId, title, shouldReset));
|
||||
}
|
||||
};
|
||||
|
||||
export const setupListEditor = listId => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: LIST_EDITOR_SETUP,
|
||||
list: getState().getIn(['lists', listId]),
|
||||
});
|
||||
|
||||
dispatch(fetchListAccounts(listId));
|
||||
};
|
||||
|
||||
export const changeListEditorTitle = value => ({
|
||||
type: LIST_EDITOR_TITLE_CHANGE,
|
||||
value,
|
||||
});
|
||||
|
||||
export const createList = (title, shouldReset) => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(createListRequest());
|
||||
|
||||
api(getState).post('/api/v1/lists', { title }).then(({ data }) => {
|
||||
dispatch(createListSuccess(data));
|
||||
|
||||
if (shouldReset) {
|
||||
dispatch(resetListEditor());
|
||||
}
|
||||
}).catch(err => dispatch(createListFail(err)));
|
||||
};
|
||||
|
||||
export const createListRequest = () => ({
|
||||
type: LIST_CREATE_REQUEST,
|
||||
});
|
||||
|
||||
export const createListSuccess = list => ({
|
||||
type: LIST_CREATE_SUCCESS,
|
||||
list,
|
||||
});
|
||||
|
||||
export const createListFail = error => ({
|
||||
type: LIST_CREATE_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const updateList = (id, title, shouldReset) => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(updateListRequest(id));
|
||||
|
||||
api(getState).put(`/api/v1/lists/${id}`, { title }).then(({ data }) => {
|
||||
dispatch(updateListSuccess(data));
|
||||
|
||||
if (shouldReset) {
|
||||
dispatch(resetListEditor());
|
||||
}
|
||||
}).catch(err => dispatch(updateListFail(id, err)));
|
||||
};
|
||||
|
||||
export const updateListRequest = id => ({
|
||||
type: LIST_UPDATE_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const updateListSuccess = list => ({
|
||||
type: LIST_UPDATE_SUCCESS,
|
||||
list,
|
||||
});
|
||||
|
||||
export const updateListFail = (id, error) => ({
|
||||
type: LIST_UPDATE_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const resetListEditor = () => ({
|
||||
type: LIST_EDITOR_RESET,
|
||||
});
|
||||
|
||||
export const deleteList = id => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(deleteListRequest(id));
|
||||
|
||||
api(getState).delete(`/api/v1/lists/${id}`)
|
||||
.then(() => dispatch(deleteListSuccess(id)))
|
||||
.catch(err => dispatch(deleteListFail(id, err)));
|
||||
};
|
||||
|
||||
export const deleteListRequest = id => ({
|
||||
type: LIST_DELETE_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const deleteListSuccess = id => ({
|
||||
type: LIST_DELETE_SUCCESS,
|
||||
id,
|
||||
});
|
||||
|
||||
export const deleteListFail = (id, error) => ({
|
||||
type: LIST_DELETE_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchListAccounts = listId => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchListAccountsRequest(listId));
|
||||
|
||||
api(getState).get(`/api/v1/lists/${listId}/accounts`, { params: { limit: 0 } }).then(({ data }) => {
|
||||
dispatch(importFetchedAccounts(data));
|
||||
dispatch(fetchListAccountsSuccess(listId, data));
|
||||
}).catch(err => dispatch(fetchListAccountsFail(listId, err)));
|
||||
};
|
||||
|
||||
export const fetchListAccountsRequest = id => ({
|
||||
type: LIST_ACCOUNTS_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchListAccountsSuccess = (id, accounts, next) => ({
|
||||
type: LIST_ACCOUNTS_FETCH_SUCCESS,
|
||||
id,
|
||||
accounts,
|
||||
next,
|
||||
});
|
||||
|
||||
export const fetchListAccountsFail = (id, error) => ({
|
||||
type: LIST_ACCOUNTS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchListSuggestions = q => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const params = {
|
||||
q,
|
||||
resolve: false,
|
||||
limit: 4,
|
||||
following: true,
|
||||
};
|
||||
|
||||
api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => {
|
||||
dispatch(importFetchedAccounts(data));
|
||||
dispatch(fetchListSuggestionsReady(q, data));
|
||||
}).catch(error => dispatch(showAlertForError(error)));
|
||||
};
|
||||
|
||||
export const fetchListSuggestionsReady = (query, accounts) => ({
|
||||
type: LIST_EDITOR_SUGGESTIONS_READY,
|
||||
query,
|
||||
accounts,
|
||||
});
|
||||
|
||||
export const clearListSuggestions = () => ({
|
||||
type: LIST_EDITOR_SUGGESTIONS_CLEAR,
|
||||
});
|
||||
|
||||
export const changeListSuggestions = value => ({
|
||||
type: LIST_EDITOR_SUGGESTIONS_CHANGE,
|
||||
value,
|
||||
});
|
||||
|
||||
export const addToListEditor = accountId => (dispatch, getState) => {
|
||||
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId));
|
||||
};
|
||||
|
||||
export const addToList = (listId, accountId) => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(addToListRequest(listId, accountId));
|
||||
|
||||
api(getState).post(`/api/v1/lists/${listId}/accounts`, { account_ids: [accountId] })
|
||||
.then(() => dispatch(addToListSuccess(listId, accountId)))
|
||||
.catch(err => dispatch(addToListFail(listId, accountId, err)));
|
||||
};
|
||||
|
||||
export const addToListRequest = (listId, accountId) => ({
|
||||
type: LIST_EDITOR_ADD_REQUEST,
|
||||
listId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const addToListSuccess = (listId, accountId) => ({
|
||||
type: LIST_EDITOR_ADD_SUCCESS,
|
||||
listId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const addToListFail = (listId, accountId, error) => ({
|
||||
type: LIST_EDITOR_ADD_FAIL,
|
||||
listId,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
export const removeFromListEditor = accountId => (dispatch, getState) => {
|
||||
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId));
|
||||
};
|
||||
|
||||
export const removeFromList = (listId, accountId) => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(removeFromListRequest(listId, accountId));
|
||||
|
||||
api(getState).delete(`/api/v1/lists/${listId}/accounts`, { params: { account_ids: [accountId] } })
|
||||
.then(() => dispatch(removeFromListSuccess(listId, accountId)))
|
||||
.catch(err => dispatch(removeFromListFail(listId, accountId, err)));
|
||||
};
|
||||
|
||||
export const removeFromListRequest = (listId, accountId) => ({
|
||||
type: LIST_EDITOR_REMOVE_REQUEST,
|
||||
listId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const removeFromListSuccess = (listId, accountId) => ({
|
||||
type: LIST_EDITOR_REMOVE_SUCCESS,
|
||||
listId,
|
||||
accountId,
|
||||
});
|
||||
|
||||
export const removeFromListFail = (listId, accountId, error) => ({
|
||||
type: LIST_EDITOR_REMOVE_FAIL,
|
||||
listId,
|
||||
accountId,
|
||||
error,
|
||||
});
|
||||
|
||||
export const resetListAdder = () => ({
|
||||
type: LIST_ADDER_RESET,
|
||||
});
|
||||
|
||||
export const setupListAdder = accountId => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: LIST_ADDER_SETUP,
|
||||
account: getState().getIn(['accounts', accountId]),
|
||||
});
|
||||
dispatch(fetchLists());
|
||||
dispatch(fetchAccountLists(accountId));
|
||||
};
|
||||
|
||||
export const fetchAccountLists = accountId => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchAccountListsRequest(accountId));
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${accountId}/lists`)
|
||||
.then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data)))
|
||||
.catch(err => dispatch(fetchAccountListsFail(accountId, err)));
|
||||
};
|
||||
|
||||
export const fetchAccountListsRequest = id => ({
|
||||
type:LIST_ADDER_LISTS_FETCH_REQUEST,
|
||||
id,
|
||||
});
|
||||
|
||||
export const fetchAccountListsSuccess = (id, lists) => ({
|
||||
type: LIST_ADDER_LISTS_FETCH_SUCCESS,
|
||||
id,
|
||||
lists,
|
||||
});
|
||||
|
||||
export const fetchAccountListsFail = (id, err) => ({
|
||||
type: LIST_ADDER_LISTS_FETCH_FAIL,
|
||||
id,
|
||||
err,
|
||||
});
|
||||
|
||||
export const addToListAdder = listId => (dispatch, getState) => {
|
||||
dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])));
|
||||
};
|
||||
|
||||
export const removeFromListAdder = listId => (dispatch, getState) => {
|
||||
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
|
||||
};
|
||||
16
app/javascript/gabsocial/actions/modal.js
Normal file
16
app/javascript/gabsocial/actions/modal.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export const MODAL_OPEN = 'MODAL_OPEN';
|
||||
export const MODAL_CLOSE = 'MODAL_CLOSE';
|
||||
|
||||
export function openModal(type, props) {
|
||||
return {
|
||||
type: MODAL_OPEN,
|
||||
modalType: type,
|
||||
modalProps: props,
|
||||
};
|
||||
};
|
||||
|
||||
export function closeModal() {
|
||||
return {
|
||||
type: MODAL_CLOSE,
|
||||
};
|
||||
};
|
||||
111
app/javascript/gabsocial/actions/mutes.js
Normal file
111
app/javascript/gabsocial/actions/mutes.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { openModal } from './modal';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const MUTES_FETCH_REQUEST = 'MUTES_FETCH_REQUEST';
|
||||
export const MUTES_FETCH_SUCCESS = 'MUTES_FETCH_SUCCESS';
|
||||
export const MUTES_FETCH_FAIL = 'MUTES_FETCH_FAIL';
|
||||
|
||||
export const MUTES_EXPAND_REQUEST = 'MUTES_EXPAND_REQUEST';
|
||||
export const MUTES_EXPAND_SUCCESS = 'MUTES_EXPAND_SUCCESS';
|
||||
export const MUTES_EXPAND_FAIL = 'MUTES_EXPAND_FAIL';
|
||||
|
||||
export const MUTES_INIT_MODAL = 'MUTES_INIT_MODAL';
|
||||
export const MUTES_TOGGLE_HIDE_NOTIFICATIONS = 'MUTES_TOGGLE_HIDE_NOTIFICATIONS';
|
||||
|
||||
export function fetchMutes() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchMutesRequest());
|
||||
|
||||
api(getState).get('/api/v1/mutes').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchMutesSuccess(response.data, next ? next.uri : null));
|
||||
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 {
|
||||
type: MUTES_FETCH_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchMutesFail(error) {
|
||||
return {
|
||||
type: MUTES_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandMutes() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const url = getState().getIn(['user_lists', 'mutes', 'next']);
|
||||
|
||||
if (url === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandMutesRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(expandMutesSuccess(response.data, next ? next.uri : null));
|
||||
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 {
|
||||
type: MUTES_EXPAND_SUCCESS,
|
||||
accounts,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandMutesFail(error) {
|
||||
return {
|
||||
type: MUTES_EXPAND_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function initMuteModal(account) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: MUTES_INIT_MODAL,
|
||||
account,
|
||||
});
|
||||
|
||||
dispatch(openModal('MUTE'));
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleHideNotifications() {
|
||||
return dispatch => {
|
||||
dispatch({ type: MUTES_TOGGLE_HIDE_NOTIFICATIONS });
|
||||
};
|
||||
}
|
||||
198
app/javascript/gabsocial/actions/notifications.js
Normal file
198
app/javascript/gabsocial/actions/notifications.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import api, { getLinks } from '../api';
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import {
|
||||
importFetchedAccount,
|
||||
importFetchedAccounts,
|
||||
importFetchedStatus,
|
||||
importFetchedStatuses,
|
||||
} from './importer';
|
||||
import { saveSettings } from './settings';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import { unescapeHTML } from '../utils/html';
|
||||
import { getFilters, regexFromFilters } from '../selectors';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
|
||||
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
|
||||
|
||||
export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
|
||||
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
|
||||
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
|
||||
|
||||
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
|
||||
|
||||
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
|
||||
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
|
||||
|
||||
defineMessages({
|
||||
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
|
||||
group: { id: 'notifications.group', defaultMessage: '{count} notifications' },
|
||||
});
|
||||
|
||||
const fetchRelatedRelationships = (dispatch, notifications) => {
|
||||
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
|
||||
|
||||
if (accountIds.length > 0) {
|
||||
dispatch(fetchRelationships(accountIds));
|
||||
}
|
||||
};
|
||||
|
||||
export function updateNotifications(notification, intlMessages, intlLocale) {
|
||||
return (dispatch, getState) => {
|
||||
const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
|
||||
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
|
||||
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
|
||||
const filters = getFilters(getState(), { contextType: 'notifications' });
|
||||
|
||||
let filtered = false;
|
||||
|
||||
if (notification.type === 'mention') {
|
||||
const regex = regexFromFilters(filters);
|
||||
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
|
||||
|
||||
filtered = regex && regex.test(searchIndex);
|
||||
}
|
||||
|
||||
if (showInColumn) {
|
||||
dispatch(importFetchedAccount(notification.account));
|
||||
|
||||
if (notification.status) {
|
||||
dispatch(importFetchedStatus(notification.status));
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: NOTIFICATIONS_UPDATE,
|
||||
notification,
|
||||
meta: (playSound && !filtered) ? { sound: 'ribbit' } : undefined,
|
||||
});
|
||||
|
||||
fetchRelatedRelationships(dispatch, [notification]);
|
||||
} else if (playSound && !filtered) {
|
||||
dispatch({
|
||||
type: NOTIFICATIONS_UPDATE_NOOP,
|
||||
meta: { sound: 'ribbit' },
|
||||
});
|
||||
}
|
||||
|
||||
// Desktop notifications
|
||||
if (typeof window.Notification !== 'undefined' && showAlert && !filtered) {
|
||||
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
|
||||
const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : '');
|
||||
|
||||
const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id });
|
||||
|
||||
notify.addEventListener('click', () => {
|
||||
window.focus();
|
||||
notify.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
|
||||
|
||||
const excludeTypesFromFilter = filter => {
|
||||
const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention', 'poll']);
|
||||
return allTypes.filterNot(item => item === filter).toJS();
|
||||
};
|
||||
|
||||
const noOp = () => {};
|
||||
|
||||
export function expandNotifications({ maxId } = {}, done = noOp) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
|
||||
const notifications = getState().get('notifications');
|
||||
const isLoadingMore = !!maxId;
|
||||
|
||||
if (notifications.get('isLoading')) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
const params = {
|
||||
max_id: maxId,
|
||||
exclude_types: activeFilter === 'all'
|
||||
? excludeTypesFromSettings(getState())
|
||||
: excludeTypesFromFilter(activeFilter),
|
||||
};
|
||||
|
||||
if (!maxId && notifications.get('items').size > 0) {
|
||||
params.since_id = notifications.getIn(['items', 0, 'id']);
|
||||
}
|
||||
|
||||
dispatch(expandNotificationsRequest(isLoadingMore));
|
||||
|
||||
api(getState).get('/api/v1/notifications', { params }).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data.map(item => item.account)));
|
||||
dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
|
||||
|
||||
dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore));
|
||||
fetchRelatedRelationships(dispatch, response.data);
|
||||
done();
|
||||
}).catch(error => {
|
||||
dispatch(expandNotificationsFail(error, isLoadingMore));
|
||||
done();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function expandNotificationsRequest(isLoadingMore) {
|
||||
return {
|
||||
type: NOTIFICATIONS_EXPAND_REQUEST,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandNotificationsSuccess(notifications, next, isLoadingMore) {
|
||||
return {
|
||||
type: NOTIFICATIONS_EXPAND_SUCCESS,
|
||||
notifications,
|
||||
next,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandNotificationsFail(error, isLoadingMore) {
|
||||
return {
|
||||
type: NOTIFICATIONS_EXPAND_FAIL,
|
||||
error,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
|
||||
export function clearNotifications() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch({
|
||||
type: NOTIFICATIONS_CLEAR,
|
||||
});
|
||||
|
||||
api(getState).post('/api/v1/notifications/clear');
|
||||
};
|
||||
};
|
||||
|
||||
export function scrollTopNotifications(top) {
|
||||
return {
|
||||
type: NOTIFICATIONS_SCROLL_TOP,
|
||||
top,
|
||||
};
|
||||
};
|
||||
|
||||
export function setFilter (filterType) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: NOTIFICATIONS_FILTER_SET,
|
||||
path: ['notifications', 'quickFilter', 'active'],
|
||||
value: filterType,
|
||||
});
|
||||
dispatch(expandNotifications());
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
};
|
||||
8
app/javascript/gabsocial/actions/onboarding.js
Normal file
8
app/javascript/gabsocial/actions/onboarding.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { changeSetting, saveSettings } from './settings';
|
||||
|
||||
export const INTRODUCTION_VERSION = 20181216044202;
|
||||
|
||||
export const closeOnboarding = () => dispatch => {
|
||||
dispatch(changeSetting(['introductionVersion'], INTRODUCTION_VERSION));
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
43
app/javascript/gabsocial/actions/pin_statuses.js
Normal file
43
app/javascript/gabsocial/actions/pin_statuses.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import api from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
|
||||
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
|
||||
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
|
||||
|
||||
export function fetchPinnedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(fetchPinnedStatusesRequest());
|
||||
|
||||
api(getState).get(`/api/v1/accounts/${me}/statuses`, { params: { pinned: true } }).then(response => {
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(fetchPinnedStatusesSuccess(response.data, null));
|
||||
}).catch(error => {
|
||||
dispatch(fetchPinnedStatusesFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchPinnedStatusesRequest() {
|
||||
return {
|
||||
type: PINNED_STATUSES_FETCH_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchPinnedStatusesSuccess(statuses, next) {
|
||||
return {
|
||||
type: PINNED_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchPinnedStatusesFail(error) {
|
||||
return {
|
||||
type: PINNED_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
60
app/javascript/gabsocial/actions/polls.js
Normal file
60
app/javascript/gabsocial/actions/polls.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import api from '../api';
|
||||
import { importFetchedPoll } from './importer';
|
||||
|
||||
export const POLL_VOTE_REQUEST = 'POLL_VOTE_REQUEST';
|
||||
export const POLL_VOTE_SUCCESS = 'POLL_VOTE_SUCCESS';
|
||||
export const POLL_VOTE_FAIL = 'POLL_VOTE_FAIL';
|
||||
|
||||
export const POLL_FETCH_REQUEST = 'POLL_FETCH_REQUEST';
|
||||
export const POLL_FETCH_SUCCESS = 'POLL_FETCH_SUCCESS';
|
||||
export const POLL_FETCH_FAIL = 'POLL_FETCH_FAIL';
|
||||
|
||||
export const vote = (pollId, choices) => (dispatch, getState) => {
|
||||
dispatch(voteRequest());
|
||||
|
||||
api(getState).post(`/api/v1/polls/${pollId}/votes`, { choices })
|
||||
.then(({ data }) => {
|
||||
dispatch(importFetchedPoll(data));
|
||||
dispatch(voteSuccess(data));
|
||||
})
|
||||
.catch(err => dispatch(voteFail(err)));
|
||||
};
|
||||
|
||||
export const fetchPoll = pollId => (dispatch, getState) => {
|
||||
dispatch(fetchPollRequest());
|
||||
|
||||
api(getState).get(`/api/v1/polls/${pollId}`)
|
||||
.then(({ data }) => {
|
||||
dispatch(importFetchedPoll(data));
|
||||
dispatch(fetchPollSuccess(data));
|
||||
})
|
||||
.catch(err => dispatch(fetchPollFail(err)));
|
||||
};
|
||||
|
||||
export const voteRequest = () => ({
|
||||
type: POLL_VOTE_REQUEST,
|
||||
});
|
||||
|
||||
export const voteSuccess = poll => ({
|
||||
type: POLL_VOTE_SUCCESS,
|
||||
poll,
|
||||
});
|
||||
|
||||
export const voteFail = error => ({
|
||||
type: POLL_VOTE_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const fetchPollRequest = () => ({
|
||||
type: POLL_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchPollSuccess = poll => ({
|
||||
type: POLL_FETCH_SUCCESS,
|
||||
poll,
|
||||
});
|
||||
|
||||
export const fetchPollFail = error => ({
|
||||
type: POLL_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
23
app/javascript/gabsocial/actions/push_notifications/index.js
Normal file
23
app/javascript/gabsocial/actions/push_notifications/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
SET_BROWSER_SUPPORT,
|
||||
SET_SUBSCRIPTION,
|
||||
CLEAR_SUBSCRIPTION,
|
||||
SET_ALERTS,
|
||||
setAlerts,
|
||||
} from './setter';
|
||||
import { register, saveSettings } from './registerer';
|
||||
|
||||
export {
|
||||
SET_BROWSER_SUPPORT,
|
||||
SET_SUBSCRIPTION,
|
||||
CLEAR_SUBSCRIPTION,
|
||||
SET_ALERTS,
|
||||
register,
|
||||
};
|
||||
|
||||
export function changeAlerts(path, value) {
|
||||
return dispatch => {
|
||||
dispatch(setAlerts(path, value));
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import api from '../../api';
|
||||
import { decode as decodeBase64 } from '../../utils/base64';
|
||||
import { pushNotificationsSetting } from '../../settings';
|
||||
import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
|
||||
import { me } from '../../initial_state';
|
||||
|
||||
// Taken from https://www.npmjs.com/package/web-push
|
||||
const urlBase64ToUint8Array = (base64String) => {
|
||||
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
||||
const base64 = (base64String + padding)
|
||||
.replace(/\-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
|
||||
return decodeBase64(base64);
|
||||
};
|
||||
|
||||
const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content');
|
||||
|
||||
const getRegistration = () => navigator.serviceWorker.ready;
|
||||
|
||||
const getPushSubscription = (registration) =>
|
||||
registration.pushManager.getSubscription()
|
||||
.then(subscription => ({ registration, subscription }));
|
||||
|
||||
const subscribe = (registration) =>
|
||||
registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()),
|
||||
});
|
||||
|
||||
const unsubscribe = ({ registration, subscription }) =>
|
||||
subscription ? subscription.unsubscribe().then(() => registration) : registration;
|
||||
|
||||
const sendSubscriptionToBackend = (subscription) => {
|
||||
const params = { subscription };
|
||||
|
||||
if (me) {
|
||||
const data = pushNotificationsSetting.get(me);
|
||||
if (data) {
|
||||
params.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
return api().post('/api/web/push_subscriptions', params).then(response => response.data);
|
||||
};
|
||||
|
||||
// Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload
|
||||
const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype);
|
||||
|
||||
export function register () {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(setBrowserSupport(supportsPushNotifications));
|
||||
|
||||
if (supportsPushNotifications) {
|
||||
if (!getApplicationServerKey()) {
|
||||
console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.');
|
||||
return;
|
||||
}
|
||||
|
||||
getRegistration()
|
||||
.then(getPushSubscription)
|
||||
.then(({ registration, subscription }) => {
|
||||
if (subscription !== null) {
|
||||
// We have a subscription, check if it is still valid
|
||||
const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString();
|
||||
const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString();
|
||||
const serverEndpoint = getState().getIn(['push_notifications', 'subscription', 'endpoint']);
|
||||
|
||||
// If the VAPID public key did not change and the endpoint corresponds
|
||||
// to the endpoint saved in the backend, the subscription is valid
|
||||
if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) {
|
||||
return subscription;
|
||||
} else {
|
||||
// Something went wrong, try to subscribe again
|
||||
return unsubscribe({ registration, subscription }).then(subscribe).then(
|
||||
subscription => sendSubscriptionToBackend(subscription));
|
||||
}
|
||||
}
|
||||
|
||||
// No subscription, try to subscribe
|
||||
return subscribe(registration).then(
|
||||
subscription => sendSubscriptionToBackend(subscription));
|
||||
})
|
||||
.then(subscription => {
|
||||
// If we got a PushSubscription (and not a subscription object from the backend)
|
||||
// it means that the backend subscription is valid (and was set during hydration)
|
||||
if (!(subscription instanceof PushSubscription)) {
|
||||
dispatch(setSubscription(subscription));
|
||||
if (me) {
|
||||
pushNotificationsSetting.set(me, { alerts: subscription.alerts });
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.code === 20 && error.name === 'AbortError') {
|
||||
console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.');
|
||||
} else if (error.code === 5 && error.name === 'InvalidCharacterError') {
|
||||
console.error('The VAPID public key seems to be invalid:', getApplicationServerKey());
|
||||
}
|
||||
|
||||
// Clear alerts and hide UI settings
|
||||
dispatch(clearSubscription());
|
||||
if (me) {
|
||||
pushNotificationsSetting.remove(me);
|
||||
}
|
||||
|
||||
return getRegistration()
|
||||
.then(getPushSubscription)
|
||||
.then(unsubscribe);
|
||||
})
|
||||
.catch(console.warn);
|
||||
} else {
|
||||
console.warn('Your browser does not support Web Push Notifications.');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function saveSettings() {
|
||||
return (_, getState) => {
|
||||
const state = getState().get('push_notifications');
|
||||
const subscription = state.get('subscription');
|
||||
const alerts = state.get('alerts');
|
||||
const data = { alerts };
|
||||
|
||||
api().put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
|
||||
data,
|
||||
}).then(() => {
|
||||
if (me) {
|
||||
pushNotificationsSetting.set(me, data);
|
||||
}
|
||||
}).catch(console.warn);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT';
|
||||
export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION';
|
||||
export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION';
|
||||
export const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS';
|
||||
|
||||
export function setBrowserSupport (value) {
|
||||
return {
|
||||
type: SET_BROWSER_SUPPORT,
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
export function setSubscription (subscription) {
|
||||
return {
|
||||
type: SET_SUBSCRIPTION,
|
||||
subscription,
|
||||
};
|
||||
}
|
||||
|
||||
export function clearSubscription () {
|
||||
return {
|
||||
type: CLEAR_SUBSCRIPTION,
|
||||
};
|
||||
}
|
||||
|
||||
export function setAlerts (path, value) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: SET_ALERTS,
|
||||
path,
|
||||
value,
|
||||
});
|
||||
};
|
||||
}
|
||||
89
app/javascript/gabsocial/actions/reports.js
Normal file
89
app/javascript/gabsocial/actions/reports.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import api from '../api';
|
||||
import { openModal, closeModal } from './modal';
|
||||
|
||||
export const REPORT_INIT = 'REPORT_INIT';
|
||||
export const REPORT_CANCEL = 'REPORT_CANCEL';
|
||||
|
||||
export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';
|
||||
export const REPORT_SUBMIT_SUCCESS = 'REPORT_SUBMIT_SUCCESS';
|
||||
export const REPORT_SUBMIT_FAIL = 'REPORT_SUBMIT_FAIL';
|
||||
|
||||
export const REPORT_STATUS_TOGGLE = 'REPORT_STATUS_TOGGLE';
|
||||
export const REPORT_COMMENT_CHANGE = 'REPORT_COMMENT_CHANGE';
|
||||
export const REPORT_FORWARD_CHANGE = 'REPORT_FORWARD_CHANGE';
|
||||
|
||||
export function initReport(account, status) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: REPORT_INIT,
|
||||
account,
|
||||
status,
|
||||
});
|
||||
|
||||
dispatch(openModal('REPORT'));
|
||||
};
|
||||
};
|
||||
|
||||
export function cancelReport() {
|
||||
return {
|
||||
type: REPORT_CANCEL,
|
||||
};
|
||||
};
|
||||
|
||||
export function toggleStatusReport(statusId, checked) {
|
||||
return {
|
||||
type: REPORT_STATUS_TOGGLE,
|
||||
statusId,
|
||||
checked,
|
||||
};
|
||||
};
|
||||
|
||||
export function submitReport() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(submitReportRequest());
|
||||
|
||||
api(getState).post('/api/v1/reports', {
|
||||
account_id: getState().getIn(['reports', 'new', 'account_id']),
|
||||
status_ids: getState().getIn(['reports', 'new', 'status_ids']),
|
||||
comment: getState().getIn(['reports', 'new', 'comment']),
|
||||
forward: getState().getIn(['reports', 'new', 'forward']),
|
||||
}).then(response => {
|
||||
dispatch(closeModal());
|
||||
dispatch(submitReportSuccess(response.data));
|
||||
}).catch(error => dispatch(submitReportFail(error)));
|
||||
};
|
||||
};
|
||||
|
||||
export function submitReportRequest() {
|
||||
return {
|
||||
type: REPORT_SUBMIT_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function submitReportSuccess(report) {
|
||||
return {
|
||||
type: REPORT_SUBMIT_SUCCESS,
|
||||
report,
|
||||
};
|
||||
};
|
||||
|
||||
export function submitReportFail(error) {
|
||||
return {
|
||||
type: REPORT_SUBMIT_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function changeReportComment(comment) {
|
||||
return {
|
||||
type: REPORT_COMMENT_CHANGE,
|
||||
comment,
|
||||
};
|
||||
};
|
||||
|
||||
export function changeReportForward(forward) {
|
||||
return {
|
||||
type: REPORT_FORWARD_CHANGE,
|
||||
forward,
|
||||
};
|
||||
};
|
||||
83
app/javascript/gabsocial/actions/search.js
Normal file
83
app/javascript/gabsocial/actions/search.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import api from '../api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts, importFetchedStatuses } from './importer';
|
||||
|
||||
export const SEARCH_CHANGE = 'SEARCH_CHANGE';
|
||||
export const SEARCH_CLEAR = 'SEARCH_CLEAR';
|
||||
export const SEARCH_SHOW = 'SEARCH_SHOW';
|
||||
|
||||
export const SEARCH_FETCH_REQUEST = 'SEARCH_FETCH_REQUEST';
|
||||
export const SEARCH_FETCH_SUCCESS = 'SEARCH_FETCH_SUCCESS';
|
||||
export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
|
||||
|
||||
export function changeSearch(value) {
|
||||
return {
|
||||
type: SEARCH_CHANGE,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
export function clearSearch() {
|
||||
return {
|
||||
type: SEARCH_CLEAR,
|
||||
};
|
||||
};
|
||||
|
||||
export function submitSearch() {
|
||||
return (dispatch, getState) => {
|
||||
const value = getState().getIn(['search', 'value']);
|
||||
|
||||
if (value.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchSearchRequest());
|
||||
|
||||
api(getState).get('/api/v2/search', {
|
||||
params: {
|
||||
q: value,
|
||||
resolve: true,
|
||||
limit: 5,
|
||||
},
|
||||
}).then(response => {
|
||||
if (response.data.accounts) {
|
||||
dispatch(importFetchedAccounts(response.data.accounts));
|
||||
}
|
||||
|
||||
if (response.data.statuses) {
|
||||
dispatch(importFetchedStatuses(response.data.statuses));
|
||||
}
|
||||
|
||||
dispatch(fetchSearchSuccess(response.data));
|
||||
dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
|
||||
}).catch(error => {
|
||||
dispatch(fetchSearchFail(error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchSearchRequest() {
|
||||
return {
|
||||
type: SEARCH_FETCH_REQUEST,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchSearchSuccess(results) {
|
||||
return {
|
||||
type: SEARCH_FETCH_SUCCESS,
|
||||
results,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchSearchFail(error) {
|
||||
return {
|
||||
type: SEARCH_FETCH_FAIL,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function showSearch() {
|
||||
return {
|
||||
type: SEARCH_SHOW,
|
||||
};
|
||||
};
|
||||
37
app/javascript/gabsocial/actions/settings.js
Normal file
37
app/javascript/gabsocial/actions/settings.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import api from '../api';
|
||||
import { debounce } from 'lodash';
|
||||
import { showAlertForError } from './alerts';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const SETTING_CHANGE = 'SETTING_CHANGE';
|
||||
export const SETTING_SAVE = 'SETTING_SAVE';
|
||||
|
||||
export function changeSetting(path, value) {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: SETTING_CHANGE,
|
||||
path,
|
||||
value,
|
||||
});
|
||||
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
};
|
||||
|
||||
const debouncedSave = debounce((dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
if (getState().getIn(['settings', 'saved'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS();
|
||||
|
||||
api().put('/api/web/settings', { data })
|
||||
.then(() => dispatch({ type: SETTING_SAVE }))
|
||||
.catch(error => dispatch(showAlertForError(error)));
|
||||
}, 5000, { trailing: true });
|
||||
|
||||
export function saveSettings() {
|
||||
return (dispatch, getState) => debouncedSave(dispatch, getState);
|
||||
};
|
||||
328
app/javascript/gabsocial/actions/statuses.js
Normal file
328
app/javascript/gabsocial/actions/statuses.js
Normal file
@@ -0,0 +1,328 @@
|
||||
import api from '../api';
|
||||
import openDB from '../storage/db';
|
||||
import { evictStatus } from '../storage/modifier';
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
|
||||
import { ensureComposeIsVisible } from './compose';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
|
||||
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
|
||||
export const STATUS_FETCH_FAIL = 'STATUS_FETCH_FAIL';
|
||||
|
||||
export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
|
||||
export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
|
||||
export const STATUS_DELETE_FAIL = 'STATUS_DELETE_FAIL';
|
||||
|
||||
export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
|
||||
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
|
||||
export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL';
|
||||
|
||||
export const STATUS_MUTE_REQUEST = 'STATUS_MUTE_REQUEST';
|
||||
export const STATUS_MUTE_SUCCESS = 'STATUS_MUTE_SUCCESS';
|
||||
export const STATUS_MUTE_FAIL = 'STATUS_MUTE_FAIL';
|
||||
|
||||
export const STATUS_UNMUTE_REQUEST = 'STATUS_UNMUTE_REQUEST';
|
||||
export const STATUS_UNMUTE_SUCCESS = 'STATUS_UNMUTE_SUCCESS';
|
||||
export const STATUS_UNMUTE_FAIL = 'STATUS_UNMUTE_FAIL';
|
||||
|
||||
export const STATUS_REVEAL = 'STATUS_REVEAL';
|
||||
export const STATUS_HIDE = 'STATUS_HIDE';
|
||||
|
||||
export const REDRAFT = 'REDRAFT';
|
||||
|
||||
export function fetchStatusRequest(id, skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_REQUEST,
|
||||
id,
|
||||
skipLoading,
|
||||
};
|
||||
};
|
||||
|
||||
function getFromDB(dispatch, getState, accountIndex, index, id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = index.get(id);
|
||||
|
||||
request.onerror = reject;
|
||||
|
||||
request.onsuccess = () => {
|
||||
const promises = [];
|
||||
|
||||
if (!request.result) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(importStatus(request.result));
|
||||
|
||||
if (getState().getIn(['accounts', request.result.account], null) === null) {
|
||||
promises.push(new Promise((accountResolve, accountReject) => {
|
||||
const accountRequest = accountIndex.get(request.result.account);
|
||||
|
||||
accountRequest.onerror = accountReject;
|
||||
accountRequest.onsuccess = () => {
|
||||
if (!request.result) {
|
||||
accountReject();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(importAccount(accountRequest.result));
|
||||
accountResolve();
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
if (request.result.reblog && getState().getIn(['statuses', request.result.reblog], null) === null) {
|
||||
promises.push(getFromDB(dispatch, getState, accountIndex, index, request.result.reblog));
|
||||
}
|
||||
|
||||
resolve(Promise.all(promises));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
const skipLoading = getState().getIn(['statuses', id], null) !== null;
|
||||
|
||||
dispatch(fetchContext(id));
|
||||
|
||||
if (skipLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchStatusRequest(id, skipLoading));
|
||||
|
||||
openDB().then(db => {
|
||||
const transaction = db.transaction(['accounts', 'statuses'], 'read');
|
||||
const accountIndex = transaction.objectStore('accounts').index('id');
|
||||
const index = transaction.objectStore('statuses').index('id');
|
||||
|
||||
return getFromDB(dispatch, getState, accountIndex, index, id).then(() => {
|
||||
db.close();
|
||||
}, error => {
|
||||
db.close();
|
||||
throw error;
|
||||
});
|
||||
}).then(() => {
|
||||
dispatch(fetchStatusSuccess(skipLoading));
|
||||
}, () => api(getState).get(`/api/v1/statuses/${id}`).then(response => {
|
||||
dispatch(importFetchedStatus(response.data));
|
||||
dispatch(fetchStatusSuccess(skipLoading));
|
||||
})).catch(error => {
|
||||
dispatch(fetchStatusFail(id, error, skipLoading));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchStatusSuccess(skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_SUCCESS,
|
||||
skipLoading,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchStatusFail(id, error, skipLoading) {
|
||||
return {
|
||||
type: STATUS_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipLoading,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function redraft(status, raw_text) {
|
||||
return {
|
||||
type: REDRAFT,
|
||||
status,
|
||||
raw_text,
|
||||
};
|
||||
};
|
||||
|
||||
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
let status = getState().getIn(['statuses', id]);
|
||||
|
||||
if (status.get('poll')) {
|
||||
status = status.set('poll', getState().getIn(['polls', status.get('poll')]));
|
||||
}
|
||||
|
||||
dispatch(deleteStatusRequest(id));
|
||||
|
||||
api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
|
||||
evictStatus(id);
|
||||
dispatch(deleteStatusSuccess(id));
|
||||
dispatch(deleteFromTimelines(id));
|
||||
|
||||
if (withRedraft) {
|
||||
dispatch(redraft(status, response.data.text));
|
||||
ensureComposeIsVisible(getState, routerHistory);
|
||||
}
|
||||
}).catch(error => {
|
||||
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 {
|
||||
type: STATUS_DELETE_FAIL,
|
||||
id: id,
|
||||
error: error,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchContext(id) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchContextRequest(id));
|
||||
|
||||
api(getState).get(`/api/v1/statuses/${id}/context`).then(response => {
|
||||
dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants)));
|
||||
dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants));
|
||||
|
||||
}).catch(error => {
|
||||
if (error.response && error.response.status === 404) {
|
||||
dispatch(deleteFromTimelines(id));
|
||||
}
|
||||
|
||||
dispatch(fetchContextFail(id, error));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchContextRequest(id) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_REQUEST,
|
||||
id,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchContextSuccess(id, ancestors, descendants) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_SUCCESS,
|
||||
id,
|
||||
ancestors,
|
||||
descendants,
|
||||
statuses: ancestors.concat(descendants),
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchContextFail(id, error) {
|
||||
return {
|
||||
type: CONTEXT_FETCH_FAIL,
|
||||
id,
|
||||
error,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function muteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(muteStatusRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${id}/mute`).then(() => {
|
||||
dispatch(muteStatusSuccess(id));
|
||||
}).catch(error => {
|
||||
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 {
|
||||
type: STATUS_MUTE_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function unmuteStatus(id) {
|
||||
return (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch(unmuteStatusRequest(id));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => {
|
||||
dispatch(unmuteStatusSuccess(id));
|
||||
}).catch(error => {
|
||||
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 {
|
||||
type: STATUS_UNMUTE_FAIL,
|
||||
id,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export function hideStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
|
||||
return {
|
||||
type: STATUS_HIDE,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
|
||||
export function revealStatus(ids) {
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids];
|
||||
}
|
||||
|
||||
return {
|
||||
type: STATUS_REVEAL,
|
||||
ids,
|
||||
};
|
||||
};
|
||||
24
app/javascript/gabsocial/actions/store.js
Normal file
24
app/javascript/gabsocial/actions/store.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Iterable, fromJS } from 'immutable';
|
||||
import { hydrateCompose } from './compose';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
|
||||
export const STORE_HYDRATE = 'STORE_HYDRATE';
|
||||
export const STORE_HYDRATE_LAZY = 'STORE_HYDRATE_LAZY';
|
||||
|
||||
const convertState = rawState =>
|
||||
fromJS(rawState, (k, v) =>
|
||||
Iterable.isIndexed(v) ? v.toList() : v.toMap());
|
||||
|
||||
export function hydrateStore(rawState) {
|
||||
return dispatch => {
|
||||
const state = convertState(rawState);
|
||||
|
||||
dispatch({
|
||||
type: STORE_HYDRATE,
|
||||
state,
|
||||
});
|
||||
|
||||
dispatch(hydrateCompose());
|
||||
dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
|
||||
};
|
||||
};
|
||||
63
app/javascript/gabsocial/actions/streaming.js
Normal file
63
app/javascript/gabsocial/actions/streaming.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { connectStream } from '../stream';
|
||||
import {
|
||||
updateTimeline,
|
||||
deleteFromTimelines,
|
||||
expandHomeTimeline,
|
||||
connectTimeline,
|
||||
disconnectTimeline,
|
||||
} from './timelines';
|
||||
import { updateNotifications, expandNotifications } from './notifications';
|
||||
import { updateConversations } from './conversations';
|
||||
import { fetchFilters } from './filters';
|
||||
import { getLocale } from '../locales';
|
||||
|
||||
const { messages } = getLocale();
|
||||
|
||||
export function connectTimelineStream (timelineId, path, pollingRefresh = null, accept = null) {
|
||||
|
||||
return connectStream (path, pollingRefresh, (dispatch, getState) => {
|
||||
const locale = getState().getIn(['meta', 'locale']);
|
||||
|
||||
return {
|
||||
onConnect() {
|
||||
dispatch(connectTimeline(timelineId));
|
||||
},
|
||||
|
||||
onDisconnect() {
|
||||
dispatch(disconnectTimeline(timelineId));
|
||||
},
|
||||
|
||||
onReceive (data) {
|
||||
switch(data.event) {
|
||||
case 'update':
|
||||
dispatch(updateTimeline(timelineId, JSON.parse(data.payload), accept));
|
||||
break;
|
||||
case 'delete':
|
||||
dispatch(deleteFromTimelines(data.payload));
|
||||
break;
|
||||
case 'notification':
|
||||
dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
|
||||
break;
|
||||
case 'conversation':
|
||||
dispatch(updateConversations(JSON.parse(data.payload)));
|
||||
break;
|
||||
case 'filters_changed':
|
||||
dispatch(fetchFilters());
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const refreshHomeTimelineAndNotification = (dispatch, done) => {
|
||||
dispatch(expandHomeTimeline({}, () => dispatch(expandNotifications({}, done))));
|
||||
};
|
||||
|
||||
export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
|
||||
export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
|
||||
export const connectPublicStream = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`);
|
||||
export const connectHashtagStream = (id, tag, accept) => connectTimelineStream(`hashtag:${id}`, `hashtag&tag=${tag}`, null, accept);
|
||||
export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
|
||||
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
|
||||
export const connectGroupStream = id => connectTimelineStream(`group:${id}`, `group&group=${id}`);
|
||||
55
app/javascript/gabsocial/actions/suggestions.js
Normal file
55
app/javascript/gabsocial/actions/suggestions.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { me } from 'gabsocial/initial_state';
|
||||
|
||||
export const SUGGESTIONS_FETCH_REQUEST = 'SUGGESTIONS_FETCH_REQUEST';
|
||||
export const SUGGESTIONS_FETCH_SUCCESS = 'SUGGESTIONS_FETCH_SUCCESS';
|
||||
export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL';
|
||||
|
||||
export const SUGGESTIONS_DISMISS = 'SUGGESTIONS_DISMISS';
|
||||
|
||||
export function fetchSuggestions() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchSuggestionsRequest());
|
||||
|
||||
api(getState).get('/api/v1/suggestions').then(response => {
|
||||
dispatch(importFetchedAccounts(response.data));
|
||||
dispatch(fetchSuggestionsSuccess(response.data));
|
||||
}).catch(error => dispatch(fetchSuggestionsFail(error)));
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchSuggestionsRequest() {
|
||||
return {
|
||||
type: SUGGESTIONS_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchSuggestionsSuccess(accounts) {
|
||||
return {
|
||||
type: SUGGESTIONS_FETCH_SUCCESS,
|
||||
accounts,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchSuggestionsFail(error) {
|
||||
return {
|
||||
type: SUGGESTIONS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const dismissSuggestion = accountId => (dispatch, getState) => {
|
||||
if (!me) return;
|
||||
|
||||
dispatch({
|
||||
type: SUGGESTIONS_DISMISS,
|
||||
id: accountId,
|
||||
});
|
||||
|
||||
api(getState).delete(`/api/v1/suggestions/${accountId}`);
|
||||
};
|
||||
150
app/javascript/gabsocial/actions/timelines.js
Normal file
150
app/javascript/gabsocial/actions/timelines.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
import api, { getLinks } from '../api';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
|
||||
|
||||
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
|
||||
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
|
||||
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
|
||||
|
||||
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
|
||||
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
|
||||
|
||||
export function updateTimeline(timeline, status, accept) {
|
||||
return dispatch => {
|
||||
if (typeof accept === 'function' && !accept(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(importFetchedStatus(status));
|
||||
|
||||
dispatch({
|
||||
type: TIMELINE_UPDATE,
|
||||
timeline,
|
||||
status,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function deleteFromTimelines(id) {
|
||||
return (dispatch, getState) => {
|
||||
const accountId = getState().getIn(['statuses', id, 'account']);
|
||||
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]);
|
||||
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
|
||||
|
||||
dispatch({
|
||||
type: TIMELINE_DELETE,
|
||||
id,
|
||||
accountId,
|
||||
references,
|
||||
reblogOf,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export function clearTimeline(timeline) {
|
||||
return (dispatch) => {
|
||||
dispatch({ type: TIMELINE_CLEAR, timeline });
|
||||
};
|
||||
};
|
||||
|
||||
const noOp = () => {};
|
||||
|
||||
const parseTags = (tags = {}, mode) => {
|
||||
return (tags[mode] || []).map((tag) => {
|
||||
return tag.value;
|
||||
});
|
||||
};
|
||||
|
||||
export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
||||
return (dispatch, getState) => {
|
||||
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
|
||||
const isLoadingMore = !!params.max_id;
|
||||
|
||||
if (timeline.get('isLoading')) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) {
|
||||
params.since_id = timeline.getIn(['items', 0]);
|
||||
}
|
||||
|
||||
const isLoadingRecent = !!params.since_id;
|
||||
|
||||
dispatch(expandTimelineRequest(timelineId, isLoadingMore));
|
||||
|
||||
api(getState).get(path, { params }).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore));
|
||||
done();
|
||||
}).catch(error => {
|
||||
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
|
||||
done();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
|
||||
export const expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
|
||||
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
|
||||
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
|
||||
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
|
||||
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: 40 });
|
||||
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
|
||||
export const expandGroupTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { max_id: maxId }, done);
|
||||
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
|
||||
return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
|
||||
max_id: maxId,
|
||||
any: parseTags(tags, 'any'),
|
||||
all: parseTags(tags, 'all'),
|
||||
none: parseTags(tags, 'none'),
|
||||
}, done);
|
||||
};
|
||||
|
||||
export function expandTimelineRequest(timeline, isLoadingMore) {
|
||||
return {
|
||||
type: TIMELINE_EXPAND_REQUEST,
|
||||
timeline,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
|
||||
return {
|
||||
type: TIMELINE_EXPAND_SUCCESS,
|
||||
timeline,
|
||||
statuses,
|
||||
next,
|
||||
partial,
|
||||
isLoadingRecent,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
|
||||
export function expandTimelineFail(timeline, error, isLoadingMore) {
|
||||
return {
|
||||
type: TIMELINE_EXPAND_FAIL,
|
||||
timeline,
|
||||
error,
|
||||
skipLoading: !isLoadingMore,
|
||||
};
|
||||
};
|
||||
|
||||
export function connectTimeline(timeline) {
|
||||
return {
|
||||
type: TIMELINE_CONNECT,
|
||||
timeline,
|
||||
};
|
||||
};
|
||||
|
||||
export function disconnectTimeline(timeline) {
|
||||
return {
|
||||
type: TIMELINE_DISCONNECT,
|
||||
timeline,
|
||||
};
|
||||
};
|
||||
39
app/javascript/gabsocial/actions/trends.js
Normal file
39
app/javascript/gabsocial/actions/trends.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import api from '../api';
|
||||
|
||||
export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST';
|
||||
export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS';
|
||||
export const TRENDS_FETCH_FAIL = 'TRENDS_FETCH_FAIL';
|
||||
|
||||
export function fetchTrends() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(fetchTrendsRequest());
|
||||
|
||||
api(getState).get('/api/v1/trends').then(response => {
|
||||
dispatch(fetchTrendsSuccess(response.data));
|
||||
}).catch(error => dispatch(fetchTrendsFail(error)));
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchTrendsRequest() {
|
||||
return {
|
||||
type: TRENDS_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchTrendsSuccess(tags) {
|
||||
return {
|
||||
type: TRENDS_FETCH_SUCCESS,
|
||||
tags,
|
||||
skipLoading: true,
|
||||
};
|
||||
};
|
||||
|
||||
export function fetchTrendsFail(error) {
|
||||
return {
|
||||
type: TRENDS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
skipAlert: true,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user