Merge branch 'develop' of https://code.gab.com/gab/social/gab-social into feature/frontend_refactor

This commit is contained in:
mgabdev
2020-01-28 11:29:26 -05:00
225 changed files with 5598 additions and 2652 deletions

View File

@@ -1,6 +1,7 @@
import api from '../api';
import { CancelToken, isCancel } from 'axios';
import { throttle } from 'lodash';
import moment from 'moment';
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
import { tagHistory } from '../settings';
import { useEmoji } from './emojis';
@@ -20,6 +21,7 @@ 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_QUOTE = 'COMPOSE_QUOTE';
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
@@ -60,6 +62,8 @@ 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';
export const COMPOSE_SCHEDULED_AT_CHANGE = 'COMPOSE_SCHEDULED_AT_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.' },
@@ -91,6 +95,17 @@ export function replyCompose(status, routerHistory) {
};
};
export function quoteCompose(status, routerHistory) {
return (dispatch, getState) => {
dispatch({
type: COMPOSE_QUOTE,
status: status,
});
dispatch(openModal('COMPOSE'));
};
};
export function cancelReplyCompose() {
return {
type: COMPOSE_REPLY_CANCEL,
@@ -125,6 +140,45 @@ export function directCompose(account, routerHistory) {
};
};
export function handleComposeSubmit(dispatch, getState, response, status) {
if (!dispatch || !getState) return;
const isScheduledStatus = response.data['scheduled_at'] !== undefined;
if (isScheduledStatus) {
dispatch(showAlertForError({
response: {
data: {},
status: 200,
statusText: 'Successfully scheduled status',
}
}));
dispatch(submitComposeSuccess({ ...response.data }));
return;
}
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')) {
let dequeueArgs = {};
if (timelineId === 'community') dequeueArgs.onlyMedia = getState().getIn(['settings', 'community', 'other', 'onlyMedia']);
dispatch(dequeueTimeline(timelineId, null, dequeueArgs));
dispatch(updateTimeline(timelineId, { ...response.data }));
}
};
if (response.data.visibility !== 'direct') {
insertIfOnline('home');
} else if (response.data.in_reply_to_id === null && response.data.visibility === 'public') {
insertIfOnline('community');
insertIfOnline('public');
}
}
export function submitCompose(routerHistory, group) {
return function (dispatch, getState) {
if (!me) return;
@@ -139,9 +193,20 @@ export function submitCompose(routerHistory, group) {
dispatch(submitComposeRequest());
dispatch(closeModal());
api(getState).post('/api/v1/statuses', {
const id = getState().getIn(['compose', 'id']);
const endpoint = id === null
? '/api/v1/statuses'
: `/api/v1/statuses/${id}`;
const method = id === null ? 'post' : 'put';
let scheduled_at = getState().getIn(['compose', 'scheduled_at'], null);
if (scheduled_at !== null) scheduled_at = moment.utc(scheduled_at).toDate();
api(getState)[method](endpoint, {
status,
scheduled_at,
in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
quote_of_id: getState().getIn(['compose', 'quote_of_id'], null),
media_ids: media.map(item => item.get('id')),
sensitive: getState().getIn(['compose', 'sensitive']),
spoiler_text: getState().getIn(['compose', 'spoiler_text'], ''),
@@ -156,33 +221,7 @@ export function submitCompose(routerHistory, group) {
if (response.data.visibility === 'direct' && getState().getIn(['conversations', 'mounted']) <= 0 && routerHistory) {
routerHistory.push('/messages');
}
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')) {
let dequeueArgs = {};
if (timelineId === 'community') dequeueArgs.onlyMedia = getState().getIn(['settings', 'community', 'other', 'onlyMedia']),
dispatch(dequeueTimeline(timelineId, null, dequeueArgs));
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');
}
handleComposeSubmit(dispatch, getState, response, status);
}).catch(function (error) {
dispatch(submitComposeFail(error));
});
@@ -561,3 +600,10 @@ export function changePollSettings(expiresIn, isMultiple) {
isMultiple,
};
};
export function changeScheduledAt(date) {
return {
type: COMPOSE_SCHEDULED_AT_CHANGE,
date,
};
};

View File

@@ -51,6 +51,10 @@ export const GROUP_REMOVE_STATUS_REQUEST = 'GROUP_REMOVE_STATUS_REQUEST';
export const GROUP_REMOVE_STATUS_SUCCESS = 'GROUP_REMOVE_STATUS_SUCCESS';
export const GROUP_REMOVE_STATUS_FAIL = 'GROUP_REMOVE_STATUS_FAIL';
export const GROUP_UPDATE_ROLE_REQUEST = 'GROUP_UPDATE_ROLE_REQUEST';
export const GROUP_UPDATE_ROLE_SUCCESS = 'GROUP_UPDATE_ROLE_SUCCESS';
export const GROUP_UPDATE_ROLE_FAIL = 'GROUP_UPDATE_ROLE_FAIL';
export const fetchGroup = id => (dispatch, getState) => {
if (!me) return;
@@ -521,4 +525,43 @@ export function groupRemoveStatusFail(groupId, id, error) {
id,
error,
};
};
export function updateRole(groupId, id, role) {
return (dispatch, getState) => {
if (!me) return;
dispatch(updateRoleRequest(groupId, id));
api(getState).patch(`/api/v1/groups/${groupId}/accounts?account_id=${id}`, { role }).then(response => {
dispatch(updateRoleSuccess(groupId, id));
}).catch(error => {
dispatch(updateRoleFail(groupId, id, error));
});
};
};
export function updateRoleRequest(groupId, id) {
return {
type: GROUP_REMOVED_ACCOUNTS_CREATE_REQUEST,
groupId,
id,
};
};
export function updateRoleSuccess(groupId, id) {
return {
type: GROUP_REMOVED_ACCOUNTS_CREATE_SUCCESS,
groupId,
id,
};
};
export function updateRoleFail(groupId, id, error) {
return {
type: GROUP_REMOVED_ACCOUNTS_CREATE_FAIL,
groupId,
id,
error,
};
};

View File

@@ -71,6 +71,10 @@ export function importFetchedStatuses(statuses) {
processStatus(status.reblog);
}
if (status.quote && status.quote.id) {
processStatus(status.quote);
}
if (status.poll && status.poll.id) {
pushUnique(polls, normalizePoll(status.poll));
}

View File

@@ -43,13 +43,17 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.reblog = status.reblog.id;
}
if (status.quote && status.quote.id) {
normalStatus.quote = status.quote.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) {
if (normalOldStatus && normalOldStatus.get('content') === normalStatus.content && normalOldStatus.get('spoiler_text') === normalStatus.spoiler_text) {
normalStatus.search_index = normalOldStatus.get('search_index');
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');

View File

@@ -14,6 +14,7 @@ import { unescapeHTML } from '../utils/html';
import { getFilters, regexFromFilters } from '../selectors';
import { me } from 'gabsocial/initial_state';
export const NOTIFICATIONS_INITIALIZE = 'NOTIFICATIONS_INITIALIZE';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
@@ -27,6 +28,7 @@ export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
export const NOTIFICATIONS_MARK_READ = 'NOTIFICATIONS_MARK_READ';
export const MAX_QUEUED_NOTIFICATIONS = 40;
@@ -43,6 +45,12 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
}
};
export function initializeNotifications() {
return {
type: NOTIFICATIONS_INITIALIZE,
};
}
export function updateNotifications(notification, intlMessages, intlLocale) {
return (dispatch, getState) => {
const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
@@ -134,6 +142,7 @@ export function dequeueNotifications() {
dispatch({
type: NOTIFICATIONS_DEQUEUE,
});
dispatch(markReadNotifications());
}
};
@@ -225,10 +234,13 @@ export function clearNotifications() {
};
export function scrollTopNotifications(top) {
return {
type: NOTIFICATIONS_SCROLL_TOP,
top,
};
return (dispatch, getState) => {
dispatch({
type: NOTIFICATIONS_SCROLL_TOP,
top,
});
dispatch(markReadNotifications());
}
};
export function setFilter (filterType) {
@@ -242,3 +254,20 @@ export function setFilter (filterType) {
dispatch(saveSettings());
};
};
export function markReadNotifications() {
return (dispatch, getState) => {
if (!me) return;
const top_notification = parseInt(getState().getIn(['notifications', 'items', 0, 'id']));
const last_read = getState().getIn(['notifications', 'lastRead']);
if (top_notification && top_notification > last_read) {
api(getState).post('/api/v1/notifications/mark_read', {id: top_notification}).then(response => {
dispatch({
type: NOTIFICATIONS_MARK_READ,
notification: top_notification,
});
});
}
}
};

View File

@@ -37,7 +37,6 @@ export function submitSearch() {
params: {
q: value,
resolve: true,
limit: 5,
},
}).then(response => {
if (response.data.accounts) {

View File

@@ -0,0 +1,14 @@
export const SIDEBAR_OPEN = 'SIDEBAR_OPEN';
export const SIDEBAR_CLOSE = 'SIDEBAR_CLOSE';
export function openSidebar() {
return {
type: SIDEBAR_OPEN,
};
};
export function closeSidebar() {
return {
type: SIDEBAR_CLOSE,
};
};

View File

@@ -0,0 +1,16 @@
import api from '../api';
export const STATUS_REVISION_LIST_LOAD = 'STATUS_REVISION_LIST';
export const STATUS_REVISION_LIST_LOAD_SUCCESS = 'STATUS_REVISION_LIST_SUCCESS';
export const STATUS_REVISION_LIST_LOAD_FAIL = 'STATUS_REVISION_LIST_FAIL';
const loadSuccess = data => ({ type: STATUS_REVISION_LIST_LOAD_SUCCESS, payload: data });
const loadFail = e => ({ type: STATUS_REVISION_LIST_LOAD_FAIL, payload: e });
export function load(statusId) {
return (dispatch, getState) => {
api(getState).get(`/api/v1/statuses/${statusId}/revisions`)
.then(res => dispatch(loadSuccess(res.data)))
.catch(e => dispatch(loadFail(e)));
};
}

View File

@@ -3,7 +3,7 @@ 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 { openModal } from './modal';
import { me } from 'gabsocial/initial_state';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
@@ -29,7 +29,7 @@ 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 const STATUS_EDIT = 'STATUS_EDIT';
export function fetchStatusRequest(id, skipLoading) {
return {
@@ -132,15 +132,18 @@ export function fetchStatusFail(id, error, skipLoading) {
};
};
export function redraft(status, raw_text) {
return {
type: REDRAFT,
status,
raw_text,
export function editStatus(status) {
return dispatch => {
dispatch({
type: STATUS_EDIT,
status,
});
dispatch(openModal('COMPOSE'));
};
};
export function deleteStatus(id, routerHistory, withRedraft = false) {
export function deleteStatus(id, routerHistory) {
return (dispatch, getState) => {
if (!me) return;
@@ -156,11 +159,6 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
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));
});
@@ -272,7 +270,7 @@ export function muteStatusFail(id, error) {
export function unmuteStatus(id) {
return (dispatch, getState) => {
if (!me) return;
dispatch(unmuteStatusRequest(id));
api(getState).post(`/api/v1/statuses/${id}/unmute`).then(() => {

View File

@@ -10,6 +10,7 @@ import { updateNotificationsQueue, expandNotifications } from './notifications';
import { updateConversations } from './conversations';
import { fetchFilters } from './filters';
import { getLocale } from '../locales';
import { handleComposeSubmit } from './compose';
const { messages } = getLocale();
@@ -61,3 +62,18 @@ export const connectHashtagStream = (id, tag, accept) => connectTimelineStream
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}`);
export const connectStatusUpdateStream = () => {
return connectStream('statuscard', null, (dispatch, getState) => {
return {
onConnect() {},
onDisconnect() {},
onReceive (data) {
if (!data['event'] || !data['payload']) return;
if (data.event === 'update') {
handleComposeSubmit(dispatch, getState, {data: JSON.parse(data.payload)}, null)
}
},
};
});
}