Commiting

This commit is contained in:
mgabdev
2020-11-15 12:48:32 -06:00
parent 62515bbaee
commit fb612f60c8
1011 changed files with 3507 additions and 49604 deletions

View File

@@ -36,14 +36,6 @@ 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';
@@ -772,72 +764,3 @@ export function rejectFollowRequestFail(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,
};
};

View File

@@ -11,79 +11,69 @@ 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
/**
*
*/
export const fetchBlocks = () => (dispatch, getState) => {
if (!me) return
dispatch(fetchBlocksRequest())
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)))
}
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 const fetchBlocksRequest = () => ({
type: BLOCKS_FETCH_REQUEST,
})
export const fetchBlocksSuccess = (accounts, next) => ({
type: BLOCKS_FETCH_SUCCESS,
accounts,
next,
})
export const fetchBlocksFail = (error) => ({
type: BLOCKS_FETCH_FAIL,
error,
})
/**
*
*/
export const expandBlocks = () => (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['user_lists', 'blocks', me, 'next'])
const isLoading = getState().getIn(['user_lists', 'blocks', me, 'isLoading'])
if (url === null || isLoading) 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 fetchBlocksSuccess(accounts, next) {
return {
type: BLOCKS_FETCH_SUCCESS,
accounts,
next,
}
}
export const expandBlocksRequest = () => ({
type: BLOCKS_EXPAND_REQUEST,
})
export function fetchBlocksFail(error) {
return {
type: BLOCKS_FETCH_FAIL,
error,
}
}
export const expandBlocksSuccess = (accounts, next) => ({
type: BLOCKS_EXPAND_SUCCESS,
accounts,
next,
})
export function expandBlocks() {
return (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['user_lists', 'blocks', me, 'next'])
const isLoading = getState().getIn(['user_lists', 'blocks', me, 'isLoading'])
if (url === null || isLoading) 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,
}
}
export const expandBlocksFail = (error) => ({
type: BLOCKS_EXPAND_FAIL,
error,
})

View File

@@ -10,89 +10,79 @@ export const BOOKMARKED_STATUSES_EXPAND_REQUEST = 'BOOKMARKED_STATUSES_EXPAND_RE
export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS'
export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL'
export function fetchBookmarkedStatuses() {
return (dispatch, getState) => {
if (!me) return
/**
*
*/
export const fetchBookmarkedStatuses = () => (dispatch, getState) => {
if (!me) return
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
return
}
dispatch(fetchBookmarkedStatusesRequest())
api(getState).get('/api/v1/bookmarks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(fetchBookmarkedStatusesFail(error))
})
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
return
}
dispatch(fetchBookmarkedStatusesRequest())
api(getState).get('/api/v1/bookmarks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(fetchBookmarkedStatusesFail(error))
})
}
export function fetchBookmarkedStatusesRequest() {
return {
type: BOOKMARKED_STATUSES_FETCH_REQUEST,
skipLoading: true,
const fetchBookmarkedStatusesRequest = () => ({
type: BOOKMARKED_STATUSES_FETCH_REQUEST,
skipLoading: true,
})
const fetchBookmarkedStatusesSuccess = (statuses, next) => ({
type: BOOKMARKED_STATUSES_FETCH_SUCCESS,
statuses,
next,
skipLoading: true,
})
const fetchBookmarkedStatusesFail = (error) => ({
type: BOOKMARKED_STATUSES_FETCH_FAIL,
error,
skipLoading: true,
})
/**
*
*/
export const expandBookmarkedStatuses = () => (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null)
if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
return
}
dispatch(expandBookmarkedStatusesRequest())
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(expandBookmarkedStatusesFail(error))
})
}
export function fetchBookmarkedStatusesSuccess(statuses, next) {
return {
type: BOOKMARKED_STATUSES_FETCH_SUCCESS,
statuses,
next,
skipLoading: true,
}
}
const expandBookmarkedStatusesRequest = () => ({
type: BOOKMARKED_STATUSES_EXPAND_REQUEST,
})
export function fetchBookmarkedStatusesFail(error) {
return {
type: BOOKMARKED_STATUSES_FETCH_FAIL,
error,
skipLoading: true,
}
}
const expandBookmarkedStatusesSuccess = (statuses, next) => ({
type: BOOKMARKED_STATUSES_EXPAND_SUCCESS,
statuses,
next,
})
export function expandBookmarkedStatuses() {
return (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['status_lists', 'bookmarks', 'next'], null)
if (url === null || getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
return
}
dispatch(expandBookmarkedStatusesRequest())
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(expandBookmarkedStatusesFail(error))
})
}
}
export function expandBookmarkedStatusesRequest() {
return {
type: BOOKMARKED_STATUSES_EXPAND_REQUEST,
}
}
export function expandBookmarkedStatusesSuccess(statuses, next) {
return {
type: BOOKMARKED_STATUSES_EXPAND_SUCCESS,
statuses,
next,
}
}
export function expandBookmarkedStatusesFail(error) {
return {
type: BOOKMARKED_STATUSES_EXPAND_FAIL,
error,
}
}
const expandBookmarkedStatusesFail = (error) => ({
type: BOOKMARKED_STATUSES_EXPAND_FAIL,
error,
})

View File

@@ -1,25 +1,19 @@
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 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 const fetchBundleRequest = (skipLoading) => ({
type: BUNDLE_FETCH_REQUEST,
skipLoading,
})
export function fetchBundleSuccess(skipLoading) {
return {
type: BUNDLE_FETCH_SUCCESS,
skipLoading,
};
}
export const fetchBundleSuccess = (skipLoading) => ({
type: BUNDLE_FETCH_SUCCESS,
skipLoading,
})
export function fetchBundleFail(error, skipLoading) {
return {
type: BUNDLE_FETCH_FAIL,
error,
skipLoading,
};
}
export const fetchBundleFail = (error, skipLoading) => ({
type: BUNDLE_FETCH_FAIL,
error,
skipLoading,
})

View File

@@ -1,23 +1,23 @@
import api from '../api';
import api from '../api'
import { FormattedMessage } from 'react-intl'
import { CancelToken, isCancel } from 'axios';
import { CancelToken, isCancel } from 'axios'
import throttle from 'lodash.throttle'
import moment from 'moment-mini'
import { isMobile } from '../utils/is_mobile'
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light';
import { search as emojiSearch } from '../components/emoji/emoji_mart_search_light'
import { urlRegex } from '../features/ui/util/url_regex'
import { tagHistory } from '../settings';
import { tagHistory } from '../settings'
import { joinGroup } from './groups'
import { useEmoji } from './emojis';
import resizeImage from '../utils/resize_image';
import { importFetchedAccounts } from './importer';
import { useEmoji } from './emojis'
import resizeImage from '../utils/resize_image'
import { importFetchedAccounts } from './importer'
import {
updateTimelineQueue,
updateTimeline,
} from './timelines'
// import { showAlert, showAlertForError } from './alerts';
import { defineMessages } from 'react-intl';
import { openModal, closeModal } from './modal';
// import { showAlert, showAlertForError } from './alerts'
import { defineMessages } from 'react-intl'
import { openModal, closeModal } from './modal'
import {
STATUS_EXPIRATION_OPTION_5_MINUTES,
STATUS_EXPIRATION_OPTION_60_MINUTES,
@@ -26,57 +26,58 @@ import {
STATUS_EXPIRATION_OPTION_3_DAYS,
STATUS_EXPIRATION_OPTION_7_DAYS,
} from '../constants'
import { me } from '../initial_state';
import { me } from '../initial_state'
import { makeGetStatus } from '../selectors'
let cancelFetchComposeSuggestionsAccounts;
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_QUOTE = 'COMPOSE_QUOTE';
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
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_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_QUOTE = 'COMPOSE_QUOTE'
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL'
export const COMPOSE_MENTION = 'COMPOSE_MENTION'
export const COMPOSE_RESET = 'COMPOSE_RESET'
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_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_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE';
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_MOUNT = 'COMPOSE_MOUNT';
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT';
export const COMPOSE_TAG_HISTORY_UPDATE = 'COMPOSE_TAG_HISTORY_UPDATE'
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_MOUNT = 'COMPOSE_MOUNT'
export const COMPOSE_UNMOUNT = 'COMPOSE_UNMOUNT'
export const COMPOSE_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT';
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_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_EMOJI_INSERT = 'COMPOSE_EMOJI_INSERT'
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';
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_SCHEDULED_AT_CHANGE = 'COMPOSE_SCHEDULED_AT_CHANGE';
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'
export const COMPOSE_SCHEDULED_AT_CHANGE = 'COMPOSE_SCHEDULED_AT_CHANGE'
export const COMPOSE_EXPIRES_AT_CHANGE = 'COMPOSE_EXPIRES_AT_CHANGE'
@@ -87,15 +88,15 @@ export const COMPOSE_CLEAR = 'COMPOSE_CLEAR'
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);
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');
routerHistory.push('/posts/new')
}
};
}
export function changeCompose(text, markdown, replyId, isStandalone, caretPosition) {
return function (dispatch, getState) {
@@ -566,13 +567,11 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
};
};
export function readyComposeSuggestionsAccounts(token, accounts) {
return {
type: COMPOSE_SUGGESTIONS_READY,
token,
accounts,
};
};
export const readyComposeSuggestionsAccounts = (token, accounts) => ({
type: COMPOSE_SUGGESTIONS_READY,
token,
accounts,
})
export function selectComposeSuggestion(position, token, suggestion, path) {
return (dispatch, getState) => {
@@ -730,31 +729,35 @@ export function removePollOption(index) {
};
};
export function changePollSettings(expiresIn, isMultiple) {
return {
type: COMPOSE_POLL_SETTINGS_CHANGE,
expiresIn,
isMultiple,
};
};
/**
*
*/
export const changePollSettings = (expiresIn, isMultiple) => ({
type: COMPOSE_POLL_SETTINGS_CHANGE,
expiresIn,
isMultiple,
})
export function changeScheduledAt(date) {
return {
type: COMPOSE_SCHEDULED_AT_CHANGE,
date,
};
};
/**
*
*/
export const changeScheduledAt = (date) => ({
type: COMPOSE_SCHEDULED_AT_CHANGE,
date,
})
export function changeExpiresAt(value) {
return {
type: COMPOSE_EXPIRES_AT_CHANGE,
value,
}
}
/**
*
*/
export const changeExpiresAt = (value) => ({
type: COMPOSE_EXPIRES_AT_CHANGE,
value,
})
export function changeRichTextEditorControlsVisibility(open) {
return {
type: COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY,
open,
}
}
/**
*
*/
export const changeRichTextEditorControlsVisibility = (open) => ({
type: COMPOSE_RICH_TEXT_EDITOR_CONTROLS_VISIBILITY,
open,
})

View File

@@ -1,89 +0,0 @@
import api, { getLinks } from '../api';
import {
importFetchedAccounts,
importFetchedStatuses,
importFetchedStatus,
} from './importer';
import { me } from '../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,
});
};

View File

@@ -1,40 +1,32 @@
import api from '../api';
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 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());
export const fetchCustomEmojis = () => (dispatch, getState) => {
dispatch(fetchCustomEmojisRequest())
api(getState).get('/api/v1/custom_emojis').then(response => {
dispatch(fetchCustomEmojisSuccess(response.data));
}).catch(error => {
dispatch(fetchCustomEmojisFail(error));
});
};
};
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,
};
};
const fetchCustomEmojisRequest = () => ({
type: CUSTOM_EMOJIS_FETCH_REQUEST,
skipLoading: true,
})
export function fetchCustomEmojisSuccess(custom_emojis) {
return {
type: CUSTOM_EMOJIS_FETCH_SUCCESS,
custom_emojis,
skipLoading: true,
};
};
const fetchCustomEmojisSuccess = (custom_emojis) => ({
type: CUSTOM_EMOJIS_FETCH_SUCCESS,
custom_emojis,
skipLoading: true,
})
export function fetchCustomEmojisFail(error) {
return {
type: CUSTOM_EMOJIS_FETCH_FAIL,
error,
skipLoading: true,
};
};
const fetchCustomEmojisFail = (error) => ({
type: CUSTOM_EMOJIS_FETCH_FAIL,
error,
skipLoading: true,
})

View File

@@ -1,14 +1,12 @@
import { saveSettings } from './settings';
import { saveSettings } from './settings'
export const EMOJI_USE = 'EMOJI_USE';
export const EMOJI_USE = 'EMOJI_USE'
export function useEmoji(emoji) {
return dispatch => {
dispatch({
type: EMOJI_USE,
emoji,
});
export const useEmoji = (emoji) => (dispatch) => {
dispatch({
type: EMOJI_USE,
emoji,
});
dispatch(saveSettings());
};
};
dispatch(saveSettings())
}

View File

@@ -10,89 +10,79 @@ export const FAVORITED_STATUSES_EXPAND_REQUEST = 'FAVORITED_STATUSES_EXPAND_REQU
export const FAVORITED_STATUSES_EXPAND_SUCCESS = 'FAVORITED_STATUSES_EXPAND_SUCCESS'
export const FAVORITED_STATUSES_EXPAND_FAIL = 'FAVORITED_STATUSES_EXPAND_FAIL'
export function fetchFavoritedStatuses() {
return (dispatch, getState) => {
if (!me) return
/**
*
*/
export const fetchFavoritedStatuses = () => (dispatch, getState) => {
if (!me) return
if (getState().getIn(['status_lists', 'favorites', 'isLoading'])) {
return
}
dispatch(fetchFavoritedStatusesRequest())
api(getState).get('/api/v1/favourites').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(fetchFavoritedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(fetchFavoritedStatusesFail(error))
})
if (getState().getIn(['status_lists', 'favorites', 'isLoading'])) {
return
}
dispatch(fetchFavoritedStatusesRequest())
api(getState).get('/api/v1/favourites').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(fetchFavoritedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(fetchFavoritedStatusesFail(error))
})
}
export function fetchFavoritedStatusesRequest() {
return {
type: FAVORITED_STATUSES_FETCH_REQUEST,
skipLoading: true,
const fetchFavoritedStatusesRequest = () => ({
type: FAVORITED_STATUSES_FETCH_REQUEST,
skipLoading: true,
})
const fetchFavoritedStatusesSuccess = (statuses, next) => ({
type: FAVORITED_STATUSES_FETCH_SUCCESS,
statuses,
next,
skipLoading: true,
})
const fetchFavoritedStatusesFail = (error) => ({
type: FAVORITED_STATUSES_FETCH_FAIL,
error,
skipLoading: true,
})
/**
*
*/
export const expandFavoritedStatuses = () => (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['status_lists', 'favorites', 'next'], null)
if (url === null || getState().getIn(['status_lists', 'favorites', 'isLoading'])) {
return
}
dispatch(expandFavoritedStatusesRequest())
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(expandFavoritedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(expandFavoritedStatusesFail(error))
})
}
export function fetchFavoritedStatusesSuccess(statuses, next) {
return {
type: FAVORITED_STATUSES_FETCH_SUCCESS,
statuses,
next,
skipLoading: true,
}
}
const expandFavoritedStatusesRequest = () => ({
type: FAVORITED_STATUSES_EXPAND_REQUEST,
})
export function fetchFavoritedStatusesFail(error) {
return {
type: FAVORITED_STATUSES_FETCH_FAIL,
error,
skipLoading: true,
}
}
const expandFavoritedStatusesSuccess = (statuses, next) => ({
type: FAVORITED_STATUSES_EXPAND_SUCCESS,
statuses,
next,
})
export function expandFavoritedStatuses() {
return (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['status_lists', 'favorites', 'next'], null)
if (url === null || getState().getIn(['status_lists', 'favorites', 'isLoading'])) {
return
}
dispatch(expandFavoritedStatusesRequest())
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedStatuses(response.data))
dispatch(expandFavoritedStatusesSuccess(response.data, next ? next.uri : null))
}).catch(error => {
dispatch(expandFavoritedStatusesFail(error))
})
}
}
export function expandFavoritedStatusesRequest() {
return {
type: FAVORITED_STATUSES_EXPAND_REQUEST,
}
}
export function expandFavoritedStatusesSuccess(statuses, next) {
return {
type: FAVORITED_STATUSES_EXPAND_SUCCESS,
statuses,
next,
}
}
export function expandFavoritedStatusesFail(error) {
return {
type: FAVORITED_STATUSES_EXPAND_FAIL,
error,
}
}
const expandFavoritedStatusesFail = (error) => ({
type: FAVORITED_STATUSES_EXPAND_FAIL,
error,
})

View File

@@ -1,29 +1,40 @@
import api from '../api';
import { me } from '../initial_state';
import api from '../api'
import { me } from '../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 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;
if (!me) return
dispatch({
type: FILTERS_FETCH_REQUEST,
skipLoading: true,
});
dispatch(fetchFiltersRequest())
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,
}));
};
api(getState).get('/api/v1/filters').then(({ data }) => {
dispatch(fetchFiltersSuccess(data))
}).catch((err) => {
dispatch(fetchFiltersFail(err))
})
}
const fetchFiltersRequest = () => ({
type: FILTERS_FETCH_REQUEST,
skipLoading: true,
})
const fetchFiltersSuccess = (filters) => ({
type: FILTERS_FETCH_SUCCESS,
filters,
skipLoading: true,
})
const fetchFiltersFail = (err) => ({
type: FILTERS_FETCH_FAIL,
err,
skipLoading: true,
skipAlert: true,
})

View File

@@ -4,6 +4,9 @@ export const GROUP_CATEGORIES_FETCH_REQUEST = 'GROUP_CATEGORIES_FETCH_REQUEST'
export const GROUP_CATEGORIES_FETCH_SUCCESS = 'GROUP_CATEGORIES_FETCH_SUCCESS'
export const GROUP_CATEGORIES_FETCH_FAIL = 'GROUP_CATEGORIES_FETCH_FAIL'
/**
*
*/
export const fetchGroupCategories = () => (dispatch, getState) => {
dispatch(fetchGroupCategoriesRequest())
@@ -12,16 +15,16 @@ export const fetchGroupCategories = () => (dispatch, getState) => {
.catch(err => dispatch(fetchGroupCategoriesFail(err)))
}
export const fetchGroupCategoriesRequest = () => ({
const fetchGroupCategoriesRequest = () => ({
type: GROUP_CATEGORIES_FETCH_REQUEST,
})
export const fetchGroupCategoriesSuccess = (categories) => ({
const fetchGroupCategoriesSuccess = (categories) => ({
type: GROUP_CATEGORIES_FETCH_SUCCESS,
categories,
})
export const fetchGroupCategoriesFail = (error) => ({
const fetchGroupCategoriesFail = (error) => ({
type: GROUP_CATEGORIES_FETCH_FAIL,
error,
})

View File

@@ -22,6 +22,9 @@ export const GROUP_EDITOR_IS_VISIBLE_CHANGE = 'GROUP_EDITOR_IS_VISIBLE_CHANGE'
export const GROUP_EDITOR_RESET = 'GROUP_EDITOR_RESET'
export const GROUP_EDITOR_SETUP = 'GROUP_EDITOR_SETUP'
/**
*
*/
export const submit = (routerHistory) => (dispatch, getState) => {
if (!me) return
@@ -50,16 +53,19 @@ export const submit = (routerHistory) => (dispatch, getState) => {
}
if (groupId === null) {
dispatch(create(options, routerHistory))
dispatch(createGroup(options, routerHistory))
} else {
dispatch(update(groupId, options, routerHistory))
dispatch(updateGroup(groupId, options, routerHistory))
}
}
const create = (options, routerHistory) => (dispatch, getState) => {
/**
*
*/
const createGroup = (options, routerHistory) => (dispatch, getState) => {
if (!me) return
dispatch(createRequest())
dispatch(createGroupRequest())
const formData = new FormData()
formData.append('title', options.title)
@@ -81,28 +87,31 @@ const create = (options, routerHistory) => (dispatch, getState) => {
'Content-Type': 'multipart/form-data'
}
}).then(({ data }) => {
dispatch(createSuccess(data))
dispatch(createGroupSuccess(data))
routerHistory.push(`/groups/${data.id}`)
}).catch(err => dispatch(createFail(err)))
}).catch((err) => dispatch(createGroupFail(err)))
}
export const createRequest = (id) => ({
const createGroupRequest = (id) => ({
type: GROUP_CREATE_REQUEST,
id,
})
export const createSuccess = (group) => ({
const createSuccess = (group) => ({
type: GROUP_CREATE_SUCCESS,
group,
})
export const createFail = (error) => ({
const createFail = (error) => ({
type: GROUP_CREATE_FAIL,
error,
})
const update = (groupId, options, routerHistory) => (dispatch, getState) => {
/**
*
*/
const updateGroup = (groupId, options, routerHistory) => (dispatch, getState) => {
if (!me) return
dispatch(updateRequest())
@@ -130,26 +139,29 @@ const update = (groupId, options, routerHistory) => (dispatch, getState) => {
'Content-Type': 'multipart/form-data'
}
}).then(({ data }) => {
dispatch(updateSuccess(data))
dispatch(updateGroupSuccess(data))
routerHistory.push(`/groups/${data.id}`)
}).catch(err => dispatch(updateFail(err)))
}).catch((err) => dispatch(updateGroupFail(err)))
}
export const updateRequest = (id) => ({
const updateGroupRequest = (id) => ({
type: GROUP_UPDATE_REQUEST,
id,
})
export const updateSuccess = (group) => ({
const updateGroupSuccess = (group) => ({
type: GROUP_UPDATE_SUCCESS,
group,
})
export const updateFail = (error) => ({
const updateGroupFail = (error) => ({
type: GROUP_UPDATE_FAIL,
error,
})
/**
*
*/
export const resetEditor = () => ({
type: GROUP_EDITOR_RESET
})

View File

@@ -107,6 +107,11 @@ export const importGroup = (group) => (dispatch) => {
dispatch(fetchGroupSuccess(group))
}
export const importGroups = (groups) => (dispatch) => {
if (!Array.isArray(groups)) return
groups.map((group) => dispatch(fetchGroupSuccess(group)))
}
export const fetchGroup = (id) => (dispatch, getState) => {
dispatch(fetchGroupRelationships([id]));

View File

@@ -1,17 +1,13 @@
export const HEIGHT_CACHE_SET = 'HEIGHT_CACHE_SET';
export const HEIGHT_CACHE_CLEAR = 'HEIGHT_CACHE_CLEAR';
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 const setHeight = (key, id, height) => ({
type: HEIGHT_CACHE_SET,
key,
id,
height,
})
export function clearHeight () {
return {
type: HEIGHT_CACHE_CLEAR,
};
};
export const clearHeight = () => ({
type: HEIGHT_CACHE_CLEAR,
})

View File

@@ -1,12 +1,18 @@
import { normalizeAccount, normalizeStatus, normalizePoll } from './normalizer';
import isObject from 'lodash.isobject'
import {
normalizeAccount,
normalizeStatus,
normalizePoll,
} from './normalizer'
import { fetchContext } from '../statuses'
import { importGroups } from '../groups'
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';
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)) {
@@ -14,34 +20,39 @@ function pushUnique(array, object) {
}
}
export function importAccount(account) {
return { type: ACCOUNT_IMPORT, account };
}
export const importAccount = (account) => ({
type: ACCOUNT_IMPORT,
account,
})
export function importAccounts(accounts) {
return { type: ACCOUNTS_IMPORT, accounts };
}
export const importAccounts = (accounts) => ({
type: ACCOUNTS_IMPORT,
accounts,
})
export function importStatus(status) {
return { type: STATUS_IMPORT, status };
}
export const importStatus = (status) => ({
type: STATUS_IMPORT,
status,
})
export function importStatuses(statuses) {
return { type: STATUSES_IMPORT, statuses };
}
export const importStatuses = (statuses) => ({
type: STATUSES_IMPORT,
statuses,
})
export function importPolls(polls) {
return { type: POLLS_IMPORT, polls };
}
export const importPolls = (polls) => ({
type: POLLS_IMPORT,
polls,
})
export function importFetchedAccount(account) {
export const importFetchedAccount = (account) => {
return importFetchedAccounts([account]);
}
export function importFetchedAccounts(accounts) {
export const importFetchedAccounts = (accounts) => {
const normalAccounts = [];
function processAccount(account) {
const processAccount = (account) => {
pushUnique(normalAccounts, normalizeAccount(account));
if (account.moved) {
@@ -54,47 +65,48 @@ export function importFetchedAccounts(accounts) {
return importAccounts(normalAccounts);
}
export function importFetchedStatus(status) {
export const importFetchedStatus = (status) => {
return importFetchedStatuses([status]);
}
export function importFetchedStatuses(statuses) {
return (dispatch, getState) => {
const accounts = [];
const normalStatuses = [];
const polls = [];
export const importFetchedStatuses = (statuses) => (dispatch, getState) => {
const accounts = []
const normalStatuses = []
const polls = []
const groups = []
function processStatus(status) {
pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id])));
pushUnique(accounts, status.account);
const processStatus = (status) => {
pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id])))
if (status.reblog && status.reblog.id) {
processStatus(status.reblog);
}
if (status.quote && status.quote.id) {
processStatus(status.quote);
}
if (status.poll && status.poll.id) {
pushUnique(polls, normalizePoll(status.poll));
}
if (isObject(status.account)) pushUnique(accounts, status.account)
if (isObject(status.group)) pushUnique(groups, status.group)
if (status.reblog && status.reblog.id) {
processStatus(status.reblog)
}
statuses.forEach(processStatus);
if (status.quote && status.quote.id) {
processStatus(status.quote)
}
dispatch(importPolls(polls));
dispatch(importFetchedAccounts(accounts));
dispatch(importStatuses(normalStatuses));
};
if (status.poll && status.poll.id) {
pushUnique(polls, normalizePoll(status.poll))
}
}
statuses.forEach(processStatus)
dispatch(importPolls(polls))
dispatch(importFetchedAccounts(accounts))
dispatch(importStatuses(normalStatuses))
dispatch(importGroups(groups))
}
export function importFetchedPoll(poll) {
return dispatch => {
dispatch(importPolls([normalizePoll(poll)]));
};
export const importFetchedPoll = (poll) => (dispatch) => {
dispatch(importPolls([normalizePoll(poll)]))
}
export function importErrorWhileFetchingAccountByUsername(username) {
return { type: ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP, username };
};
export const importErrorWhileFetchingAccountByUsername = (username) => ({
type: ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP,
username
})

View File

@@ -39,7 +39,7 @@ export function normalizeAccount(account) {
export function normalizeStatus(status, normalOldStatus) {
const normalStatus = { ...status };
normalStatus.account = status.account.id;
normalStatus.account = status.account_id || status.account.id;
if (status.reblog && status.reblog.id) {
normalStatus.reblog = status.reblog.id;
@@ -53,6 +53,10 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.poll = status.poll.id;
}
if (!!status.group || !!status.group_id) {
normalStatus.group = status.group_id || status.group.id;
}
// Only calculate these values when status first encountered
// Otherwise keep the ones already in the reducer
if (normalOldStatus && normalOldStatus.get('content') === normalStatus.content && normalOldStatus.get('spoiler_text') === normalStatus.spoiler_text) {

View File

@@ -1,34 +1,35 @@
import api from '../api';
import { importFetchedAccounts, importFetchedStatus } from './importer';
import { me } from '../initial_state';
import api from '../api'
import { importFetchedAccounts, importFetchedStatus } from './importer'
import { updateStatusStats } from './statuses'
import { me } from '../initial_state'
export const REPOST_REQUEST = 'REPOST_REQUEST';
export const REPOST_SUCCESS = 'REPOST_SUCCESS';
export const REPOST_FAIL = 'REPOST_FAIL';
export const REPOST_REQUEST = 'REPOST_REQUEST'
export const REPOST_SUCCESS = 'REPOST_SUCCESS'
export const REPOST_FAIL = 'REPOST_FAIL'
export const FAVORITE_REQUEST = 'FAVORITE_REQUEST';
export const FAVORITE_SUCCESS = 'FAVORITE_SUCCESS';
export const FAVORITE_FAIL = 'FAVORITE_FAIL';
export const FAVORITE_REQUEST = 'FAVORITE_REQUEST'
export const FAVORITE_SUCCESS = 'FAVORITE_SUCCESS'
export const FAVORITE_FAIL = 'FAVORITE_FAIL'
export const UNREPOST_REQUEST = 'UNREPOST_REQUEST';
export const UNREPOST_SUCCESS = 'UNREPOST_SUCCESS';
export const UNREPOST_FAIL = 'UNREPOST_FAIL';
export const UNREPOST_REQUEST = 'UNREPOST_REQUEST'
export const UNREPOST_SUCCESS = 'UNREPOST_SUCCESS'
export const UNREPOST_FAIL = 'UNREPOST_FAIL'
export const UNFAVORITE_REQUEST = 'UNFAVORITE_REQUEST';
export const UNFAVORITE_SUCCESS = 'UNFAVORITE_SUCCESS';
export const UNFAVORITE_FAIL = 'UNFAVORITE_FAIL';
export const UNFAVORITE_REQUEST = 'UNFAVORITE_REQUEST'
export const UNFAVORITE_SUCCESS = 'UNFAVORITE_SUCCESS'
export const UNFAVORITE_FAIL = 'UNFAVORITE_FAIL'
export const REPOSTS_FETCH_REQUEST = 'REPOSTS_FETCH_REQUEST';
export const REPOSTS_FETCH_SUCCESS = 'REPOSTS_FETCH_SUCCESS';
export const REPOSTS_FETCH_FAIL = 'REPOSTS_FETCH_FAIL';
export const REPOSTS_FETCH_REQUEST = 'REPOSTS_FETCH_REQUEST'
export const REPOSTS_FETCH_SUCCESS = 'REPOSTS_FETCH_SUCCESS'
export const REPOSTS_FETCH_FAIL = 'REPOSTS_FETCH_FAIL'
export const PIN_REQUEST = 'PIN_REQUEST';
export const PIN_SUCCESS = 'PIN_SUCCESS';
export const PIN_FAIL = 'PIN_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 const UNPIN_REQUEST = 'UNPIN_REQUEST'
export const UNPIN_SUCCESS = 'UNPIN_SUCCESS'
export const UNPIN_FAIL = 'UNPIN_FAIL'
export const BOOKMARK_REQUEST = 'BOOKMARK_REQUEST'
export const BOOKMARK_SUCCESS = 'BOOKMARK_SUCCESS'
@@ -38,392 +39,342 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARK_REQUEST'
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARK_SUCCESS'
export const UNBOOKMARK_FAIL = 'UNBOOKMARK_FAIL'
export const LIKES_FETCH_REQUEST = 'LIKES_FETCH_REQUEST';
export const LIKES_FETCH_SUCCESS = 'LIKES_FETCH_SUCCESS';
export const LIKES_FETCH_FAIL = 'LIKES_FETCH_FAIL';
export const LIKES_FETCH_REQUEST = 'LIKES_FETCH_REQUEST'
export const LIKES_FETCH_SUCCESS = 'LIKES_FETCH_SUCCESS'
export const LIKES_FETCH_FAIL = 'LIKES_FETCH_FAIL'
export function repost(status) {
return function (dispatch, getState) {
if (!me) return;
/**
*
*/
export const repost = (status) => (dispatch, getState) => {
if (!me) return
dispatch(repostRequest(status));
dispatch(repostRequest(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(repostSuccess(status));
}).catch(function (error) {
dispatch(repostFail(status, error));
});
};
};
export function unrepost(status) {
return (dispatch, getState) => {
if (!me) return;
dispatch(unrepostRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
dispatch(importFetchedStatus(response.data));
dispatch(unrepostSuccess(status));
}).catch(error => {
dispatch(unrepostFail(status, error));
});
};
};
export function repostRequest(status) {
return {
type: REPOST_REQUEST,
status: status,
skipLoading: true,
};
};
export function repostSuccess(status) {
return {
type: REPOST_SUCCESS,
status: status,
skipLoading: true,
};
};
export function repostFail(status, error) {
return {
type: REPOST_FAIL,
status: status,
error: error,
skipLoading: true,
};
};
export function unrepostRequest(status) {
return {
type: UNREPOST_REQUEST,
status: status,
skipLoading: true,
};
};
export function unrepostSuccess(status) {
return {
type: UNREPOST_SUCCESS,
status: status,
skipLoading: true,
};
};
export function unrepostFail(status, error) {
return {
type: UNREPOST_FAIL,
status: status,
error: error,
skipLoading: true,
};
};
export function favorite(status) {
return function (dispatch, getState) {
if (!me) return;
dispatch(favoriteRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
dispatch(importFetchedStatus(response.data));
dispatch(favoriteSuccess(status));
}).catch(function (error) {
dispatch(favoriteFail(status, error));
});
};
};
export function unfavorite(status) {
return (dispatch, getState) => {
if (!me) return;
dispatch(unfavoriteRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
dispatch(importFetchedStatus(response.data));
dispatch(unfavoriteSuccess(status));
}).catch(error => {
dispatch(unfavoriteFail(status, error));
});
};
};
export function favoriteRequest(status) {
return {
type: FAVORITE_REQUEST,
status: status,
skipLoading: true,
};
};
export function favoriteSuccess(status) {
return {
type: FAVORITE_SUCCESS,
status: status,
skipLoading: true,
};
};
export function favoriteFail(status, error) {
return {
type: FAVORITE_FAIL,
status: status,
error: error,
skipLoading: true,
};
};
export function unfavoriteRequest(status) {
return {
type: UNFAVORITE_REQUEST,
status: status,
skipLoading: true,
};
};
export function unfavoriteSuccess(status) {
return {
type: UNFAVORITE_SUCCESS,
status: status,
skipLoading: true,
};
};
export function unfavoriteFail(status, error) {
return {
type: UNFAVORITE_FAIL,
status: status,
error: error,
skipLoading: true,
};
};
export function fetchReposts(id) {
return (dispatch, getState) => {
if (!me) return;
dispatch(fetchRepostsRequest(id));
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then(response => {
dispatch(importFetchedAccounts(response.data));
dispatch(fetchRepostsSuccess(id, response.data));
}).catch(error => {
dispatch(fetchRepostsFail(id, error));
});
};
};
export function fetchRepostsRequest(id) {
return {
type: REPOSTS_FETCH_REQUEST,
id,
};
};
export function fetchRepostsSuccess(id, accounts) {
return {
type: REPOSTS_FETCH_SUCCESS,
id,
accounts,
};
};
export function fetchRepostsFail(id, error) {
return {
type: REPOSTS_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,
};
};
export function fetchLikes(id) {
return (dispatch, getState) => {
dispatch(fetchLikesRequest(id));
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then(response => {
dispatch(importFetchedAccounts(response.data));
dispatch(fetchLikesSuccess(id, response.data));
}).catch(error => {
dispatch(fetchLikesFail(id, error));
});
};
};
export function fetchLikesRequest(id) {
return {
type: LIKES_FETCH_REQUEST,
id,
};
};
export function fetchLikesSuccess(id, accounts) {
return {
type: LIKES_FETCH_SUCCESS,
id,
accounts,
};
};
export function fetchLikesFail(id, error) {
return {
type: LIKES_FETCH_FAIL,
error,
};
};
export function bookmark(status) {
return function (dispatch, getState) {
dispatch(bookmarkRequest(status));
api(getState).post(`/api/v1/statuses/${status.get('id')}/bookmark`).then(function (response) {
dispatch(importFetchedStatus(response.data));
dispatch(bookmarkSuccess(status, response.data));
}).catch(function (error) {
dispatch(bookmarkFail(status, error))
})
}
api(getState).post(`/api/v1/statuses/${status.get('id')}/reblog`).then((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(repostSuccess(status))
}).catch((error) => {
dispatch(repostFail(status, error))
})
}
export function unbookmark(status) {
return (dispatch, getState) => {
dispatch(unbookmarkRequest(status))
export const repostRequest = (status) => ({
type: REPOST_REQUEST,
status: status,
skipLoading: true,
})
api(getState).post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then(response => {
dispatch(importFetchedStatus(response.data))
dispatch(unbookmarkSuccess(status, response.data))
}).catch(error => {
dispatch(unbookmarkFail(status, error))
})
}
export const repostSuccess = (status) => ({
type: REPOST_SUCCESS,
status: status,
skipLoading: true,
})
export const repostFail = (status, error) => ({
type: REPOST_FAIL,
status: status,
error: error,
skipLoading: true,
})
/**
*
*/
export const unrepost = (status) => (dispatch, getState) => {
if (!me) return
dispatch(unrepostRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(unrepostSuccess(status))
}).catch((error) => {
dispatch(unrepostFail(status, error))
})
}
export function bookmarkRequest(status) {
return {
type: BOOKMARK_REQUEST,
status: status,
}
export const unrepostRequest = (status) => ({
type: UNREPOST_REQUEST,
status: status,
skipLoading: true,
})
export const unrepostSuccess = (status) => ({
type: UNREPOST_SUCCESS,
status: status,
skipLoading: true,
})
export const unrepostFail = (status, error) => ({
type: UNREPOST_FAIL,
status: status,
error: error,
skipLoading: true,
})
/**
*
*/
export const favorite = (status) => (dispatch, getState) => {
if (!me) return
dispatch(favoriteRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then((response) => {
dispatch(updateStatusStats(response.data))
dispatch(favoriteSuccess(status))
}).catch((error) => {
dispatch(favoriteFail(status, error))
})
}
export function bookmarkSuccess(status, response) {
return {
type: BOOKMARK_SUCCESS,
status: status,
response: response,
}
export const favoriteRequest = (status) => ({
type: FAVORITE_REQUEST,
status: status,
skipLoading: true,
})
export const favoriteSuccess = (status) => ({
type: FAVORITE_SUCCESS,
status: status,
skipLoading: true,
})
export const favoriteFail = (status, error) => ({
type: FAVORITE_FAIL,
status: status,
error: error,
skipLoading: true,
})
/**
*
*/
export const unfavorite = (status) => (dispatch, getState) => {
if (!me) return
dispatch(unfavoriteRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(unfavoriteSuccess(status))
}).catch((error) => {
dispatch(unfavoriteFail(status, error))
})
}
export function bookmarkFail(status, error) {
return {
type: BOOKMARK_FAIL,
status: status,
error: error,
}
export const unfavoriteRequest = (status) => ({
type: UNFAVORITE_REQUEST,
status: status,
skipLoading: true,
})
export const unfavoriteSuccess = (status) => ({
type: UNFAVORITE_SUCCESS,
status: status,
skipLoading: true,
})
export const unfavoriteFail = (status, error) => ({
type: UNFAVORITE_FAIL,
status: status,
error: error,
skipLoading: true,
})
/**
*
*/
export const fetchReposts = (id) => (dispatch, getState) => {
if (!me) return
dispatch(fetchRepostsRequest(id))
api(getState).get(`/api/v1/statuses/${id}/reblogged_by`).then((response) => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchRepostsSuccess(id, response.data))
}).catch((error) => {
dispatch(fetchRepostsFail(id, error))
})
}
export function unbookmarkRequest(status) {
return {
type: UNBOOKMARK_REQUEST,
status: status,
}
export const fetchRepostsRequest = (id) => ({
type: REPOSTS_FETCH_REQUEST,
id,
})
export const fetchRepostsSuccess = (id, accounts) => ({
type: REPOSTS_FETCH_SUCCESS,
id,
accounts,
})
export const fetchRepostsFail = (id, error) => ({
type: REPOSTS_FETCH_FAIL,
error,
})
/**
*
*/
export const pin = (status) => (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 unbookmarkSuccess(status, response) {
return {
type: UNBOOKMARK_SUCCESS,
status: status,
response: response,
}
export const pinRequest = (status) => ({
type: PIN_REQUEST,
status,
skipLoading: true,
})
export const pinSuccess = (status) => ({
type: PIN_SUCCESS,
status,
skipLoading: true,
})
export const pinFail = (status, error) => ({
type: PIN_FAIL,
status,
error,
skipLoading: true,
})
/**
*
*/
export const unpin = (status) => (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 unbookmarkFail(status, error) {
return {
type: UNBOOKMARK_FAIL,
status: status,
error: error,
}
}
export const unpinRequest = (status) => ({
type: UNPIN_REQUEST,
status,
skipLoading: true,
})
export const unpinSuccess = (status) => ({
type: UNPIN_SUCCESS,
status,
skipLoading: true,
})
export const unpinFail = (status, error) => ({
type: UNPIN_FAIL,
status,
error,
skipLoading: true,
})
/**
*
*/
export const fetchLikes = (id) => (dispatch, getState) => {
dispatch(fetchLikesRequest(id))
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then((response) => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchLikesSuccess(id, response.data))
}).catch((error) => {
dispatch(fetchLikesFail(id, error))
})
}
export const fetchLikesRequest = (id) => ({
type: LIKES_FETCH_REQUEST,
id,
})
export const fetchLikesSuccess = (id, accounts) => ({
type: LIKES_FETCH_SUCCESS,
id,
accounts,
})
export const fetchLikesFail = (id, error) => ({
type: LIKES_FETCH_FAIL,
error,
})
/**
*
*/
export const bookmark = (status) => (dispatch, getState) => {
dispatch(bookmarkRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/bookmark`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(bookmarkSuccess(status, response.data))
}).catch((error) => {
dispatch(bookmarkFail(status, error))
})
}
export const bookmarkRequest = (status) => ({
type: BOOKMARK_REQUEST,
status: status,
})
export const bookmarkSuccess = (status, response) => ({
type: BOOKMARK_SUCCESS,
status: status,
response: response,
})
export const bookmarkFail = (status, error) => ({
type: BOOKMARK_FAIL,
status: status,
error: error,
})
/**
*
*/
export const unbookmark = (status) => (dispatch, getState) => {
dispatch(unbookmarkRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(unbookmarkSuccess(status, response.data))
}).catch((error) => {
dispatch(unbookmarkFail(status, error))
})
}
export const unbookmarkRequest = (status) => ({
type: UNBOOKMARK_REQUEST,
status: status,
})
export const unbookmarkSuccess = (status, response) => ({
type: UNBOOKMARK_SUCCESS,
status: status,
response: response,
})
export const unbookmarkFail = (status, error) => ({
type: UNBOOKMARK_FAIL,
status: status,
error: error,
})

View File

@@ -10,6 +10,17 @@ export const POPULAR_LINKS_FETCH_FAIL = 'POPULAR_LINKS_FETCH_FAIL'
export const IMPORT_LINK_CARDS = 'IMPORT_LINK_CARDS'
/**
*
*/
export const importLinkCards = (cards) => ({
type: IMPORT_LINK_CARDS,
cards,
})
/**
*
*/
export const fetchLinkCard = (cardId) => (dispatch, getState) => {
//If card exists, don't refetch
const card = getState().getIn(['links', 'items', `${cardId}`])
@@ -39,11 +50,9 @@ export const fetchLinkCardFail = (error, cardId) => ({
cardId,
})
export const importLinkCards = (cards) => ({
type: IMPORT_LINK_CARDS,
cards,
})
/**
*
*/
export const fetchPopularLinks = () => (dispatch, getState) => {
const isFetched = getState().getIn(['links', 'popular', 'isFetched'], false)
if (isFetched) return
@@ -56,16 +65,16 @@ export const fetchPopularLinks = () => (dispatch, getState) => {
.catch((err) => dispatch(fetchPopularLinksFail(err)))
}
export const fetchPopularLinksRequest = () => ({
const fetchPopularLinksRequest = () => ({
type: POPULAR_LINKS_FETCH_REQUEST,
})
export const fetchPopularLinksSuccess = (cards) => ({
const fetchPopularLinksSuccess = (cards) => ({
type: POPULAR_LINKS_FETCH_SUCCESS,
cards,
})
export const fetchPopularLinksFail = (error) => ({
const fetchPopularLinksFail = (error) => ({
type: POPULAR_LINKS_FETCH_FAIL,
error,
})

View File

@@ -1,88 +1,92 @@
import api from '../api';
import { importFetchedAccounts } from './importer';
// import { showAlertForError } from './alerts';
import api from '../api'
import { importFetchedAccounts } from './importer'
// import { showAlertForError } from './alerts'
import { me } from '../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 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 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_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_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_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_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_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_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_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_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_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 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;
/**
*
*/
export const fetchList = (id) => (dispatch, getState) => {
if (!me) return
if (getState().getIn(['lists', id])) {
return;
}
if (getState().getIn(['lists', id])) return
dispatch(fetchListRequest(id));
dispatch(fetchListRequest(id))
api(getState).get(`/api/v1/lists/${id}`)
.then(({ data }) => dispatch(fetchListSuccess(data)))
.catch(err => dispatch(fetchListFail(id, err)));
};
.catch((err) => dispatch(fetchListFail(id, err)))
}
export const fetchListRequest = id => ({
const fetchListRequest = id => ({
type: LIST_FETCH_REQUEST,
id,
});
})
export const fetchListSuccess = list => ({
const fetchListSuccess = list => ({
type: LIST_FETCH_SUCCESS,
list,
});
})
export const fetchListFail = (id, error) => ({
const fetchListFail = (id, error) => ({
type: LIST_FETCH_FAIL,
id,
error,
});
})
/**
*
*/
export const fetchLists = () => (dispatch, getState) => {
return new Promise((resolve, reject) => {
dispatch(fetchListsRequest());
dispatch(fetchListsRequest())
if (!me) return reject()
api(getState).get('/api/v1/lists').then(({ data }) => {
dispatch(fetchListsSuccess(data))
@@ -90,307 +94,367 @@ export const fetchLists = () => (dispatch, getState) => {
}).catch((err) => {
dispatch(fetchListsFail(err))
return reject()
});
})
})
};
}
export const fetchListsRequest = () => ({
const fetchListsRequest = () => ({
type: LISTS_FETCH_REQUEST,
});
})
export const fetchListsSuccess = (lists) => ({
const fetchListsSuccess = (lists) => ({
type: LISTS_FETCH_SUCCESS,
lists,
});
})
export const fetchListsFail = (error) => ({
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']);
const listId = getState().getIn(['listEditor', 'listId'])
const title = getState().getIn(['listEditor', 'title'])
if (listId === null) {
dispatch(createList(title, shouldReset));
dispatch(createList(title, shouldReset))
} else {
dispatch(updateList(listId, title, shouldReset));
dispatch(updateList(listId, title, shouldReset))
}
};
}
/**
*
*/
export const setupListEditor = (listId) => (dispatch, getState) => {
dispatch({
type: LIST_EDITOR_SETUP,
list: getState().getIn(['lists', listId]),
});
})
dispatch(fetchListAccounts(listId));
};
dispatch(fetchListAccounts(listId))
}
/**
*
*/
export const changeListEditorTitle = (value) => ({
type: LIST_EDITOR_TITLE_CHANGE,
value,
});
})
/**
*
*/
export const createList = (title, shouldReset) => (dispatch, getState) => {
if (!me) return;
if (!me) return
dispatch(createListRequest());
dispatch(createListRequest())
api(getState).post('/api/v1/lists', { title }).then(({ data }) => {
dispatch(createListSuccess(data));
dispatch(createListSuccess(data))
if (shouldReset) {
dispatch(resetListEditor());
dispatch(resetListEditor())
}
}).catch(err => dispatch(createListFail(err)));
};
}).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;
if (!me) return
dispatch(updateListRequest(id));
dispatch(updateListRequest(id))
api(getState).put(`/api/v1/lists/${id}`, { title }).then(({ data }) => {
dispatch(updateListSuccess(data));
dispatch(updateListSuccess(data))
if (shouldReset) {
dispatch(resetListEditor());
dispatch(resetListEditor())
}
}).catch(err => dispatch(updateListFail(id, err)));
};
}).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;
if (!me) return
dispatch(deleteListRequest(id));
dispatch(deleteListRequest(id))
api(getState).delete(`/api/v1/lists/${id}`)
.then(() => dispatch(deleteListSuccess(id)))
.catch(err => dispatch(deleteListFail(id, err)));
};
.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;
if (!me) return
dispatch(fetchListAccountsRequest(listId));
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)));
};
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;
if (!me) return
const params = {
q,
resolve: false,
limit: 25,
};
}
api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => {
dispatch(importFetchedAccounts(data));
dispatch(fetchListSuggestionsReady(q, data));
dispatch(importFetchedAccounts(data))
dispatch(fetchListSuggestionsReady(q, data))
})
// }).catch(error => dispatch(showAlertForError(error)));
};
// }).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));
};
dispatch(addToList(getState().getIn(['listEditor', 'listId']), accountId))
}
/**
*
*/
export const addToList = (listId, accountId) => (dispatch, getState) => {
if (!me) return;
if (!me) return
dispatch(addToListRequest(listId, accountId));
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)));
};
.catch((err) => dispatch(addToListFail(listId, accountId, err)))
}
export const addToListRequest = (listId, accountId) => ({
const addToListRequest = (listId, accountId) => ({
type: LIST_EDITOR_ADD_REQUEST,
listId,
accountId,
});
})
export const addToListSuccess = (listId, accountId) => ({
const addToListSuccess = (listId, accountId) => ({
type: LIST_EDITOR_ADD_SUCCESS,
listId,
accountId,
});
})
export const addToListFail = (listId, accountId, error) => ({
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));
};
dispatch(removeFromList(getState().getIn(['listEditor', 'listId']), accountId))
}
/**
*
*/
export const removeFromList = (listId, accountId) => (dispatch, getState) => {
if (!me) return;
if (!me) return
dispatch(removeFromListRequest(listId, accountId));
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)));
};
.catch((err) => dispatch(removeFromListFail(listId, accountId, err)))
}
export const removeFromListRequest = (listId, accountId) => ({
const removeFromListRequest = (listId, accountId) => ({
type: LIST_EDITOR_REMOVE_REQUEST,
listId,
accountId,
});
})
export const removeFromListSuccess = (listId, accountId) => ({
const removeFromListSuccess = (listId, accountId) => ({
type: LIST_EDITOR_REMOVE_SUCCESS,
listId,
accountId,
});
})
export const removeFromListFail = (listId, accountId, error) => ({
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));
};
})
dispatch(fetchLists())
dispatch(fetchAccountLists(accountId))
}
/**
*
*/
export const fetchAccountLists = (accountId) => (dispatch, getState) => {
if (!me) return;
if (!me) return
dispatch(fetchAccountListsRequest(accountId));
dispatch(fetchAccountListsRequest(accountId))
api(getState).get(`/api/v1/accounts/${accountId}/lists`)
.then(({ data }) => dispatch(fetchAccountListsSuccess(accountId, data)))
.catch(err => dispatch(fetchAccountListsFail(accountId, err)));
};
.catch((err) => dispatch(fetchAccountListsFail(accountId, err)))
}
export const fetchAccountListsRequest = (id) => ({
const fetchAccountListsRequest = (id) => ({
type:LIST_ADDER_LISTS_FETCH_REQUEST,
id,
});
})
export const fetchAccountListsSuccess = (id, lists) => ({
const fetchAccountListsSuccess = (id, lists) => ({
type: LIST_ADDER_LISTS_FETCH_SUCCESS,
id,
lists,
});
})
export const fetchAccountListsFail = (id, err) => ({
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'])));
};
dispatch(addToList(listId, getState().getIn(['listAdder', 'accountId'])))
}
/**
*
*/
export const removeFromListAdder = (listId) => (dispatch, getState) => {
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])));
};
dispatch(removeFromList(listId, getState().getIn(['listAdder', 'accountId'])))
}

View File

@@ -1,16 +1,12 @@
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 const openModal = (type, props) => ({
type: MODAL_OPEN,
modalType: type,
modalProps: props,
})
export function closeModal() {
return {
type: MODAL_CLOSE,
}
}
export const closeModal = () => ({
type: MODAL_CLOSE,
})

View File

@@ -1,103 +1,95 @@
import api, { getLinks } from '../api';
import { fetchRelationships } from './accounts';
import { importFetchedAccounts } from './importer';
import { openModal } from './modal';
import { me } from '../initial_state';
import api, { getLinks } from '../api'
import { fetchRelationships } from './accounts'
import { importFetchedAccounts } from './importer'
import { openModal } from './modal'
import { me } from '../initial_state'
import { MODAL_MUTE } from '../constants'
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_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_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_INIT_MODAL = 'MUTES_INIT_MODAL'
export function fetchMutes() {
return (dispatch, getState) => {
if (!me) return;
/**
*
*/
export const fetchMutes = () => (dispatch, getState) => {
if (!me) return
dispatch(fetchMutesRequest());
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)));
};
};
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,
};
};
const fetchMutesRequest = () => ({
type: MUTES_FETCH_REQUEST,
})
export function fetchMutesSuccess(accounts, next) {
return {
type: MUTES_FETCH_SUCCESS,
accounts,
next,
};
};
const fetchMutesSuccess = (accounts, next) => ({
type: MUTES_FETCH_SUCCESS,
accounts,
next,
})
export function fetchMutesFail(error) {
return {
type: MUTES_FETCH_FAIL,
error,
};
};
const fetchMutesFail = (error) => ({
type: MUTES_FETCH_FAIL,
error,
})
export function expandMutes() {
return (dispatch, getState) => {
if (!me) return;
const url = getState().getIn(['user_lists', 'mutes', me, 'next']);
const isLoading = getState().getIn(['user_lists', 'mutes', me, 'isLoading']);
/**
*
*/
export const expandMutes = () => (dispatch, getState) => {
if (!me) return
const url = getState().getIn(['user_lists', 'mutes', me, 'next'])
const isLoading = getState().getIn(['user_lists', 'mutes', me, 'isLoading'])
if (url === null || isLoading) return
if (url === null || isLoading) return
dispatch(expandMutesRequest());
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)));
};
};
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,
};
};
const expandMutesRequest = () => ({
type: MUTES_EXPAND_REQUEST,
})
export function expandMutesSuccess(accounts, next) {
return {
type: MUTES_EXPAND_SUCCESS,
accounts,
next,
};
};
const expandMutesSuccess = (accounts, next) => ({
type: MUTES_EXPAND_SUCCESS,
accounts,
next,
})
export function expandMutesFail(error) {
return {
type: MUTES_EXPAND_FAIL,
error,
};
};
export const expandMutesFail = (error) => ({
type: MUTES_EXPAND_FAIL,
error,
})
export function initMuteModal(account) {
return dispatch => {
dispatch({
type: MUTES_INIT_MODAL,
account,
});
/**
*
*/
export const initMuteModal = (account) => (dispatch) => {
dispatch({
type: MUTES_INIT_MODAL,
account,
})
dispatch(openModal('MUTE'));
};
dispatch(openModal(MODAL_MUTE))
}

View File

@@ -30,7 +30,6 @@ export const fetchGabTrends = () => (dispatch, getState) => {
dispatch(fetchGabTrendsRequest())
const url = 'https://trends.gab.com/partner'
// api(getState).get(`/api/v1/gab_trends?type=partner`).then((response) => {
axios.get(url).then((response) => {
dispatch(fetchGabTrendsSuccess(response.data))
}).catch((error) => {
@@ -65,7 +64,6 @@ export const expandGabTrendsFeed = (feedId) => (dispatch, getState) => {
dispatch(expandGabTrendsFeedRequest(feedId))
const url = `https://trends.gab.com/feed/${feedId}?fmt=json&p=${page}`
// api(getState).get(`/api/v1/gab_trends?type=rss&page=${page}&feedId=${feedId}`).then((response) => {
axios.get(url).then((response) => {
dispatch(expandGabTrendsFeedSuccess(response.data.rssFeedItems, feedId, response.data.pagination.p))
}).catch((error) => {
@@ -102,8 +100,7 @@ export const fetchGabNews = () => (dispatch, getState) => {
dispatch(fetchGabNewsRequest())
const url = 'https://news.gab.com/feed/?feed=json'
// api(getState).get(`/api/v1/gab_trends?type=news`).then((response) => {
axios.get(url).then((response) => {
axios.get(url).then((response) => {
dispatch(fetchGabNewsSuccess(response.data.items))
}).catch((error) => {
dispatch(fetchGabNewsFail(error))

View File

@@ -1,278 +1,283 @@
import api, { getLinks } from '../api';
import IntlMessageFormat from 'intl-messageformat';
import { fetchRelationships } from './accounts';
import api, { getLinks } from '../api'
import IntlMessageFormat from 'intl-messageformat'
import { fetchRelationships } from './accounts'
import {
importFetchedAccount,
importFetchedAccounts,
importFetchedStatus,
importFetchedStatuses,
} from './importer';
import { defineMessages } from 'react-intl';
import { List as ImmutableList } from 'immutable';
import { unescapeHTML } from '../utils/html';
import { getFilters, regexFromFilters } from '../selectors';
import { me } from '../initial_state';
} from './importer'
import { defineMessages } from 'react-intl'
import { List as ImmutableList } from 'immutable'
import { unescapeHTML } from '../utils/html'
import { getFilters, regexFromFilters } from '../selectors'
import { me } from '../initial_state'
import { NOTIFICATION_FILTERS } from '../constants'
export const NOTIFICATIONS_INITIALIZE = 'NOTIFICATIONS_INITIALIZE';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE';
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE';
export const NOTIFICATIONS_INITIALIZE = 'NOTIFICATIONS_INITIALIZE'
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'
export const NOTIFICATIONS_UPDATE_QUEUE = 'NOTIFICATIONS_UPDATE_QUEUE'
export const NOTIFICATIONS_DEQUEUE = 'NOTIFICATIONS_DEQUEUE'
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_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_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 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
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);
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id)
if (accountIds.length > 0) {
dispatch(fetchRelationships(accountIds));
dispatch(fetchRelationships(accountIds))
}
}
export function initializeNotifications() {
return {
type: NOTIFICATIONS_INITIALIZE,
}
}
export function updateNotifications(notification, intlMessages, intlLocale) {
return (dispatch, getState) => {
const showInColumn = getState().getIn(['notifications', 'filter', notification.type], true);
if (showInColumn) {
dispatch(importFetchedAccount(notification.account));
if (notification.status) {
dispatch(importFetchedStatus(notification.status));
}
dispatch({
type: NOTIFICATIONS_UPDATE,
notification,
});
fetchRelatedRelationships(dispatch, [notification]);
}
};
};
export function updateNotificationsQueue(notification, intlMessages, intlLocale, curPath) {
return (dispatch, getState) => {
// : todo :
// const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
const filters = getFilters(getState(), { contextType: 'notifications' });
let filtered = false;
const isOnNotificationsPage = curPath === '/notifications';
if (notification.type === 'mention') {
const regex = regexFromFilters(filters);
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
filtered = regex && regex.test(searchIndex);
}
// Desktop notifications
// : todo :
// 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();
// });
// }
if (isOnNotificationsPage) {
dispatch({
type: NOTIFICATIONS_UPDATE_QUEUE,
notification,
intlMessages,
intlLocale,
});
} else {
dispatch(updateNotifications(notification, intlMessages, intlLocale));
}
}
};
export function forceDequeueNotifications() {
return (dispatch,) => {
dispatch({
type: NOTIFICATIONS_DEQUEUE,
})
}
}
export function dequeueNotifications() {
return (dispatch, getState) => {
const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList());
const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0);
if (totalQueuedNotificationsCount === 0) {
return;
} else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
queuedNotifications.forEach(block => {
dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale));
});
} else {
dispatch(expandNotifications());
}
dispatch({
type: NOTIFICATIONS_DEQUEUE,
});
dispatch(markReadNotifications());
}
};
const excludeTypesFromFilter = filter => {
const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention', 'poll']);
return allTypes.filterNot(item => item === filter).toJS();
};
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
/**
*
*/
export const initializeNotifications = () => ({
type: NOTIFICATIONS_INITIALIZE,
})
const onlyVerified = getState().getIn(['notifications', 'filter', 'onlyVerified'])
const onlyFollowing = getState().getIn(['notifications', 'filter', 'onlyFollowing'])
const activeFilter = getState().getIn(['notifications', 'filter', 'active'])
const notifications = getState().get('notifications')
const isLoadingMore = !!maxId
/**
*
*/
export const updateNotifications = (notification, intlMessages, intlLocale) => (dispatch, getState) => {
const showInColumn = getState().getIn(['notifications', 'filter', notification.type], true)
if (notifications.get('isLoading') || activeFilter === 'follow_requests') {
done();
return;
if (showInColumn) {
dispatch(importFetchedAccount(notification.account))
if (notification.status) {
dispatch(importFetchedStatus(notification.status))
}
const params = {
max_id: maxId,
exclude_types: activeFilter === 'all' ? null : excludeTypesFromFilter(activeFilter),
}
if (!!onlyVerified) params.only_verified = onlyVerified
if (!!onlyFollowing) params.only_following = onlyFollowing
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 (dispatch, getState) => {
dispatch({
type: NOTIFICATIONS_SCROLL_TOP,
top,
});
dispatch(markReadNotifications());
}
};
export function setFilter(path, value) {
return (dispatch) => {
if (path === 'active' && NOTIFICATION_FILTERS.indexOf(value) === -1) return
dispatch({
type: NOTIFICATIONS_FILTER_SET,
path: path,
value: value,
type: NOTIFICATIONS_UPDATE,
notification,
})
dispatch(expandNotifications())
fetchRelatedRelationships(dispatch, [notification])
}
}
export function markReadNotifications() {
return (dispatch, getState) => {
if (!me) return
const topNotification = parseInt(getState().getIn(['notifications', 'items', 0, 'id']))
const lastReadId = getState().getIn(['notifications', 'lastReadId'])
/**
*
*/
export const updateNotificationsQueue = (notification, intlMessages, intlLocale, curPath) => (dispatch, getState) => {
// : todo :
// const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true)
const filters = getFilters(getState(), { contextType: 'notifications' })
if (topNotification && topNotification > lastReadId && lastReadId !== -1) {
api(getState).post('/api/v1/notifications/mark_read', {
id: topNotification
}).then(() => {
dispatch({
type: NOTIFICATIONS_MARK_READ,
notification: topNotification,
})
let filtered = false
const isOnNotificationsPage = curPath === '/notifications'
if (notification.type === 'mention') {
const regex = regexFromFilters(filters)
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content)
filtered = regex && regex.test(searchIndex)
}
// Desktop notifications
// : todo :
// 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()
// })
// }
if (isOnNotificationsPage) {
dispatch({
type: NOTIFICATIONS_UPDATE_QUEUE,
notification,
intlMessages,
intlLocale,
})
} else {
dispatch(updateNotifications(notification, intlMessages, intlLocale))
}
}
/**
*
*/
export const forceDequeueNotifications = () => (dispatch) => {
dispatch({
type: NOTIFICATIONS_DEQUEUE,
})
}
/**
*
*/
export const dequeueNotifications = () => (dispatch, getState) => {
const queuedNotifications = getState().getIn(['notifications', 'queuedNotifications'], ImmutableList())
const totalQueuedNotificationsCount = getState().getIn(['notifications', 'totalQueuedNotificationsCount'], 0)
if (totalQueuedNotificationsCount === 0) {
return
} else if (totalQueuedNotificationsCount > 0 && totalQueuedNotificationsCount <= MAX_QUEUED_NOTIFICATIONS) {
queuedNotifications.forEach((block) => {
dispatch(updateNotifications(block.notification, block.intlMessages, block.intlLocale))
})
} else {
dispatch(expandNotifications())
}
dispatch({
type: NOTIFICATIONS_DEQUEUE,
})
dispatch(markReadNotifications())
}
/**
*
*/
export const expandNotifications = ({ maxId } = {}, done = noOp) => (dispatch, getState) => {
if (!me) return
const onlyVerified = getState().getIn(['notifications', 'filter', 'onlyVerified'])
const onlyFollowing = getState().getIn(['notifications', 'filter', 'onlyFollowing'])
const activeFilter = getState().getIn(['notifications', 'filter', 'active'])
const notifications = getState().get('notifications')
const isLoadingMore = !!maxId
if (notifications.get('isLoading') || notifications.get('isError')|| activeFilter === 'follow_requests') {
done()
return
}
const params = {
max_id: maxId,
exclude_types: activeFilter === 'all' ? null : excludeTypesFromFilter(activeFilter),
}
if (!!onlyVerified) params.only_verified = onlyVerified
if (!!onlyFollowing) params.only_following = onlyFollowing
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 const expandNotificationsRequest = (isLoadingMore) => ({
type: NOTIFICATIONS_EXPAND_REQUEST,
skipLoading: !isLoadingMore,
})
export const expandNotificationsSuccess = (notifications, next, isLoadingMore) => ({
type: NOTIFICATIONS_EXPAND_SUCCESS,
notifications,
next,
skipLoading: !isLoadingMore,
})
export const expandNotificationsFail = (error, isLoadingMore) => ({
type: NOTIFICATIONS_EXPAND_FAIL,
error,
skipLoading: !isLoadingMore,
})
/**
*
*/
// : todo : implement with alert/warning
export const clearNotifications = () => (dispatch, getState) => {
if (!me) return
dispatch({
type: NOTIFICATIONS_CLEAR,
})
api(getState).post('/api/v1/notifications/clear')
}
/**
*
*/
export const scrollTopNotifications = (top) => (dispatch, getState) => {
dispatch({
type: NOTIFICATIONS_SCROLL_TOP,
top,
})
dispatch(markReadNotifications())
}
/**
*
*/
export const setFilter = (path, value) => (dispatch) => {
if (path === 'active' && NOTIFICATION_FILTERS.indexOf(value) === -1) return
dispatch({
type: NOTIFICATIONS_FILTER_SET,
path: path,
value: value,
})
dispatch(expandNotifications())
}
/**
*
*/
export const markReadNotifications = () => (dispatch, getState) => {
if (!me) return
const topNotification = parseInt(getState().getIn(['notifications', 'items', 0, 'id']))
const lastReadId = getState().getIn(['notifications', 'lastReadId'])
if (topNotification && topNotification > lastReadId && lastReadId !== -1) {
api(getState).post('/api/v1/notifications/mark_read', {
id: topNotification
}).then(() => {
dispatch({
type: NOTIFICATIONS_MARK_READ,
notification: topNotification,
})
}
})
}
}

View File

@@ -1,6 +0,0 @@
import { changeSetting, saveSettings } from './settings'
export const saveShownOnboarding = () => (dispatch) => {
dispatch(changeSetting(['shownOnboarding'], true))
dispatch(saveSettings())
}

View File

@@ -1,60 +1,66 @@
import api from '../api';
import { importFetchedPoll } from './importer';
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_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 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());
dispatch(voteRequest())
api(getState).post(`/api/v1/polls/${pollId}/votes`, { choices })
.then(({ data }) => {
dispatch(importFetchedPoll(data));
dispatch(voteSuccess(data));
dispatch(importFetchedPoll(data))
dispatch(voteSuccess(data))
})
.catch(err => dispatch(voteFail(err)));
};
.catch(err => dispatch(voteFail(err)))
}
const voteRequest = () => ({
type: POLL_VOTE_REQUEST,
})
const voteSuccess = (poll) => ({
type: POLL_VOTE_SUCCESS,
poll,
})
const voteFail = (error) => ({
type: POLL_VOTE_FAIL,
error,
})
/**
*
*/
export const fetchPoll = pollId => (dispatch, getState) => {
dispatch(fetchPollRequest());
dispatch(fetchPollRequest())
api(getState).get(`/api/v1/polls/${pollId}`)
.then(({ data }) => {
dispatch(importFetchedPoll(data));
dispatch(fetchPollSuccess(data));
dispatch(importFetchedPoll(data))
dispatch(fetchPollSuccess(data))
})
.catch(err => dispatch(fetchPollFail(err)));
};
.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 = () => ({
const fetchPollRequest = () => ({
type: POLL_FETCH_REQUEST,
});
})
export const fetchPollSuccess = poll => ({
const fetchPollSuccess = (poll) => ({
type: POLL_FETCH_SUCCESS,
poll,
});
})
export const fetchPollFail = error => ({
const fetchPollFail = (error) => ({
type: POLL_FETCH_FAIL,
error,
});
})

View File

@@ -1,29 +1,23 @@
export const POPOVER_OPEN = 'POPOVER_OPEN'
export const POPOVER_CLOSE = 'POPOVER_CLOSE'
export function openPopover(type, props) {
return function (dispatch, getState) {
const currentlyOpenPopover = getState().getIn(['popover', 'popoverType'])
export const openPopover = (type, props) => (dispatch, getState) => {
const currentlyOpenPopover = getState().getIn(['popover', 'popoverType'])
if (currentlyOpenPopover === type) {
dispatch(closePopover(type))
} else {
dispatch(handleOpenPopover(type, props))
}
if (currentlyOpenPopover === type) {
dispatch(closePopover(type))
} else {
dispatch(handleOpenPopover(type, props))
}
}
export function closePopover(type) {
return {
type: POPOVER_CLOSE,
popoverType: type,
}
}
export const closePopover = (type) => ({
type: POPOVER_CLOSE,
popoverType: type,
})
const handleOpenPopover = (type, props) => {
return {
type: POPOVER_OPEN,
popoverType: type,
popoverProps: props,
}
}
const handleOpenPopover = (type, props) => ({
type: POPOVER_OPEN,
popoverType: type,
popoverProps: props,
})

View File

@@ -5,18 +5,19 @@ export const PROMOTIONS_FETCH_REQUEST = 'PROMOTIONS_FETCH_REQUEST'
export const PROMOTIONS_FETCH_SUCCESS = 'PROMOTIONS_FETCH_SUCCESS'
export const PROMOTIONS_FETCH_FAIL = 'PROMOTIONS_FETCH_FAIL'
export const fetchPromotions = () => {
return (dispatch, getState) => {
if (!me) return
/**
*
*/
export const fetchPromotions = () => (dispatch, getState) => {
if (!me) return
dispatch(fetchPromotionsRequest())
dispatch(fetchPromotionsRequest())
api(getState).get('/api/v1/promotions').then((response) => {
dispatch(fetchPromotionsSuccess(response.data))
}).catch((error) => {
dispatch(fetchPromotionsFail(error))
})
}
api(getState).get('/api/v1/promotions').then((response) => {
dispatch(fetchPromotionsSuccess(response.data))
}).catch((error) => {
dispatch(fetchPromotionsFail(error))
})
}
const fetchPromotionsRequest = () => ({

View File

@@ -4,8 +4,8 @@ import {
CLEAR_SUBSCRIPTION,
SET_ALERTS,
setAlerts,
} from './setter';
import { register, saveSettings } from './registerer';
} from './setter'
import { register, saveSettings } from './registerer'
export {
SET_BROWSER_SUPPORT,
@@ -13,11 +13,9 @@ export {
CLEAR_SUBSCRIPTION,
SET_ALERTS,
register,
};
export function changeAlerts(path, value) {
return dispatch => {
dispatch(setAlerts(path, value));
dispatch(saveSettings());
};
}
export const changeAlerts = (path, value) => (dispatch) => {
dispatch(setAlerts(path, value))
dispatch(saveSettings())
}

View File

@@ -1,34 +1,26 @@
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 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,
export const setBrowserSupport = (value) => ({
type: SET_BROWSER_SUPPORT,
value,
})
export const setSubscription = (subscription) => ({
type: SET_SUBSCRIPTION,
subscription,
})
export const clearSubscription = () => ({
type: CLEAR_SUBSCRIPTION,
})
export const setAlerts = (path, value) => (dispatch) => {
dispatch({
type: SET_ALERTS,
path,
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,
});
};
})
}

View File

@@ -1,89 +1,82 @@
import api from '../api';
import { openModal, closeModal } from './modal';
import api from '../api'
import { openModal, closeModal } from './modal'
import { MODAL_REPORT } from '../constants'
export const REPORT_INIT = 'REPORT_INIT';
export const REPORT_CANCEL = 'REPORT_CANCEL';
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_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 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,
});
/**
*
*/
export const initReport = (account, status) => (dispatch) => {
dispatch({
type: REPORT_INIT,
account,
status,
})
dispatch(openModal('REPORT'));
};
};
dispatch(openModal(MODAL_REPORT))
}
export function cancelReport() {
return {
type: REPORT_CANCEL,
};
};
/**
*
*/
export const cancelReport = () => ({
type: REPORT_CANCEL,
})
export function toggleStatusReport(statusId, checked) {
return {
type: REPORT_STATUS_TOGGLE,
statusId,
checked,
};
};
/**
*
*/
export const toggleStatusReport = (statusId, checked) => ({
type: REPORT_STATUS_TOGGLE,
statusId,
checked,
})
export function submitReport() {
return (dispatch, getState) => {
dispatch(submitReportRequest());
/**
*
*/
export const submitReport = () => (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)));
};
};
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,
};
};
const submitReportRequest = () => ({
type: REPORT_SUBMIT_REQUEST,
})
export function submitReportSuccess(report) {
return {
type: REPORT_SUBMIT_SUCCESS,
report,
};
};
const submitReportSuccess = (report) => ({
type: REPORT_SUBMIT_SUCCESS,
report,
})
export function submitReportFail(error) {
return {
type: REPORT_SUBMIT_FAIL,
error,
};
};
const submitReportFail = (error) => ({
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,
};
};
/**
*
*/
export const changeReportComment = (comment) => ({
type: REPORT_COMMENT_CHANGE,
comment,
})

View File

@@ -18,93 +18,92 @@ export const SEARCH_FETCH_FAIL = 'SEARCH_FETCH_FAIL';
export const SEARCH_FILTER_SET = 'SEARCH_FILTER_SET'
export function changeSearch(value) {
return {
type: SEARCH_CHANGE,
value,
};
};
/**
*
*/
export const changeSearch = (value) => ({
type: SEARCH_CHANGE,
value,
})
export function clearSearch() {
return {
type: SEARCH_CLEAR,
};
};
/**
*
*/
export const clearSearch = () => ({
type: SEARCH_CLEAR,
})
export function submitSearch() {
return (dispatch, getState) => {
const value = getState().getIn(['search', 'value']);
const onlyVerified = getState().getIn(['search', 'filter', 'onlyVerified'])
/**
*
*/
export const submitSearch = () => (dispatch, getState) => {
const value = getState().getIn(['search', 'value'])
const onlyVerified = getState().getIn(['search', 'filter', 'onlyVerified'])
if (value.length === 0) return
if (value.length === 0) return
dispatch(fetchSearchRequest());
dispatch(fetchSearchRequest())
api(getState).get('/api/v2/search', {
params: {
onlyVerified,
q: value,
resolve: true,
},
}).then(response => {
if (response.data.accounts) {
dispatch(importFetchedAccounts(response.data.accounts));
dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
}
api(getState).get('/api/v2/search', {
params: {
onlyVerified,
q: value,
resolve: true,
},
}).then((response) => {
if (response.data.accounts) {
dispatch(importFetchedAccounts(response.data.accounts))
dispatch(fetchRelationships(response.data.accounts.map(item => item.id)))
}
if (response.data.statuses) {
dispatch(importFetchedStatuses(response.data.statuses));
}
if (response.data.statuses) {
dispatch(importFetchedStatuses(response.data.statuses))
}
if (response.data.links) {
dispatch(importLinkCards(response.data.links));
}
if (response.data.links) {
dispatch(importLinkCards(response.data.links))
}
if (response.data.groups) {
dispatch(fetchGroupsSuccess(response.data.groups))
dispatch(fetchGroupRelationships(response.data.groups.map(item => item.id)))
}
if (response.data.groups) {
dispatch(fetchGroupsSuccess(response.data.groups))
dispatch(fetchGroupRelationships(response.data.groups.map(item => item.id)))
}
dispatch(fetchSearchSuccess(response.data));
}).catch(error => {
dispatch(fetchSearchFail(error));
});
};
};
dispatch(fetchSearchSuccess(response.data))
}).catch((error) => {
dispatch(fetchSearchFail(error))
})
}
export function fetchSearchRequest() {
return {
type: SEARCH_FETCH_REQUEST,
};
};
const fetchSearchRequest = () => ({
type: SEARCH_FETCH_REQUEST,
})
export function fetchSearchSuccess(results) {
return {
type: SEARCH_FETCH_SUCCESS,
results,
};
};
const fetchSearchSuccess = (results) => ({
type: SEARCH_FETCH_SUCCESS,
results,
})
export function fetchSearchFail(error) {
return {
type: SEARCH_FETCH_FAIL,
error,
};
};
const fetchSearchFail = (error) => ({
type: SEARCH_FETCH_FAIL,
error,
})
export function showSearch() {
return {
type: SEARCH_SHOW,
};
};
/**
*
*/
export const showSearch = () => ({
type: SEARCH_SHOW,
})
export function setFilter(path, value, shouldSubmit) {
return (dispatch) => {
dispatch({
type: SEARCH_FILTER_SET,
path: path,
value: value,
})
if (shouldSubmit) dispatch(submitSearch())
}
/**
*
*/
export const setFilter = (path, value, shouldSubmit) => (dispatch) => {
dispatch({
type: SEARCH_FILTER_SET,
path: path,
value: value,
})
if (shouldSubmit) dispatch(submitSearch())
}

View File

@@ -5,16 +5,26 @@ import { me } from '../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,
})
export const saveShownOnboarding = () => (dispatch) => {
dispatch(changeSetting(['shownOnboarding'], true))
dispatch(saveSettings())
}
dispatch(saveSettings())
}
export const changeSetting = (path, value) => (dispatch) => {
dispatch({
type: SETTING_CHANGE,
path,
value,
})
dispatch(saveSettings())
}
/**
*
*/
export const saveSettings = () => (dispatch, getState) => {
debouncedSave(dispatch, getState)
}
const debouncedSave = debounce((dispatch, getState) => {
@@ -28,7 +38,3 @@ const debouncedSave = debounce((dispatch, getState) => {
.then(() => dispatch({ type: SETTING_SAVE }))
.catch(() => { /* */ })
}, 350, { trailing: true })
export function saveSettings() {
return (dispatch, getState) => debouncedSave(dispatch, getState)
}

View File

@@ -1,47 +1,44 @@
import axios from 'axios'
import api from '../api'
import { me } from '../initial_state'
export const SHOP_FEATURED_PRODUCTS_FETCH_REQUEST = 'SHOP_FEATURED_PRODUCTS_FETCH_REQUEST'
export const SHOP_FEATURED_PRODUCTS_FETCH_SUCCESS = 'SHOP_FEATURED_PRODUCTS_FETCH_SUCCESS'
export const SHOP_FEATURED_PRODUCTS_FETCH_FAIL = 'SHOP_FEATURED_PRODUCTS_FETCH_FAIL'
export const fetchFeaturedProducts = () => {
return function (dispatch, getState) {
if (!me) return
/**
*
*/
export const fetchFeaturedProducts = () => (dispatch, getState) => {
if (!me) return
dispatch(fetchFeaturedProductsRequest('featured'))
dispatch(fetchFeaturedProductsRequest('featured'))
axios.get('https://dissenter-shop.gab.com/product/group/json').then((response) => {
try {
dispatch(fetchFeaturedProductsSuccess(response.data.data, 'featured'))
} catch (error) {
//
}
}).catch(function (error) {
dispatch(fetchFeaturedProductsFail(error, 'featured'))
})
}
axios.get('https://dissenter-shop.gab.com/product/group/json').then((response) => {
try {
dispatch(fetchFeaturedProductsSuccess(response.data.data, 'featured'))
} catch (error) {
//
}
}).catch((error) => {
dispatch(fetchFeaturedProductsFail(error, 'featured'))
})
}
function fetchFeaturedProductsRequest(listType) {
return {
type: SHOP_FEATURED_PRODUCTS_FETCH_REQUEST,
listType,
}
}
const fetchFeaturedProductsRequest = (listType) => ({
type: SHOP_FEATURED_PRODUCTS_FETCH_REQUEST,
listType,
})
function fetchFeaturedProductsSuccess(items, listType) {
return {
type: SHOP_FEATURED_PRODUCTS_FETCH_SUCCESS,
items,
listType,
}
}
const fetchFeaturedProductsSuccess = (items, listType) => ({
type: SHOP_FEATURED_PRODUCTS_FETCH_SUCCESS,
items,
listType,
})
function fetchFeaturedProductsFail(error, listType) {
return {
type: SHOP_FEATURED_PRODUCTS_FETCH_FAIL,
error,
listType,
}
}
const fetchFeaturedProductsFail = (error, listType) => ({
type: SHOP_FEATURED_PRODUCTS_FETCH_FAIL,
error,
listType,
})

View File

@@ -13,116 +13,104 @@ export const SHORTCUTS_REMOVE_REQUEST = 'SHORTCUTS_REMOVE_REQUEST'
export const SHORTCUTS_REMOVE_SUCCESS = 'SHORTCUTS_REMOVE_SUCCESS'
export const SHORTCUTS_REMOVE_FAIL = 'SHORTCUTS_REMOVE_FAIL'
export function fetchShortcuts() {
return (dispatch, getState) => {
if (!me) return
/**
*
*/
export const fetchShortcuts = () => (dispatch, getState) => {
if (!me) return
const isFetched = getState().getIn(['shortcuts', 'isFetched'], false)
if (isFetched) return
dispatch(fetchShortcutsRequest())
dispatch(fetchShortcutsRequest())
api(getState).get('/api/v1/shortcuts').then(response => {
dispatch(fetchShortcutsSuccess(response.data))
}).catch(error => dispatch(fetchShortcutsFail(error)))
}
api(getState).get('/api/v1/shortcuts').then(response => {
dispatch(fetchShortcutsSuccess(response.data))
}).catch(error => dispatch(fetchShortcutsFail(error)))
}
export function fetchShortcutsRequest() {
return {
type: SHORTCUTS_FETCH_REQUEST,
}
const fetchShortcutsRequest = () => ({
type: SHORTCUTS_FETCH_REQUEST,
})
const fetchShortcutsSuccess = (shortcuts) => ({
type: SHORTCUTS_FETCH_SUCCESS,
shortcuts,
})
const fetchShortcutsFail = (error) => ({
type: SHORTCUTS_FETCH_FAIL,
error,
})
/**
*
*/
export const addShortcut = (shortcutType, shortcutId) => (dispatch, getState) => {
if (!me) return
dispatch(addShortcutsRequest())
api(getState).post('/api/v1/shortcuts', {
shortcut_type: shortcutType,
shortcut_id: shortcutId,
}).then(response => {
dispatch(addShortcutsSuccess(response.data))
}).catch(error => dispatch(addShortcutsFail(error)))
}
export function fetchShortcutsSuccess(shortcuts) {
return {
shortcuts,
type: SHORTCUTS_FETCH_SUCCESS,
}
}
const addShortcutsRequest = () => ({
type: SHORTCUTS_ADD_REQUEST,
})
export function fetchShortcutsFail(error) {
return {
error,
type: SHORTCUTS_FETCH_FAIL,
}
}
const addShortcutsSuccess = (shortcut) => ({
type: SHORTCUTS_ADD_SUCCESS,
shortcut,
})
export function addShortcut(shortcutType, shortcutId) {
return (dispatch, getState) => {
if (!me) return
const addShortcutsFail = (error) => ({
type: SHORTCUTS_ADD_FAIL,
error,
})
dispatch(addShortcutsRequest())
api(getState).post('/api/v1/shortcuts', {
shortcut_type: shortcutType,
shortcut_id: shortcutId,
}).then(response => {
dispatch(addShortcutsSuccess(response.data))
}).catch(error => dispatch(addShortcutsFail(error)))
}
}
export function addShortcutsRequest() {
return {
type: SHORTCUTS_ADD_REQUEST,
}
}
export function addShortcutsSuccess(shortcut) {
return {
shortcut,
type: SHORTCUTS_ADD_SUCCESS,
}
}
export function addShortcutsFail(error) {
return {
error,
type: SHORTCUTS_ADD_FAIL,
}
}
export function removeShortcut(shortcutObjectId, shortcutType, shortcutId) {
return (dispatch, getState) => {
if (!me) return
let id
if (shortcutObjectId) {
id = shortcutObjectId
} else if (shortcutType && shortcutId) {
const shortcuts = getState().getIn(['shortcuts', 'items'])
const shortcut = shortcuts.find((s) => {
return s.get('shortcut_id') == shortcutId && s.get('shortcut_type') === shortcutType
})
if (!!shortcut) {
id = shortcut.get('id')
}
/**
*
*/
export const removeShortcut = (shortcutObjectId, shortcutType, shortcutId) => (dispatch, getState) => {
if (!me) return
let id
if (shortcutObjectId) {
id = shortcutObjectId
} else if (shortcutType && shortcutId) {
const shortcuts = getState().getIn(['shortcuts', 'items'])
const shortcut = shortcuts.find((s) => {
return s.get('shortcut_id') == shortcutId && s.get('shortcut_type') === shortcutType
})
if (!!shortcut) {
id = shortcut.get('id')
}
if (!id) return
dispatch(removeShortcutsRequest())
api(getState).delete(`/api/v1/shortcuts/${id}`).then(response => {
dispatch(removeShortcutsSuccess(response.data.id))
}).catch(error => dispatch(removeShortcutsFail(error)))
}
if (!id) return
dispatch(removeShortcutsRequest())
api(getState).delete(`/api/v1/shortcuts/${id}`).then(response => {
dispatch(removeShortcutsSuccess(response.data.id))
}).catch(error => dispatch(removeShortcutsFail(error)))
}
export function removeShortcutsRequest() {
return {
type: SHORTCUTS_REMOVE_REQUEST,
}
}
const removeShortcutsRequest = () => ({
type: SHORTCUTS_REMOVE_REQUEST,
})
export function removeShortcutsSuccess(shortcutId) {
return {
shortcutId,
type: SHORTCUTS_REMOVE_SUCCESS,
}
}
const removeShortcutsSuccess = (shortcutId) => ({
type: SHORTCUTS_REMOVE_SUCCESS,
shortcutId,
})
export function removeShortcutsFail(error) {
return {
error,
type: SHORTCUTS_REMOVE_FAIL,
}
}
const removeShortcutsFail = (error) => ({
type: SHORTCUTS_REMOVE_FAIL,
error,
})

View File

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

View File

@@ -4,6 +4,12 @@ export const STATUS_REVISIONS_LOAD = 'STATUS_REVISIONS_LOAD'
export const STATUS_REVISIONS_LOAD_SUCCESS = 'STATUS_REVISIONS_SUCCESS'
export const STATUS_REVISIONS_LOAD_FAIL = 'STATUS_REVISIONS_FAIL'
export const loadStatusRevisions = (statusId) => (dispatch, getState) => {
api(getState).get(`/api/v1/statuses/${statusId}/revisions`)
.then(res => dispatch(loadStatusRevisionsSuccess(res.data)))
.catch(() => dispatch(loadStatusRevisionsFail()))
}
const loadStatusRevisionsSuccess = (data) => ({
type: STATUS_REVISIONS_LOAD_SUCCESS,
revisions: data,
@@ -12,12 +18,4 @@ const loadStatusRevisionsSuccess = (data) => ({
const loadStatusRevisionsFail = () => ({
type: STATUS_REVISIONS_LOAD_FAIL,
error: true,
})
export function loadStatusRevisions(statusId) {
return (dispatch, getState) => {
api(getState).get(`/api/v1/statuses/${statusId}/revisions`)
.then(res => dispatch(loadStatusRevisionsSuccess(res.data)))
.catch(() => dispatch(loadStatusRevisionsFail()))
}
}
})

View File

@@ -22,19 +22,13 @@ export const COMMENTS_FETCH_REQUEST = 'COMMENTS_FETCH_REQUEST';
export const COMMENTS_FETCH_SUCCESS = 'COMMENTS_FETCH_SUCCESS';
export const COMMENTS_FETCH_FAIL = 'COMMENTS_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 STATUS_EDIT = 'STATUS_EDIT';
export const UPDATE_STATUS_STATS = 'UPDATE_STATUS_STATS'
export function fetchStatusRequest(id, skipLoading) {
return {
type: STATUS_FETCH_REQUEST,
@@ -280,78 +274,6 @@ export function fetchCommentsFail(id, error) {
};
};
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];
@@ -373,3 +295,10 @@ export function revealStatus(ids) {
ids,
};
};
export function updateStatusStats(data) {
return {
type: UPDATE_STATUS_STATS,
data,
};
};

View File

@@ -1,24 +1,22 @@
import { Iterable, fromJS } from 'immutable';
import { hydrateCompose } from './compose';
import { importFetchedAccounts } from './importer';
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';
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());
const convertState = (rawState) => {
return fromJS(rawState, (k, v) => Iterable.isIndexed(v) ? v.toList() : v.toMap())
}
export function hydrateStore(rawState) {
return (dispatch) => {
const state = convertState(rawState);
export const hydrateStore = (rawState) => (dispatch) => {
const state = convertState(rawState)
dispatch({
type: STORE_HYDRATE,
state,
});
dispatch({
type: STORE_HYDRATE,
state,
})
dispatch(hydrateCompose());
if (rawState.accounts) dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
};
};
dispatch(hydrateCompose())
if (rawState.accounts) dispatch(importFetchedAccounts(Object.values(rawState.accounts)))
}

View File

@@ -1,70 +1,73 @@
import { connectStream } from '../stream';
import { connectStream } from '../stream'
import {
deleteFromTimelines,
connectTimeline,
disconnectTimeline,
updateTimelineQueue,
} from './timelines';
import { updateNotificationsQueue } from './notifications';
import { updateConversations } from './conversations';
import { fetchFilters } from './filters';
import { getLocale } from '../locales';
import { handleComposeSubmit } from './compose';
} from './timelines'
import { updateNotificationsQueue } from './notifications'
import { fetchFilters } from './filters'
import { getLocale } from '../locales'
import { handleComposeSubmit } from './compose'
const { messages } = getLocale();
const { messages } = getLocale()
export function connectTimelineStream (timelineId, path, pollingRefresh = null, accept = null) {
/**
*
*/
export const connectTimelineStream = (timelineId, path, pollingRefresh = null, accept = null) => {
return connectStream (path, pollingRefresh, (dispatch, getState) => {
const locale = getState().getIn(['meta', 'locale']);
const locale = getState().getIn(['meta', 'locale'])
return {
onConnect() {
dispatch(connectTimeline(timelineId));
dispatch(connectTimeline(timelineId))
},
onDisconnect() {
dispatch(disconnectTimeline(timelineId));
dispatch(disconnectTimeline(timelineId))
},
onReceive (data) {
switch(data.event) {
case 'update':
dispatch(updateTimelineQueue(timelineId, JSON.parse(data.payload), accept));
break;
dispatch(updateTimelineQueue(timelineId, JSON.parse(data.payload), accept))
break
case 'delete':
dispatch(deleteFromTimelines(data.payload));
break;
dispatch(deleteFromTimelines(data.payload))
break
case 'notification':
dispatch(updateNotificationsQueue(JSON.parse(data.payload), messages, locale, window.location.pathname));
break;
case 'conversation':
dispatch(updateConversations(JSON.parse(data.payload)));
break;
dispatch(updateNotificationsQueue(JSON.parse(data.payload), messages, locale, window.location.pathname))
break
case 'filters_changed':
dispatch(fetchFilters());
break;
dispatch(fetchFilters())
break
}
},
};
});
}
})
}
export const connectUserStream = () => connectTimelineStream('home', 'user');
export const connectProStream = () => connectTimelineStream('pro', 'pro');
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
/**
*
*/
export const connectStatusUpdateStream = () => {
return connectStream('statuscard', null, (dispatch, getState) => {
return {
onConnect() {},
onDisconnect() {},
onReceive (data) {
if (!data['event'] || !data['payload']) return;
if (!data['event'] || !data['payload']) return
if (data.event === 'update') {
handleComposeSubmit(dispatch, getState, {data: JSON.parse(data.payload)}, null)
}
},
};
});
}
}
})
}
/**
*
*/
export const connectUserStream = () => connectTimelineStream('home', 'user')

View File

@@ -13,61 +13,59 @@ export const SUGGESTIONS_FETCH_FAIL = 'SUGGESTIONS_FETCH_FAIL'
export const SUGGESTIONS_DISMISS = 'SUGGESTIONS_DISMISS'
export function fetchPopularSuggestions() {
return (dispatch, getState) => {
if (!me) return false
dispatch(fetchSuggestionsRequest(SUGGESTION_TYPE_VERIFIED))
api(getState).get(`/api/v1/suggestions?type=${SUGGESTION_TYPE_VERIFIED}`).then(response => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchSuggestionsSuccess(response.data, SUGGESTION_TYPE_VERIFIED))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch(error => dispatch(fetchSuggestionsFail(error, SUGGESTION_TYPE_VERIFIED)))
}
/**
*
*/
export const fetchPopularSuggestions = () => (dispatch, getState) => {
if (!me) return false
fetchSuggestions(SUGGESTION_TYPE_VERIFIED, dispatch, getState)
}
export function fetchRelatedSuggestions(unlimited = false) {
return (dispatch, getState) => {
if (!me) return false
dispatch(fetchSuggestionsRequest(SUGGESTION_TYPE_RELATED))
api(getState).get(`/api/v1/suggestions?type=${SUGGESTION_TYPE_RELATED}&unlimited=${!!unlimited}`).then(response => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchSuggestionsSuccess(response.data, SUGGESTION_TYPE_RELATED))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch(error => dispatch(fetchSuggestionsFail(error, SUGGESTION_TYPE_RELATED)))
}
/**
*
*/
export const fetchRelatedSuggestions = (unlimited = false) => (dispatch, getState) => {
if (!me) return false
fetchSuggestions(SUGGESTION_TYPE_RELATED, dispatch, getState, unlimited)
}
export function fetchSuggestionsRequest(suggestionType) {
return {
type: SUGGESTIONS_FETCH_REQUEST,
skipLoading: true,
suggestionType,
}
/**
*
*/
const fetchSuggestions = (suggestionType, dispatch, getState, unlimited = false) => {
dispatch(fetchSuggestionsRequest(suggestionType))
api(getState).get(`/api/v1/suggestions?type=${suggestionType}&unlimited=${!!unlimited}`).then((response) => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchSuggestionsSuccess(response.data, suggestionType))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch(error => dispatch(fetchSuggestionsFail(error, suggestionType)))
}
export function fetchSuggestionsSuccess(accounts, suggestionType) {
return {
type: SUGGESTIONS_FETCH_SUCCESS,
skipLoading: true,
accounts,
suggestionType
}
}
const fetchSuggestionsRequest = (suggestionType) => ({
type: SUGGESTIONS_FETCH_REQUEST,
skipLoading: true,
suggestionType,
})
export function fetchSuggestionsFail(error, suggestionType) {
return {
type: SUGGESTIONS_FETCH_FAIL,
skipLoading: true,
skipAlert: true,
error,
suggestionType,
}
}
const fetchSuggestionsSuccess = (accounts, suggestionType) => ({
type: SUGGESTIONS_FETCH_SUCCESS,
skipLoading: true,
accounts,
suggestionType
})
const fetchSuggestionsFail = (error, suggestionType) => ({
type: SUGGESTIONS_FETCH_FAIL,
skipLoading: true,
skipAlert: true,
error,
suggestionType,
})
/**
*
*/
export const dismissRelatedSuggestion = (accountId) => (dispatch, getState) => {
if (!me) return

View File

@@ -19,7 +19,7 @@ export const showTimelineInjection = (injectionId) => (dispatch) => {
export const hideTimelineInjection = (injectionId) => (dispatch, getState) => {
const existingInjectionWeight = getState().getIn(['settings', 'injections', injectionId], null)
if (!existingInjectionWeight) return false
if (!existingInjectionWeight) return
const newInjectionWeight = Math.max(existingInjectionWeight - 0.005, 0.01)

View File

@@ -19,13 +19,6 @@ export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const MAX_QUEUED_ITEMS = 40;
const fetchStatusesAccountsRelationships = (dispatch, statuses) => {
const accountIds = statuses.map(item => item.account.id)
if (accountIds.length > 0) {
dispatch(fetchRelationships(accountIds));
}
}
export function updateTimeline(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
@@ -134,36 +127,34 @@ const parseTags = (tags = {}, mode) => {
});
};
export function expandTimeline(timelineId, path, params = {}, done = noOp) {
return (dispatch, getState) => {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
const isLoadingMore = !!params.max_id;
export const expandTimeline = (timelineId, path, params = {}, done = noOp) => (dispatch, getState) => {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap())
const isLoadingMore = !!params.max_id
if (timeline.get('isLoading')) {
done();
return;
}
if (!!timeline && (timeline.get('isLoading') || timeline.get('isError'))) {
done()
return
}
if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) {
params.since_id = timeline.getIn(['items', 0]);
}
if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) {
params.since_id = timeline.getIn(['items', 0])
}
const isLoadingRecent = !!params.since_id;
const isLoadingRecent = !!params.since_id
dispatch(expandTimelineRequest(timelineId, isLoadingMore));
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));
fetchStatusesAccountsRelationships(dispatch, response.data)
done();
}).catch(error => {
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
done();
});
};
};
api(getState).get(path, { params }).then((response) => {
console.log("response:", 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 expandExploreTimeline = ({ maxId, sortBy } = {}, done = noOp) => expandTimeline('explore', '/api/v1/timelines/explore', { max_id: maxId, sort_by: sortBy }, done);

View File

@@ -7,26 +7,29 @@ export const TOAST_SHOW = 'TOAST_SHOW'
export const TOAST_DISMISS = 'TOAST_DISMISS'
export const TOAST_CLEAR = 'TOAST_CLEAR'
export function dismissToast(alert) {
return {
type: TOAST_DISMISS,
alert,
}
}
/**
*
*/
export const dismissToast = (alert) => ({
type: TOAST_DISMISS,
alert,
})
export function clearToast() {
return {
type: TOAST_CLEAR,
}
}
/**
*
*/
export const clearToast = () => ({
type: TOAST_CLEAR,
})
function showToast(type, message) {
return {
type: TOAST_SHOW,
toastType: type,
message,
}
}
/**
*
*/
export const showToast = (type, message) => ({
type: TOAST_SHOW,
toastType: type,
message,
})
export const showToastError = (message) => {
return showToast(TOAST_TYPE_ERROR, message)

View File

@@ -11,52 +11,50 @@ export const SAVE_USER_PROFILE_INFORMATION_FETCH_SUCCESS = 'SAVE_USER_PROFILE_IN
export const SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL = 'SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL'
export const RESEND_USER_CONFIRMATION_EMAIL_SUCCESS = 'RESEND_USER_CONFIRMATION_EMAIL_SUCCESS'
export const saveUserProfileInformation = (data) => {
return function (dispatch, getState) {
if (!isObject(data) || !me) return
/**
*
*/
export const saveUserProfileInformation = (data) => (dispatch, getState) => {
if (!isObject(data) || !me) return
dispatch(saveUserProfileInformationRequest())
dispatch(saveUserProfileInformationRequest())
const formData = new FormData()
if (!!data.displayName) formData.append('display_name', data.displayName)
if (data.note !== undefined) formData.append('note', data.note)
if (data.avatar !== undefined) formData.append('avatar', data.avatar)
if (data.header !== undefined) formData.append('header', data.header)
if (data.locked !== undefined) formData.append('locked', data.locked)
const formData = new FormData()
if (!!data.displayName) formData.append('display_name', data.displayName)
if (data.note !== undefined) formData.append('note', data.note)
if (data.avatar !== undefined) formData.append('avatar', data.avatar)
if (data.header !== undefined) formData.append('header', data.header)
if (data.locked !== undefined) formData.append('locked', data.locked)
api(getState).patch('/api/v1/accounts/update_credentials', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => {
dispatch(importFetchedAccount(response.data))
dispatch(saveUserProfileInformationSuccess(response.data))
}).catch(error => {
dispatch(saveUserProfileInformationFail(error))
})
}
api(getState).patch('/api/v1/accounts/update_credentials', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((response) => {
dispatch(importFetchedAccount(response.data))
dispatch(saveUserProfileInformationSuccess(response.data))
}).catch((error) => {
dispatch(saveUserProfileInformationFail(error))
})
}
function saveUserProfileInformationRequest() {
return {
type: SAVE_USER_PROFILE_INFORMATION_FETCH_REQUEST,
}
}
const saveUserProfileInformationRequest = () => ({
type: SAVE_USER_PROFILE_INFORMATION_FETCH_REQUEST,
})
function saveUserProfileInformationSuccess(userProfileData) {
return {
type: SAVE_USER_PROFILE_INFORMATION_FETCH_SUCCESS,
userProfileData,
}
}
const saveUserProfileInformationSuccess = (userProfileData) => ({
type: SAVE_USER_PROFILE_INFORMATION_FETCH_SUCCESS,
userProfileData,
})
function saveUserProfileInformationFail(error) {
return {
type: SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL,
error,
}
}
const saveUserProfileInformationFail = (error) => ({
type: SAVE_USER_PROFILE_INFORMATION_FETCH_FAIL,
error,
})
/**
*
*/
export const resendUserConfirmationEmail = () => (dispatch, getState) => {
if (!me || emailConfirmed) return

View File

@@ -1,82 +0,0 @@
import emojify from '../emoji';
describe('emoji', () => {
describe('.emojify', () => {
it('ignores unknown shortcodes', () => {
expect(emojify(':foobarbazfake:')).toEqual(':foobarbazfake:');
});
it('ignores shortcodes inside of tags', () => {
expect(emojify('<p data-foo=":smile:"></p>')).toEqual('<p data-foo=":smile:"></p>');
});
it('works with unclosed tags', () => {
expect(emojify('hello>')).toEqual('hello>');
expect(emojify('<hello')).toEqual('<hello');
});
it('works with unclosed shortcodes', () => {
expect(emojify('smile:')).toEqual('smile:');
expect(emojify(':smile')).toEqual(':smile');
});
it('does unicode', () => {
expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual(
'<img draggable="false" class="emojione" alt="👩‍👩‍👦‍👦" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
expect(emojify('👨‍👩‍👧‍👧')).toEqual(
'<img draggable="false" class="emojione" alt="👨‍👩‍👧‍👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
expect(emojify('👩‍👩‍👦')).toEqual('<img draggable="false" class="emojione" alt="👩‍👩‍👦" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />');
expect(emojify('\u2757')).toEqual(
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
});
it('does multiple unicode', () => {
expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual(
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" />');
expect(emojify('\u2757#\uFE0F\u20E3')).toEqual(
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" />');
expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual(
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual(
'foo <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" /> bar');
});
it('ignores unicode inside of tags', () => {
expect(emojify('<p data-foo="\uD83D\uDC69\uD83D\uDC69\uD83D\uDC66"></p>')).toEqual('<p data-foo="\uD83D\uDC69\uD83D\uDC69\uD83D\uDC66"></p>');
});
it('does multiple emoji properly (issue 5188)', () => {
expect(emojify('👌🌈💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg" />');
expect(emojify('👌 🌈 💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg" />');
});
it('does an emoji that has no shortcode', () => {
expect(emojify('👁‍🗨')).toEqual('<img draggable="false" class="emojione" alt="👁‍🗨" title="" src="/emoji/1f441-200d-1f5e8.svg" />');
});
it('does an emoji whose filename is irregular', () => {
expect(emojify('↙️')).toEqual('<img draggable="false" class="emojione" alt="↙️" title=":arrow_lower_left:" src="/emoji/2199.svg" />');
});
it('avoid emojifying on invisible text', () => {
expect(emojify('<a href="http://example.com/test%F0%9F%98%84"><span class="invisible">http://</span><span class="ellipsis">example.com/te</span><span class="invisible">st😄</span></a>'))
.toEqual('<a href="http://example.com/test%F0%9F%98%84"><span class="invisible">http://</span><span class="ellipsis">example.com/te</span><span class="invisible">st😄</span></a>');
expect(emojify('<span class="invisible">:luigi:</span>', { ':luigi:': { static_url: 'luigi.exe' } }))
.toEqual('<span class="invisible">:luigi:</span>');
});
it('avoid emojifying on invisible text with nested tags', () => {
expect(emojify('<span class="invisible">😄<span class="foo">bar</span>😴</span>😇'))
.toEqual('<span class="invisible">😄<span class="foo">bar</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
expect(emojify('<span class="invisible">😄<span class="invisible">😕</span>😴</span>😇'))
.toEqual('<span class="invisible">😄<span class="invisible">😕</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
expect(emojify('<span class="invisible">😄<br/>😴</span>😇'))
.toEqual('<span class="invisible">😄<br/>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
});
it('skips the textual presentation VS15 character', () => {
expect(emojify('✴︎')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15
.toEqual('<img draggable="false" class="emojione" alt="✴" title=":eight_pointed_black_star:" src="/emoji/2734.svg" />');
});
});
});

View File

@@ -1,176 +0,0 @@
import pick from 'lodash.pick'
import { emojiIndex } from 'emoji-mart';
import { search } from '../emoji_mart_search_light';
const trimEmojis = emoji => pick(emoji, ['id', 'unified', 'native', 'custom']);
describe('emoji_index', () => {
it('should give same result for emoji_index_light and emoji-mart', () => {
const expected = [
{
id: 'pineapple',
unified: '1f34d',
native: '🍍',
},
];
expect(search('pineapple').map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('pineapple').map(trimEmojis)).toEqual(expected);
});
it('orders search results correctly', () => {
const expected = [
{
id: 'apple',
unified: '1f34e',
native: '🍎',
},
{
id: 'pineapple',
unified: '1f34d',
native: '🍍',
},
{
id: 'green_apple',
unified: '1f34f',
native: '🍏',
},
{
id: 'iphone',
unified: '1f4f1',
native: '📱',
},
];
expect(search('apple').map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('apple').map(trimEmojis)).toEqual(expected);
});
it('can include/exclude categories', () => {
expect(search('flag', { include: ['people'] })).toEqual([]);
expect(emojiIndex.search('flag', { include: ['people'] })).toEqual([]);
});
it('(different behavior from emoji-mart) do not erases custom emoji if not passed again', () => {
const custom = [
{
id: 'gabsocial',
name: 'gabsocial',
short_names: ['gabsocial'],
text: '',
emoticons: [],
keywords: ['gabsocial'],
imageUrl: 'http://example.com',
custom: true,
},
];
search('', { custom });
emojiIndex.search('', { custom });
const expected = [];
const lightExpected = [
{
id: 'gabsocial',
custom: true,
},
];
expect(search('masto').map(trimEmojis)).toEqual(lightExpected);
expect(emojiIndex.search('masto').map(trimEmojis)).toEqual(expected);
});
it('(different behavior from emoji-mart) erases custom emoji if another is passed', () => {
const custom = [
{
id: 'gabsocial',
name: 'gabsocial',
short_names: ['gabsocial'],
text: '',
emoticons: [],
keywords: ['gabsocial'],
imageUrl: 'http://example.com',
custom: true,
},
];
search('', { custom });
emojiIndex.search('', { custom });
const expected = [];
expect(search('masto', { custom: [] }).map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('masto').map(trimEmojis)).toEqual(expected);
});
it('handles custom emoji', () => {
const custom = [
{
id: 'gabsocial',
name: 'gabsocial',
short_names: ['gabsocial'],
text: '',
emoticons: [],
keywords: ['gabsocial'],
imageUrl: 'http://example.com',
custom: true,
},
];
search('', { custom });
emojiIndex.search('', { custom });
const expected = [
{
id: 'gabsocial',
custom: true,
},
];
expect(search('masto', { custom }).map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('masto', { custom }).map(trimEmojis)).toEqual(expected);
});
it('should filter only emojis we care about, exclude pineapple', () => {
const emojisToShowFilter = emoji => emoji.unified !== '1F34D';
expect(search('apple', { emojisToShowFilter }).map((obj) => obj.id))
.not.toContain('pineapple');
expect(emojiIndex.search('apple', { emojisToShowFilter }).map((obj) => obj.id))
.not.toContain('pineapple');
});
it('does an emoji whose unified name is irregular', () => {
const expected = [
{
'id': 'water_polo',
'unified': '1f93d',
'native': '🤽',
},
{
'id': 'man-playing-water-polo',
'unified': '1f93d-200d-2642-fe0f',
'native': '🤽‍♂️',
},
{
'id': 'woman-playing-water-polo',
'unified': '1f93d-200d-2640-fe0f',
'native': '🤽‍♀️',
},
];
expect(search('polo').map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('polo').map(trimEmojis)).toEqual(expected);
});
it('can search for thinking_face', () => {
const expected = [
{
id: 'thinking_face',
unified: '1f914',
native: '🤔',
},
];
expect(search('thinking_fac').map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('thinking_fac').map(trimEmojis)).toEqual(expected);
});
it('can search for woman-facepalming', () => {
const expected = [
{
id: 'woman-facepalming',
unified: '1f926-200d-2640-fe0f',
native: '🤦‍♀️',
},
];
expect(search('woman-facep').map(trimEmojis)).toEqual(expected);
expect(emojiIndex.search('woman-facep').map(trimEmojis)).toEqual(expected);
});
});

View File

@@ -54,7 +54,7 @@ class Image extends React.PureComponent {
<img
alt={alt}
className={classes}
{...otherProps}
{...otherProps} // : todo : remove
ref={imageRef}
src={src}
onError={this.handleOnError}

View File

@@ -1,112 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import api from '../../api'
import ModalLayout from './modal_layout'
import Divider from '../divider'
import Icon from '../icon'
import Input from '../input'
import Text from '../text'
class EmbedModal extends ImmutablePureComponent {
state = {
loading: false,
oembed: null,
}
componentDidMount() {
const { url } = this.props
this.setState({ loading: true })
api().post('/api/web/embed', { url }).then(res => {
this.setState({ loading: false, oembed: res.data })
const iframeDocument = this.iframe.contentWindow.document
iframeDocument.open()
iframeDocument.write(res.data.html)
iframeDocument.close()
iframeDocument.body.style.margin = 0
this.iframe.width = iframeDocument.body.scrollWidth
this.iframe.height = iframeDocument.body.scrollHeight
}).catch(error => {
this.props.onError(error)
})
}
setIframeRef = c => {
this.iframe = c
}
handleTextareaClick = (e) => {
e.target.select()
}
render() {
const { intl, onClose } = this.props
const { oembed } = this.state
return (
<ModalLayout
title={intl.formatMessage(messages.embed)}
onClose={onClose}
>
<div className={_s.d}>
<Text className={_s.my10}>
{intl.formatMessage(messages.instructions)}
</Text>
<div className={[_s.d, _s.mb10].join(' ')}>
<Input
readOnly
type='text'
value={oembed && oembed.html || ''}
onClick={this.handleTextareaClick}
/>
</div>
<Divider />
<Text className={_s.mb10}>
{intl.formatMessage(messages.preview)}
</Text>
<div className={[_s.d, _s.w100PC, _s.bgSubtle, _s.h220PX, _s.aiCenter, _s.jcCenter].join(' ')}>
<iframe
className={[_s.d, _s.w100PC, _s.h100PC, _s.z2].join(' ')}
frameBorder='0'
ref={this.setIframeRef}
sandbox='allow-same-origin'
title='preview'
/>
{
!oembed &&
<Icon id='loading' size='34px' className={[_s.posAbs, _s.z3].join(' ')} />
}
</div>
</div>
</ModalLayout>
)
}
}
const messages = defineMessages({
embed: { id: 'status.embed', defaultMessage: 'Embed' },
instructions: { id: 'embed.instructions', defaultMessage: 'Embed this status on your website by copying the code below.' },
preview: { id: 'embed.preview', defaultMessage: 'Here is what it will look like:' },
})
EmbedModal.propTypes = {
url: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onError: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
export default injectIntl(EmbedModal)

View File

@@ -17,7 +17,6 @@ import {
MODAL_EDIT_PROFILE,
MODAL_EDIT_SHORTCUTS,
MODAL_EMAIL_CONFIRMATION_REMINDER,
MODAL_EMBED,
MODAL_GROUP_CREATE,
MODAL_GROUP_DELETE,
MODAL_GROUP_PASSWORD,
@@ -50,7 +49,6 @@ import {
EditProfileModal,
EditShortcutsModal,
EmailConfirmationReminderModal,
EmbedModal,
GroupCreateModal,
GroupDeleteModal,
GroupMembersModal,
@@ -86,7 +84,6 @@ MODAL_COMPONENTS[MODAL_DISPLAY_OPTIONS] = DisplayOptionsModal
MODAL_COMPONENTS[MODAL_EDIT_SHORTCUTS] = EditShortcutsModal
MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
MODAL_COMPONENTS[MODAL_EMAIL_CONFIRMATION_REMINDER] = EmailConfirmationReminderModal
MODAL_COMPONENTS[MODAL_EMBED] = EmbedModal
MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
MODAL_COMPONENTS[MODAL_GROUP_PASSWORD] = GroupPasswordModal

View File

@@ -12,19 +12,38 @@ import MediaGalleryPanelPlaceholder from '../placeholder/media_gallery_panel_pla
class MediaGalleryPanel extends ImmutablePureComponent {
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.shouldLoad && !prevState.fetched) {
return { fetched: true }
}
return null
}
componentDidUpdate(prevProps, prevState) {
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { limit: 8 }))
}
}
componentDidMount() {
const { accountId } = this.props
const { accountId, isLazy } = this.props
if (accountId && accountId !== -1) {
if (!isLazy && !!accountId && accountId !== -1) {
this.props.dispatch(expandAccountMediaTimeline(accountId, { limit: 8 }))
this.setState({ fetched: true })
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, { limit: 8 }))
}
}
// componentWillReceiveProps(nextProps) {
// if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
// this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, { limit: 8 }))
// }
// }
render() {
const {
@@ -33,8 +52,9 @@ class MediaGalleryPanel extends ImmutablePureComponent {
intl,
isLoading,
} = this.props
const { fetched } = this.state
if (!attachments) return null
if (!attachments && fetched) return null
return (
<PanelLayout

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { me, favouritesCount } from '../../initial_state'
import { me } from '../../initial_state'
import { shortNumberFormat } from '../../utils/numbers'
import PanelLayout from './panel_layout'
import UserStat from '../user_stat'
@@ -56,7 +56,7 @@ class ProfileStatsPanel extends ImmutablePureComponent {
account.get('id') === me &&
<UserStat
title={intl.formatMessage(messages.likes)}
value={shortNumberFormat(favouritesCount)}
value={shortNumberFormat(0)}
to={`/${account.get('acct')}/likes`}
isCentered={noPanel}
/>

View File

@@ -14,8 +14,6 @@ import {
unbookmark,
} from '../../actions/interactions';
import {
muteStatus,
unmuteStatus,
deleteStatus,
editStatus,
} from '../../actions/statuses';
@@ -27,7 +25,6 @@ import {
pinGroupStatus,
unpinGroupStatus,
} from '../../actions/groups'
import { initMuteModal } from '../../actions/mutes'
import { initReport } from '../../actions/reports'
import { openModal } from '../../actions/modal'
import {
@@ -35,7 +32,6 @@ import {
openPopover,
} from '../../actions/popover'
import {
MODAL_EMBED,
MODAL_PRO_UPGRADE,
POPOVER_STATUS_SHARE,
} from '../../constants'
@@ -57,13 +53,12 @@ class StatusOptionsPopover extends ImmutablePureComponent {
componentDidMount() {
if (!this.props.groupRelationships && this.props.groupId) {
this.props.onFetchGroupRelationships(this.props.groupId)
// : todo :
// check if pin
// check if bookmark
}
}
handleConversationMuteClick = () => {
this.props.onMuteConversation(this.props.status)
}
handleGroupRemoveAccount = () => {
const { status } = this.props
@@ -144,15 +139,6 @@ class StatusOptionsPopover extends ImmutablePureComponent {
let menu = []
if (me) {
if (status.getIn(['account', 'id']) === me) {
menu.push({
icon: 'audio-mute',
hideArrow: true,
title: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
onClick: this.handleConversationMuteClick,
})
}
if (isReply) {
menu.push({
icon: 'repost',
@@ -296,8 +282,6 @@ const messages = defineMessages({
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
like: { id: 'status.like', defaultMessage: 'Like' },
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
groupPin: { id: 'status.group_pin', defaultMessage: 'Pin in group' },
@@ -327,16 +311,6 @@ const mapStateToProps = (state, { status }) => {
const mapDispatchToProps = (dispatch) => ({
onMuteConversation(status) {
dispatch(closePopover())
if (status.get('muted')) {
dispatch(unmuteStatus(status.get('id')))
} else {
dispatch(muteStatus(status.get('id')))
}
},
onPin(status) {
dispatch(closePopover())
@@ -411,11 +385,6 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(editStatus(status))
},
onMute(account) {
dispatch(closePopover())
dispatch(initMuteModal(account))
},
onBlock(status) {
dispatch(closePopover())
const account = status.get('account')
@@ -480,11 +449,9 @@ StatusOptionsPopover.propTypes = {
onMute: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onReport: PropTypes.func.isRequired,
onMuteConversation: PropTypes.func.isRequired,
onPin: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
onFetchGroupRelationships: PropTypes.func.isRequired,
onOpenEmbedModal: PropTypes.func.isRequired,
onOpenProUpgradeModal: PropTypes.func.isRequired,
onClosePopover: PropTypes.func.isRequired,
isXS: PropTypes.bool,

View File

@@ -82,6 +82,7 @@ class DefaultSidebar extends ImmutablePureComponent {
</SidebarSectionTitle>
<SidebarSectionItem title='Home' icon='home' to='/home' count={homeItemsQueueCount} />
<SidebarSectionItem title='Notifications' icon='notifications' to='/notifications' count={notificationCount} />
<SidebarSectionItem title='Chats' icon='chat' to='/messages' />
<SidebarSectionItem title='Groups' icon='group' to='/groups' />
<SidebarSectionItem title='Lists' icon='list' to='/lists' />
<SidebarSectionItem title='Explore' icon='explore' to='/explore' />

View File

@@ -52,7 +52,7 @@ class TabBarItem extends React.PureComponent {
// Combine state, props, location to make absolutely
// sure of active status.
const active = isActive || (to === location.pathname && !location.search) || isCurrent
const active = (isActive === true || isCurrent || (to === location.pathname && !location.search))
const containerClasses = CX({
d: 1,

View File

@@ -0,0 +1,125 @@
import React from 'react'
import PropTypes from 'prop-types'
import {
CX,
TOAST_TYPE_ERROR,
TOAST_TYPE_SUCCESS,
// TOAST_TYPE_NOTIFICATION,
} from '../constants'
import Button from './button'
import Image from './image'
import RelativeTimestamp from './relative_timestamp'
import Text from './text'
class Toast extends React.PureComponent {
componentDidMount() {
this._timer = setTimeout(() => {
this.handleOnDismiss()
}, 5000)
}
componentWillUnmount() {
clearTimeout(this._timer)
}
handleOnDismiss = () => {
this.props.onDismiss(this.props.id)
}
render() {
const {
image,
isImageAccount,
title,
message,
date,
to,
type,
} = this.props
const contentClasses = CX({
default: 1,
mt5: 1,
pt2: 1,
maxWidth240PX: 1,
flexRow: !!image,
})
const innerContentClasses = CX({
default: 1,
flexNormal: 1,
pl10: !!image,
pt2: !!image,
displayInline: !!date && !image,
})
const imageClasses = CX({
radiusSmall: !isImageAccount,
circle: isImageAccount,
})
const dateClasses = CX({
mr5: 1,
mt2: !!image,
})
return (
<div className={[_s.default, _s.radiusSmall, _s.mb5, _s.px15, _s.pt10, _s.pb15, _s.bgPrimary, _s.boxShadowToast].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
<Text size='media' weight='medium' className={[_s.mr15, _s.minWidth160PX].join(' ')}>
{title}
</Text>
<Button
backgroundColor='secondary'
color='primary'
icon='close'
iconSize='6px'
onClick={this.handleOnDismiss}
className={[_s.mlAuto, _s.px10].join(' ')}
/>
</div>
<div className={contentClasses}>
{
!!image &&
<Image
src={image}
className={imageClasses}
height='52px'
width='52px'
/>
}
<div className={innerContentClasses}>
<Text size='small'>
{message}
</Text>
{
date &&
<Text color='secondary' size='extraSmall' className={dateClasses}>
<RelativeTimestamp timestamp={date} />
</Text>
}
</div>
</div>
</div>
)
}
}
Toast.propTypes = {
date: PropTypes.string.isRequired,
image: PropTypes.string,
isImageAccount: PropTypes.bool,
id: PropTypes.string.isRequired,
message: PropTypes.string.isRequired,
onDismiss: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
to: PropTypes.string,
type: PropTypes.oneOf([
TOAST_TYPE_ERROR,
TOAST_TYPE_SUCCESS,
]).isRequired,
}
export default Toast

View File

@@ -11,6 +11,8 @@ export const BREAKPOINT_MEDIUM = 1160
export const BREAKPOINT_SMALL = 1080
export const BREAKPOINT_EXTRA_SMALL = 992
export const LAZY_LOAD_SCROLL_OFFSET = 50
export const ALLOWED_AROUND_SHORT_CODE = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d'
export const MAX_POST_CHARACTER_COUNT = 3000
@@ -48,7 +50,6 @@ export const MODAL_DISPLAY_OPTIONS = 'DISPLAY_OPTIONS'
export const MODAL_EDIT_PROFILE = 'EDIT_PROFILE'
export const MODAL_EDIT_SHORTCUTS = 'EDIT_SHORTCUTS'
export const MODAL_EMAIL_CONFIRMATION_REMINDER = 'EMAIL_CONFIRMATION_REMINDER'
export const MODAL_EMBED = 'EMBED'
export const MODAL_GROUP_CREATE = 'GROUP_CREATE'
export const MODAL_GROUP_DELETE = 'GROUP_DELETE'
export const MODAL_GROUP_PASSWORD = 'GROUP_PASSWORD'

View File

@@ -0,0 +1,67 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { getToasts } from '../selectors'
import { dismissToast } from '../actions/toasts'
import { CX } from '../constants'
import Toast from '../components/toast'
class ToastsContainer extends React.PureComponent {
handleOnDismiss = (key) => {
this.props.dispatch(dismissToast(key))
}
render() {
const { notifications } = this.props
const hasNotifications = Array.isArray(notifications) && notifications.length > 0
const containerClasses = CX({
default: 1,
z5: 1,
posFixed: 1,
bottom0: 1,
left0: 1,
pl15: 1,
pt15: 1,
heightMax100VH: 1,
pb10: 1,
displayNone: !hasNotifications
})
return (
<div className={containerClasses}>
{
hasNotifications && notifications.map((notification) => (
<Toast
onDismiss={this.handleOnDismiss}
key={notification.key}
id={notification.key}
title={notification.title}
type={notification.type}
to={notification.to}
image={notification.image}
message={notification.message}
date={notification.date}
isImageAccount={notification.isImageAccount}
/>
))
}
</div>
)
}
}
const mapStateToProps = (state) => {
const notifications = getToasts(state)
if (!notifications) return {}
return { notifications }
}
ToastsContainer.propTypes = {
notifications: PropTypes.array,
}
export default connect(mapStateToProps)(ToastsContainer)

View File

@@ -10,7 +10,7 @@ import {
GAB_COM_INTRODUCE_YOURSELF_GROUP_ID,
} from '../constants'
import { me } from '../initial_state'
import { saveShownOnboarding } from '../actions/onboarding'
import { saveShownOnboarding } from '../actions/settings'
import { fetchGroups } from '../actions/groups'
import { saveUserProfileInformation } from '../actions/user'
import { makeGetAccount } from '../selectors'

View File

@@ -4,7 +4,6 @@ import { connect } from 'react-redux'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { FormattedMessage } from 'react-intl'
import { connectListStream } from '../actions/streaming'
import { expandListTimeline } from '../actions/timelines'
import { fetchList, deleteList } from '../actions/lists'
import { openModal } from '../actions/modal'
@@ -29,7 +28,6 @@ class ListTimeline extends ImmutablePureComponent {
componentWillReceiveProps(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
this.handleDisconnect()
this.handleConnect(nextProps.params.id)
}
}
@@ -39,15 +37,6 @@ class ListTimeline extends ImmutablePureComponent {
dispatch(fetchList(id))
dispatch(expandListTimeline(id))
this.disconnect = dispatch(connectListStream(id))
}
handleDisconnect() {
if (this.disconnect) {
this.disconnect()
this.disconnect = null
}
}
handleLoadMore = (maxId) => {

View File

@@ -0,0 +1,116 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { NavLink } from 'react-router-dom'
import { CX } from '../../../constants'
import Input from '../../../components/input'
import Avatar from '../../../components/avatar'
import Button from '../../../components/button'
import Text from '../../../components/text'
import RelativeTimestamp from '../../../components/relative_timestamp'
import { makeGetAccount } from '../../../selectors'
class MessagesItem extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
state = {
hovering: false,
}
handleOnMouseEnter = () => {
this.setState({ isHovering: true })
}
handleOnMouseLeave = () => {
this.setState({ isHovering: false })
}
render() {
const {
account,
intl,
alt,
} = this.props
const { isHovering } = this.state
const content = { __html: 'REEEE i heard you have the sauce2?' }
const messageContainerClasses = CX({
d: 1,
flexRow: !alt,
flexRowReverse: alt,
})
const messageInnerContainerClasses = CX({
d: 1,
px15: 1,
py5: 1,
bgSecondary: !alt,
bgBrandLight: alt,
circle: 1,
ml10: 1,
mr10: 1,
})
const lowerContainerClasses = CX({
d: 1,
pt10: 1,
pl50: !alt,
pr50: alt,
})
const buttonContainerClasses = CX({
d: 1,
flexRow: 1,
displayNone: !isHovering,
})
return (
<div
className={[_s.d, _s.w100PC, _s.pb10].join(' ')}
onMouseEnter={this.handleOnMouseEnter}
onMouseLeave={this.handleOnMouseLeave}
>
<div className={[_s.d, _s.w100PC, _s.pb15].join(' ')}>
<div className={messageContainerClasses}>
<Avatar account={account} size={38} />
<div className={messageInnerContainerClasses}>
<div className={[_s.py5, _s.dangerousContent].join(' ')} dangerouslySetInnerHTML={content} />
</div>
<div className={buttonContainerClasses}>
<Button
onClick={undefined}
color='tertiary'
backgroundColor='none'
icon='ellipsis'
iconSize='18px'
/>
</div>
</div>
<div className={lowerContainerClasses}>
<Text size='small' color='tertiary' align={alt ? 'right' : 'left'}>
Apr 16, 2020, 8:20 AM
{ /* <RelativeTimestamp timestamp={'2020-20-10'} /> */ }
</Text>
</div>
</div>
</div>
)
}
}
const mapStateToProps = (state, props) => ({
account: makeGetAccount()(state, '1'),
})
MessagesItem.propTypes = {
intl: PropTypes.object.isRequired,
alt: PropTypes.bool,
}
export default connect(mapStateToProps)(MessagesItem)

View File

@@ -0,0 +1,47 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import Heading from '../../../components/heading'
import Button from '../../../components/button'
class MessagesHeader extends ImmutablePureComponent {
render() {
const {
account,
} = this.props
return (
<div className={[_s.d, _s.w100PC, _s.h60PX, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
<div className={[_s.d, _s.flexRow, _s.pl15, _s.pr5, _s.py15].join(' ')}>
<Heading size='h1'>
Messages
</Heading>
<div className={[_s.d, _s.bgTransparent, _s.flexRow, _s.aiCenter, _s.jcCenter, _s.mlAuto].join(' ')}>
<Button
isNarrow
onClick={undefined}
className={[_s.ml5, _s.px15].join(' ')}
>
New
</Button>
<Button
isNarrow
onClick={undefined}
color='brand'
backgroundColor='none'
className={_s.ml5}
icon='cog'
iconSize='18px'
/>
</div>
</div>
</div>
)
}
}
export default MessagesHeader

View File

@@ -0,0 +1,36 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import MessagesListItem from './messages_list_item'
import { makeGetAccount } from '../../../selectors'
class MessagesList extends ImmutablePureComponent {
render() {
const {
account,
} = this.props
return (
<div className={[_s.d, _s.w100PC].join(' ')}>
<MessagesListItem />
<MessagesListItem selected />
<MessagesListItem />
<MessagesListItem />
</div>
)
}
}
const mapStateToProps = (state, props) => ({
account: makeGetAccount()(state, '1'),
})
MessagesList.propTypes = {
intl: PropTypes.object.isRequired,
}
export default connect(mapStateToProps)(MessagesList)

View File

@@ -0,0 +1,107 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { NavLink } from 'react-router-dom'
import { CX } from '../../../constants'
import Input from '../../../components/input'
import DisplayName from '../../../components/display_name'
import Avatar from '../../../components/avatar'
import Text from '../../../components/text'
import RelativeTimestamp from '../../../components/relative_timestamp'
import { makeGetAccount } from '../../../selectors'
class MessagesListItem extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
state = {
composeFocused: false,
}
render() {
const {
account,
intl,
selected,
} = this.props
const buttonClasses = CX({
d: 1,
pt2: 1,
pr5: 1,
noUnderline: 1,
overflowHidden: 1,
flexNormal: 1,
flexRow: 1,
aiStart: 1,
aiCenter: 1,
})
const containerClasses = CX({
d: 1,
bgSubtle_onHover: 1,
borderBottom1PX: 1,
borderColorSecondary: 1,
noUnderline: 1,
})
const innerContainerClasses = CX({
d: 1,
flexRow: 1,
aiStart: 1,
aiCenter: 0,
px15: 1,
py15: 1,
borderRight4PX: selected,
borderColorBrand: selected,
})
const avatarSize = 49
const content = { __html: 'REEEE i heard you have the sauce?' }
return (
<NavLink
className={containerClasses}
title={account.get('acct')}
to={`/messages/conversation-id`}
>
<div className={innerContainerClasses}>
<Avatar account={account} size={avatarSize} noHover />
<div className={[_s.d, _s.pl10, _s.overflowHidden, _s.flexNormal].join(' ')}>
<div className={[_s.d, _s.flexRow, _s.aiCenter].join(' ')}>
<div className={buttonClasses}>
<div className={_s.maxW100PC42PX}>
<DisplayName account={account} noHover />
</div>
<Text size='extraSmall' color='secondary' className={_s.mlAuto}>
May 1
{ /* <RelativeTimestamp timestamp={'2020-20-10'} /> */ }
</Text>
</div>
</div>
<div className={[_s.py5, _s.dangerousContent].join(' ')} dangerouslySetInnerHTML={content} />
</div>
</div>
</NavLink>
)
}
}
const mapStateToProps = (state, props) => ({
account: makeGetAccount()(state, '1'),
})
MessagesListItem.propTypes = {
intl: PropTypes.object.isRequired,
selected: PropTypes.bool,
}
export default connect(mapStateToProps)(MessagesListItem)

View File

@@ -0,0 +1,45 @@
import React from 'react'
import PropTypes from 'prop-types'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import Input from '../../../components/input'
class MessagesSearch extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
state = {
composeFocused: false,
}
render() {
const {
intl,
} = this.props
return (
<div className={[_s.d, _s.h60PX, _s.w100PC, _s.px10, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
<Input
type='search'
placeholder='Search for messages'
id='messages-search'
prependIcon='search'
/>
</div>
)
}
}
const messages = defineMessages({
placeholder: { id: 'compose_form.placeholder', defaultMessage: "What's on your mind?" },
})
MessagesSearch.propTypes = {
intl: PropTypes.object.isRequired,
}
export default injectIntl(MessagesSearch)

View File

@@ -0,0 +1 @@
export { default } from './messages'

View File

@@ -0,0 +1,103 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { makeGetAccount } from '../../selectors'
import Text from '../../components/text'
import Button from '../../components/button'
import Avatar from '../../components/avatar'
import DisplayName from '../../components/display_name'
import Input from '../../components/input'
import EmojiPickerButton from '../compose/components/emoji_picker_button'
import UploadButton from '../compose/components/media_upload_button'
import MessageItem from './components/message_item'
// import MessagesContainer from './containers/messages_container'
class Messages extends React.PureComponent {
render () {
const { account } = this.props
const selectedMessage = true
return (
<div className={[_s.d, _s.bgPrimary, _s.h100PC, _s.w100PC].join(' ')}>
{
!selectedMessage &&
<div className={[_s.d, _s.w100PC, _s.h100PC, _s.aiCenter, _s.jcCenter].join(' ')}>
<Text weight='bold' size='extraLarge'>
You dont have a message selected
</Text>
<Text size='medium' color='secondary' className={_s.py10}>
Choose one from your existing messages, or start a new one.
</Text>
<Button className={_s.mt10}>
<Text color='inherit' weight='bold' className={_s.px15}>
New Message
</Text>
</Button>
</div>
}
{
selectedMessage &&
<div className={[_s.d, _s.h100PC, _s.w100PC].join(' ')}>
<div className={[_s.d, _s.posAbs, _s.top0, _s.left0, _s.right0, _s.flexRow, _s.aiCenter, _s.h60PX, _s.w100PC, _s.borderBottom1PX, _s.borderColorSecondary, _s.px15, _s.py5].join(' ')}>
<Avatar account={account} size={34} />
<div className={[_s.d, _s.pl10, _s.maxW100PC86PX, _s.overflowHidden].join(' ')}>
<DisplayName account={account} isMultiline />
</div>
<Button
isNarrow
onClick={undefined}
color='brand'
backgroundColor='none'
className={_s.mlAuto}
icon='more'
iconSize='18px'
/>
</div>
<div className={[_s.d, _s.posAbs, _s.bottom60PX, _s.left0, _s.right0, _s.px15, _s.py15, _s.top60PX, _s.w100PC, _s.overflowYScroll].join(' ')}>
<MessageItem />
<MessageItem />
<MessageItem alt />
<MessageItem />
<MessageItem alt />
<MessageItem alt />
<MessageItem />
<MessageItem />
<MessageItem />
<MessageItem alt />
<MessageItem />
</div>
<div className={[_s.d, _s.posAbs, _s.bottom0, _s.left0, _s.right0, _s.flexRow, _s.aiCenter, _s.h60PX, _s.w100PC, _s.borderTop1PX, _s.borderColorSecondary, _s.px15, _s.py5].join(' ')}>
<EmojiPickerButton />
<UploadButton />
<div className={[_s.d, _s.px15, _s.flexGrow1].join(' ')}>
<Input
placeholder='Type a message...'
/>
</div>
<Button>
Send
</Button>
</div>
</div>
}
</div>
)
}
}
const mapStateToProps = (state, props) => ({
account: makeGetAccount()(state, '1'),
})
Messages.propTypes = {
intl: PropTypes.object.isRequired,
selected: PropTypes.bool,
}
export default connect(mapStateToProps)(Messages)

View File

@@ -3,7 +3,10 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import throttle from 'lodash.throttle'
import { fetchPopularLinks } from '../actions/links'
import { BREAKPOINT_EXTRA_SMALL } from '../constants'
import {
BREAKPOINT_EXTRA_SMALL,
LAZY_LOAD_SCROLL_OFFSET,
} from '../constants'
import Button from '../components/button'
import Text from '../components/text'
import TrendsItem from '../components/trends_item'
@@ -46,14 +49,12 @@ class News extends React.PureComponent {
if (this.window) {
const { scrollTop } = this.documentElement
if (scrollTop > 25 && !this.state.lazyLoaded) {
if (scrollTop > LAZY_LOAD_SCROLL_OFFSET && !this.state.lazyLoaded) {
this.setState({ lazyLoaded: true })
this.detachScrollListener()
}
}
}, 150, {
trailing: true,
})
}, 150, { trailing: true })
render() {
const { children } = this.props

View File

@@ -3,7 +3,6 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { defineMessages, injectIntl } from 'react-intl'
import { expandProTimeline } from '../actions/timelines'
import { connectProStream } from '../actions/streaming'
import StatusList from '../components/status_list'
class ProTimeline extends React.PureComponent {
@@ -16,15 +15,6 @@ class ProTimeline extends React.PureComponent {
const { dispatch } = this.props
dispatch(expandProTimeline())
this.disconnect = dispatch(connectProStream())
}
componentWillUnmount() {
if (this.disconnect) {
this.disconnect()
this.disconnect = null
}
}
handleLoadMore = (maxId) => {

View File

@@ -6,7 +6,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import { withRouter } from 'react-router-dom'
import { me } from '../initial_state'
import ResponsiveClassesComponent from '../features/ui/util/responsive_classes_component'
import HashtagItem from '../components/hashtag_item'
import GroupListItem from '../components/group_list_item'
import Text from '../components/text'
import Account from '../components/account'
@@ -60,14 +59,13 @@ class Search extends ImmutablePureComponent {
const pathname = location.pathname || ''
const showPeople = pathname === '/search/people'
const showHashtags = pathname === '/search/hashtags'
const showGroups = pathname === '/search/groups'
const showStatuses = pathname === '/search/statuses'
const showLinks = pathname === '/search/links'
const isTop = !showPeople && !showHashtags && !showGroups && !showStatuses && !showLinks
const isTop = !showPeople && !showGroups && !showStatuses && !showLinks
const theLimit = 4
let accounts, statuses, hashtags, groups, links
let accounts, statuses, groups, links
if (results.get('accounts') && results.get('accounts').size > 0 && (isTop || showPeople)) {
const size = isTop ? Math.min(results.get('accounts').size, theLimit) : results.get('accounts').size;
@@ -192,30 +190,7 @@ class Search extends ImmutablePureComponent {
)
}
if (results.get('hashtags') && results.get('hashtags').size > 0 && me && (isTop || showHashtags)) {
const size = isTop ? Math.min(results.get('hashtags').size, theLimit) : results.get('hashtags').size;
const isMax = size === results.get('hashtags').size
hashtags = (
<PanelLayout
title='Hashtags'
headerButtonTo={isMax ? undefined : '/search/hashtags'}
headerButtonTitle={isMax ? undefined : 'See more'}
footerButtonTo={isMax ? undefined : '/search/hashtags'}
footerButtonTitle={isMax ? undefined : 'See more'}
noPadding
>
<div className={[_s.d, _s.pb10, _s.px15, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
<Text color='tertiary' size='small'>
Showing {size} of {results.get('hashtags').size} results
</Text>
</div>
{results.get('hashtags').slice(0, size).map(hashtag => <HashtagItem isCompact key={hashtag.get('name')} hashtag={hashtag} />)}
</PanelLayout>
)
}
if (!accounts && !statuses && !hashtags && !groups && !links) {
if (!accounts && !statuses && !groups && !links) {
return (
<ResponsiveClassesComponent classNamesXS={[_s.px10, _s.pt15].join(' ')}>
<Block>
@@ -231,7 +206,6 @@ class Search extends ImmutablePureComponent {
{groups}
{statuses}
{links}
{hashtags}
</div>
)
}

View File

@@ -23,6 +23,7 @@ import { clearHeight } from '../../actions/height_cache'
import { openModal } from '../../actions/modal'
import WrappedRoute from './util/wrapped_route'
import ModalRoot from '../../components/modal/modal_root'
import ToastsContainer from '../../containers/toasts_container'
import PopoverRoot from '../../components/popover/popover_root'
import UploadArea from '../../components/upload_area'
import ProfilePage from '../../pages/profile_page'
@@ -45,6 +46,8 @@ import ExplorePage from '../../pages/explore_page'
import NewsPage from '../../pages/news_page'
import AboutPage from '../../pages/about_page'
import LinkPage from '../../pages/link_page'
import MessagesPage from '../../pages/messages_page'
import ComposePage from '../../pages/compose_page'
import {
About,
@@ -83,6 +86,7 @@ import {
ListsDirectory,
ListEdit,
ListTimeline,
Messages,
Mutes,
News,
NewsView,
@@ -190,12 +194,14 @@ class SwitchingArea extends React.PureComponent {
<WrappedRoute path='/explore' publicRoute page={ExplorePage} component={ExploreTimeline} content={children} componentParams={{ title: 'Explore' }} />
<WrappedRoute path='/suggestions' exact page={BasicPage} component={Suggestions} content={children} componentParams={{ title: 'Suggestions' }} />
<WrappedRoute path='/compose' exact page={BasicPage} component={Compose} content={children} componentParams={{ title: 'Compose', page: 'compose' }} />
<WrappedRoute path='/compose' exact page={ComposePage} component={Compose} content={children} componentParams={{ title: 'Compose', page: 'compose' }} />
<WrappedRoute path='/news' exact publicRoute page={NewsPage} component={News} content={children} componentParams={{ title: 'News' }} />
<WrappedRoute path='/news/view/:trendsRSSId' page={NewsPage} component={NewsView} content={children} componentParams={{ title: 'News RSS Feed' }} />
<WrappedRoute path='/messages' exact page={MessagesPage} component={Messages} content={children} />
<WrappedRoute path='/messages/:conversationId' exact page={MessagesPage} component={Messages} content={children} />
<WrappedRoute path='/timeline/all' exact page={CommunityPage} component={CommunityTimeline} content={children} componentParams={{ title: 'Community Feed' }} />
<WrappedRoute path='/timeline/pro' exact page={ProPage} component={ProTimeline} content={children} componentParams={{ title: 'Pro Feed' }} />
@@ -234,7 +240,6 @@ class SwitchingArea extends React.PureComponent {
<WrappedRoute path='/search' exact publicRoute page={SearchPage} component={Search} content={children} />
<WrappedRoute path='/search/people' exact publicRoute page={SearchPage} component={Search} content={children} />
<WrappedRoute path='/search/hashtags' exact publicRoute page={SearchPage} component={Search} content={children} />
<WrappedRoute path='/search/groups' exact publicRoute page={SearchPage} component={Search} content={children} />
<WrappedRoute path='/search/statuses' exact publicRoute page={SearchPage} component={Search} content={children} />
<WrappedRoute path='/search/links' exact page={SearchPage} component={Search} content={children} />
@@ -593,6 +598,8 @@ class UI extends React.PureComponent {
<PopoverRoot />
<UploadArea active={draggingOver} onClose={this.closeUploadModal} />
<ToastsContainer />
</div>
)
}

View File

@@ -20,7 +20,6 @@ export function DMCA() { return import(/* webpackChunkName: "features/about/dmca
export function EditProfileModal() { return import(/* webpackChunkName: "components/edit_profile_modal" */'../../../components/modal/edit_profile_modal') }
export function EditShortcutsModal() { return import(/* webpackChunkName: "components/edit_shortcuts_modal" */'../../../components/modal/edit_shortcuts_modal') }
export function EmailConfirmationReminderModal() { return import(/* webpackChunkName: "components/email_confirmation_reminder_modal" */'../../../components/modal/email_confirmation_reminder_modal') }
export function EmbedModal() { return import(/* webpackChunkName: "modals/embed_modal" */'../../../components/modal/embed_modal') }
export function EmojiPicker() { return import(/* webpackChunkName: "emoji_picker" */'../../../components/emoji/emoji_picker') }
export function EmojiPickerPopover() { return import(/* webpackChunkName: "components/emoji_picker_popover" */'../../../components/popover/emoji_picker_popover') }
export function ExploreTimeline() { return import(/* webpackChunkName: "features/explore_timeline" */'../../explore_timeline') }

View File

@@ -20,7 +20,6 @@ export const isStaff = getMeta('is_staff');
export const unreadCount = getMeta('unread_count');
export const lastReadNotificationId = getMeta('last_read_notification_id');
export const monthlyExpensesComplete = getMeta('monthly_expenses_complete');
export const favouritesCount = getMeta('favourites_count');
export const isFirstSession = getMeta('is_first_session');
export const emailConfirmed = getMeta('email_confirmed');
export const meEmail = getMeta('email');

View File

@@ -5,7 +5,10 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import throttle from 'lodash.throttle'
import Sticky from 'react-stickynode'
import { me } from '../initial_state'
import { BREAKPOINT_EXTRA_SMALL } from '../constants'
import {
BREAKPOINT_EXTRA_SMALL,
LAZY_LOAD_SCROLL_OFFSET,
} from '../constants'
import Layout from './layout'
import SidebarPanelGroup from '../components/sidebar_panel_group'
import Responsive from '../features/ui/util/responsive_component'
@@ -43,14 +46,12 @@ class ExploreLayout extends ImmutablePureComponent {
if (this.window) {
const { scrollTop } = this.documentElement
if (scrollTop > 25 && !this.state.lazyLoaded) {
if (scrollTop > LAZY_LOAD_SCROLL_OFFSET && !this.state.lazyLoaded) {
this.setState({ lazyLoaded: true })
this.detachScrollListener()
}
}
}, 150, {
trailing: true,
})
}, 150, { trailing: true })
render() {
const { children, title } = this.props
@@ -67,7 +68,7 @@ class ExploreLayout extends ImmutablePureComponent {
<WrappedBundle component={GroupsPanel} componentParams={{ groupType: 'featured' }} />,
]
if (!!me) {
layout.push(<WrappedBundle component={UserSuggestionsPanel} componentParams={{ suggestionType: 'verified' }} />)
layout.push(<WrappedBundle component={UserSuggestionsPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded, suggestionType: 'verified' }} />)
}
layout.push(<WrappedBundle component={TrendsBreakingPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />)

View File

@@ -40,13 +40,15 @@ class Layout extends React.PureComponent {
title,
} = this.props
const noPaddingPages = ['group', 'messages']
const hasPadding = noPaddingPages.indexOf(page) === -1
const mainBlockClasses = CX({
d: 1,
w1015PX: 1,
flexRow: 1,
jcEnd: 1,
py15: page !== 'group',
pb15: page === 'group',
py15: hasPadding,
pb15: hasPadding,
})
return (

View File

@@ -0,0 +1,96 @@
import React from 'react'
import PropTypes from 'prop-types'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { me } from '../initial_state'
import {
CX,
BREAKPOINT_EXTRA_SMALL,
} from '../constants'
import Layout from './layout'
import Responsive from '../features/ui/util/responsive_component'
import ResponsiveClassesComponent from '../features/ui/util/responsive_classes_component'
import MessagesSearch from '../features/messages/components/messages_search'
import MessagesList from '../features/messages/components/messages_list'
import MessagesHeader from '../features/messages/components/messages_header'
class MessagesLayout extends ImmutablePureComponent {
render() {
const {
children,
showBackBtn,
title,
} = this.props
const mainBlockClasses = CX({
d: 1,
w1015PX: 1,
h100PC: 1,
flexRow: 1,
jcEnd: 1,
})
return (
<Layout
showBackBtn
showGlobalFooter
noRightSidebar
showLinkFooterInSidebar
page='messages'
title='Chats'
actions={[
{
icon: 'cog',
onClick: this.onOpenCommunityPageSettingsModal,
},
{
icon: 'pencil',
onClick: this.onOpenCommunityPageSettingsModal,
},
]}
>
<div className={[_s.d, _s.flexRow, _s.w100PC, _s.calcH53PX].join(' ')}>
<ResponsiveClassesComponent
classNames={[_s.d, _s.flexShrink1, _s.flexGrow1].join(' ')}
classNamesSmall={[_s.d, _s.flexShrink1, _s.flexGrow1].join(' ')}
classNamesXS={[_s.d, _s.w100PC].join(' ')}
>
<ResponsiveClassesComponent
classNames={mainBlockClasses}
classNamesXS={[_s.d, _s.w100PC, _s.jcEnd].join(' ')}
>
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
<div className={[_s.d, _s.w340PX, _s.h100PC, _s.bgPrimary, _s.borderLeft1PX, _s.borderRight1PX, _s.borderColorSecondary].join(' ')}>
<div className={[_s.d, _s.w340PX].join(' ')}>
{ /* <MessagesHeader /> */ }
<MessagesSearch />
<MessagesList />
</div>
</div>
</Responsive>
<div className={[_s.d, _s.flexGrow1, _s.h100PC, _s.bgPrimary, _s.borderColorSecondary, _s.borderRight1PX, _s.z1].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.h100PC].join(' ')}>
{children}
</div>
</div>
</ResponsiveClassesComponent>
</ResponsiveClassesComponent>
</div>
</Layout>
)
}
}
MessagesLayout.propTypes = {
children: PropTypes.node,
showBackBtn: PropTypes.bool,
title: PropTypes.string,
}
export default MessagesLayout

View File

@@ -4,8 +4,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { BREAKPOINT_EXTRA_SMALL } from '../constants'
import Sticky from 'react-stickynode'
import throttle from 'lodash.throttle'
import { me } from '../initial_state'
import { CX } from '../constants'
import {
CX,
LAZY_LOAD_SCROLL_OFFSET,
} from '../constants'
import DefaultNavigationBar from '../components/navigation_bar/default_navigation_bar'
import FooterBar from '../components/footer_bar'
import ProfileHeader from '../components/profile_header'
@@ -26,6 +30,37 @@ import {
class ProfileLayout extends ImmutablePureComponent {
state = {
lazyLoaded: false,
}
componentDidMount() {
this.window = window
this.documentElement = document.scrollingElement || document.documentElement
this.window.addEventListener('scroll', this.handleScroll)
}
componentWillUnmount() {
this.detachScrollListener()
}
detachScrollListener = () => {
this.window.removeEventListener('scroll', this.handleScroll)
}
handleScroll = throttle(() => {
if (this.window) {
const { scrollTop } = this.documentElement
if (scrollTop > LAZY_LOAD_SCROLL_OFFSET && !this.state.lazyLoaded) {
this.setState({ lazyLoaded: true })
this.detachScrollListener()
}
}
}, 150, { trailing: true })
render() {
const {
account,
@@ -34,6 +69,7 @@ class ProfileLayout extends ImmutablePureComponent {
unavailable,
noSidebar,
} = this.props
const { lazyLoaded } = this.state
const mainContentClasses = CX({
d: 1,
@@ -121,7 +157,7 @@ class ProfileLayout extends ImmutablePureComponent {
<div className={[_s.d, _s.w340PX].join(' ')}>
<WrappedBundle component={ProfileStatsPanel} componentParams={{ account }} />
<WrappedBundle component={ProfileInfoPanel} componentParams={{ account }} />
{ !unavailable && <WrappedBundle component={MediaGalleryPanel} componentParams={{ account }} /> }
{ !unavailable && <WrappedBundle component={MediaGalleryPanel} componentParams={{ account, isLazy: true, shouldLoad: lazyLoaded }} />}
{ !me && <WrappedBundle component={SignUpPanel} /> }
<WrappedBundle component={LinkFooter} />
</div>

View File

@@ -0,0 +1,40 @@
import React from 'react'
import PropTypes from 'prop-types'
import PageTitle from '../features/ui/util/page_title'
import DefaultLayout from '../layouts/default_layout'
class ComposePage extends React.PureComponent {
render() {
const {
children,
page,
title,
} = this.props
return (
<DefaultLayout
noComposeButton
showBackBtn
title={title}
page={page}
actions={[
{
title: 'Post',
onClick: this.onOpenCommunityPageSettingsModal,
},
]}
>
<PageTitle path={title} />
{children}
</DefaultLayout>
)
}
}
ComposePage.propTypes = {
children: PropTypes.node.isRequired,
}
export default ComposePage

View File

@@ -2,13 +2,17 @@ import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import throttle from 'lodash.throttle'
import { openModal } from '../actions/modal'
import { defineMessages, injectIntl } from 'react-intl'
import { MODAL_HOME_TIMELINE_SETTINGS } from '../constants'
import { openModal } from '../actions/modal'
import {
MODAL_HOME_TIMELINE_SETTINGS,
LAZY_LOAD_SCROLL_OFFSET,
} from '../constants'
import { me } from '../initial_state'
import PageTitle from '../features/ui/util/page_title'
import DefaultLayout from '../layouts/default_layout'
import TimelineComposeBlock from '../components/timeline_compose_block'
import TabBar from '../components/tab_bar'
import WrappedBundle from '../features/ui/util/wrapped_bundle'
import {
UserPanel,
@@ -47,7 +51,7 @@ class HomePage extends React.PureComponent {
if (this.window) {
const { scrollTop } = this.documentElement
if (scrollTop > 25 && !this.state.lazyLoaded) {
if (scrollTop > LAZY_LOAD_SCROLL_OFFSET && !this.state.lazyLoaded) {
this.setState({ lazyLoaded: true })
this.detachScrollListener()
}
@@ -98,11 +102,10 @@ class HomePage extends React.PureComponent {
path={title}
badge={totalQueuedItemsCount}
/>
<TimelineComposeBlock autoFocus={false} />
{children}
</DefaultLayout>
)
}

View File

@@ -0,0 +1,38 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { defineMessages, injectIntl } from 'react-intl'
import PageTitle from '../features/ui/util/page_title'
import MessagesLayout from '../layouts/messages_layout'
class MessagesPage extends React.PureComponent {
render() {
const { children, intl } = this.props
const title = intl.formatMessage(messages.chats)
return (
<MessagesLayout
showBackBtn
title={title}
>
<PageTitle path={title} />
{children}
</MessagesLayout>
)
}
}
const messages = defineMessages({
chats: { id: 'chats', defaultMessage: 'Chats' },
})
MessagesPage.propTypes = {
intl: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
dispatch: PropTypes.func.isRequired,
}
export default injectIntl(connect()(MessagesPage))

View File

@@ -15,7 +15,9 @@ import ProfileLayout from '../layouts/profile_layout'
class ProfilePage extends ImmutablePureComponent {
componentDidMount() {
this.props.dispatch(fetchAccountByUsername(this.props.params.username))
if (!this.props.account) {
this.props.dispatch(fetchAccountByUsername(this.props.params.username))
}
}
render() {

View File

@@ -1,102 +0,0 @@
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import {
CONVERSATIONS_MOUNT,
CONVERSATIONS_UNMOUNT,
CONVERSATIONS_FETCH_REQUEST,
CONVERSATIONS_FETCH_SUCCESS,
CONVERSATIONS_FETCH_FAIL,
CONVERSATIONS_UPDATE,
CONVERSATIONS_READ,
} from '../actions/conversations';
import compareId from '../utils/compare_id';
const initialState = ImmutableMap({
items: ImmutableList(),
isLoading: false,
hasMore: true,
mounted: false,
});
const conversationToMap = item => ImmutableMap({
id: item.id,
unread: item.unread,
accounts: ImmutableList(item.accounts.map(a => a.id)),
last_status: item.last_status ? item.last_status.id : null,
});
const updateConversation = (state, item) => state.update('items', list => {
const index = list.findIndex(x => x.get('id') === item.id);
const newItem = conversationToMap(item);
if (index === -1) {
return list.unshift(newItem);
} else {
return list.set(index, newItem);
}
});
const expandNormalizedConversations = (state, conversations, next, isLoadingRecent) => {
let items = ImmutableList(conversations.map(conversationToMap));
return state.withMutations(mutable => {
if (!items.isEmpty()) {
mutable.update('items', list => {
list = list.map(oldItem => {
const newItemIndex = items.findIndex(x => x.get('id') === oldItem.get('id'));
if (newItemIndex === -1) {
return oldItem;
}
const newItem = items.get(newItemIndex);
items = items.delete(newItemIndex);
return newItem;
});
list = list.concat(items);
return list.sortBy(x => x.get('last_status'), (a, b) => {
if(a === null || b === null) {
return -1;
}
return compareId(a, b) * -1;
});
});
}
if (!next && !isLoadingRecent) {
mutable.set('hasMore', false);
}
mutable.set('isLoading', false);
});
};
export default function conversations(state = initialState, action) {
switch (action.type) {
case CONVERSATIONS_FETCH_REQUEST:
return state.set('isLoading', true);
case CONVERSATIONS_FETCH_FAIL:
return state.set('isLoading', false);
case CONVERSATIONS_FETCH_SUCCESS:
return expandNormalizedConversations(state, action.conversations, action.next, action.isLoadingRecent);
case CONVERSATIONS_UPDATE:
return updateConversation(state, action.conversation);
case CONVERSATIONS_MOUNT:
return state.update('mounted', count => count + 1);
case CONVERSATIONS_UNMOUNT:
return state.update('mounted', count => count - 1);
case CONVERSATIONS_READ:
return state.update('items', list => list.map(item => {
if (item.get('id') === action.id) {
return item.set('unread', false);
}
return item;
}));
default:
return state;
}
};

View File

@@ -4,7 +4,6 @@ import accounts from './accounts'
import accounts_counters from './accounts_counters'
import compose from './compose'
import contexts from './contexts'
import conversations from './conversations'
import custom_emojis from './custom_emojis'
import filters from './filters'
import groups from './groups'
@@ -49,7 +48,6 @@ const reducers = {
accounts_counters,
compose,
contexts,
conversations,
custom_emojis,
filters,
groups,

View File

@@ -31,6 +31,7 @@ const initialState = ImmutableMap({
top: false,
unread: 0,
isLoading: false,
isError: false,
queuedNotifications: ImmutableList(), //max = MAX_QUEUED_NOTIFICATIONS
totalQueuedNotificationsCount: 0, //used for queuedItems overflow for MAX_QUEUED_NOTIFICATIONS+
lastReadId: -1,
@@ -271,7 +272,10 @@ export default function notifications(state = initialState, action) {
case NOTIFICATIONS_EXPAND_REQUEST:
return state.set('isLoading', true);
case NOTIFICATIONS_EXPAND_FAIL:
return state.set('isLoading', false);
return state.withMutations(mutable => {
mutable.set('isLoading', false)
mutable.set('isError', true)
})
case NOTIFICATIONS_FILTER_SET:
return state.withMutations(mutable => {
mutable.set('items', ImmutableList()).set('hasMore', true)

View File

@@ -9,8 +9,6 @@ import {
ACCOUNT_UNBLOCK_SUCCESS,
ACCOUNT_MUTE_SUCCESS,
ACCOUNT_UNMUTE_SUCCESS,
ACCOUNT_PIN_SUCCESS,
ACCOUNT_UNPIN_SUCCESS,
RELATIONSHIPS_FETCH_SUCCESS,
} from '../actions/accounts';
import { Map as ImmutableMap, fromJS } from 'immutable';
@@ -43,8 +41,6 @@ export default function relationships(state = initialState, action) {
case ACCOUNT_UNBLOCK_SUCCESS:
case ACCOUNT_MUTE_SUCCESS:
case ACCOUNT_UNMUTE_SUCCESS:
case ACCOUNT_PIN_SUCCESS:
case ACCOUNT_UNPIN_SUCCESS:
return normalizeRelationship(state, action.relationship);
case RELATIONSHIPS_FETCH_SUCCESS:
return normalizeRelationships(state, action.relationships);

View File

@@ -12,6 +12,7 @@ import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
const initialState = ImmutableMap({
items: ImmutableList(),
isLoading: false,
isFetched: false,
isError: false,
})
@@ -50,17 +51,20 @@ export default function shortcutsReducer(state = initialState, action) {
case SHORTCUTS_FETCH_REQUEST:
return state.withMutations((map) => {
map.set('isLoading', true)
map.set('isFetched', false)
map.set('isError', false)
})
case SHORTCUTS_FETCH_SUCCESS:
return state.withMutations((map) => {
map.set('items', normalizeShortcuts(action.shortcuts))
map.set('isLoading', false)
map.set('isFetched', true)
map.set('isError', false)
})
case SHORTCUTS_FETCH_FAIL:
return state.withMutations((map) => {
map.set('isLoading', false)
map.set('isFetched', true)
map.set('isError', true)
})
case SHORTCUTS_ADD_SUCCESS:

View File

@@ -15,14 +15,6 @@ import {
BOOKMARKED_STATUSES_EXPAND_FAIL,
} from '../actions/bookmarks'
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import {
FAVORITE_SUCCESS,
UNFAVORITE_SUCCESS,
PIN_SUCCESS,
UNPIN_SUCCESS,
BOOKMARK_SUCCESS,
UNBOOKMARK_SUCCESS,
} from '../actions/interactions';
const initialState = ImmutableMap({
bookmarks: ImmutableMap({
@@ -35,11 +27,6 @@ const initialState = ImmutableMap({
loaded: false,
items: ImmutableList(),
}),
pins: ImmutableMap({
next: null,
loaded: false,
items: ImmutableList(),
}),
});
const normalizeList = (state, listType, statuses, next) => {
@@ -93,18 +80,6 @@ export default function statusLists(state = initialState, action) {
return normalizeList(state, 'favorites', action.statuses, action.next);
case FAVORITED_STATUSES_EXPAND_SUCCESS:
return appendToList(state, 'favorites', action.statuses, action.next);
case FAVORITE_SUCCESS:
return prependOneToList(state, 'favorites', action.status);
case UNFAVORITE_SUCCESS:
return removeOneFromList(state, 'favorites', action.status);
case PIN_SUCCESS:
return prependOneToList(state, 'pins', action.status);
case UNPIN_SUCCESS:
return removeOneFromList(state, 'pins', action.status);
case BOOKMARK_SUCCESS:
return prependOneToList(state, 'bookmarks', action.status);
case UNBOOKMARK_SUCCESS:
return removeOneFromList(state, 'bookmarks', action.status);
default:
return state;
}

View File

@@ -6,10 +6,9 @@ import {
UNFAVORITE_REQUEST,
} from '../actions/interactions';
import {
STATUS_MUTE_SUCCESS,
STATUS_UNMUTE_SUCCESS,
STATUS_REVEAL,
STATUS_HIDE,
UPDATE_STATUS_STATS,
} from '../actions/statuses';
import { TIMELINE_DELETE } from '../actions/timelines';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
@@ -46,10 +45,6 @@ export default function statuses(state = initialState, action) {
return state.setIn([action.status.get('id'), 'reblogged'], true);
case REPOST_FAIL:
return state.get(action.status.get('id')) === undefined ? state : state.setIn([action.status.get('id'), 'reblogged'], false);
case STATUS_MUTE_SUCCESS:
return state.setIn([action.id, 'muted'], true);
case STATUS_UNMUTE_SUCCESS:
return state.setIn([action.id, 'muted'], false);
case STATUS_REVEAL:
return state.withMutations(map => {
action.ids.forEach(id => {
@@ -68,6 +63,9 @@ export default function statuses(state = initialState, action) {
});
case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.references);
case UPDATE_STATUS_STATS:
// : todo :
return state;
default:
return state;
}

View File

@@ -28,6 +28,7 @@ const initialTimeline = ImmutableMap({
online: false,
top: true,
isLoading: false,
isError: false,
hasMore: true,
items: ImmutableList(),
queuedItems: ImmutableList(), //max= MAX_QUEUED_ITEMS
@@ -49,10 +50,15 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, is
return newIds;
}
// const realtime = ['home']
// if no realtime, do all that, otherwise just concat
// return oldIds.concat(newIds);
const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1;
const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) > 0);
if (firstIndex < 0) {
console.log("----2")
return (isPartial ? newIds.unshift(null) : newIds).concat(oldIds.skip(lastIndex));
}
@@ -162,7 +168,10 @@ export default function timelines(state = initialState, action) {
case TIMELINE_EXPAND_REQUEST:
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', true));
case TIMELINE_EXPAND_FAIL:
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
return state.update(action.timeline, initialTimeline, map => map.withMutations(mMap => {
map.set('isLoading', false)
map.set('isError', true)
}));
case TIMELINE_EXPAND_SUCCESS:
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
case TIMELINE_UPDATE:

View File

@@ -78,6 +78,7 @@ export const makeGetStatus = () => {
[
(state) => state,
(state, { id }) => state.getIn(['statuses', id]),
(state, { id }) => state.getIn(['groups', state.getIn(['statuses', id, 'group'])]),
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'quote_of_id'])]),
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
@@ -87,7 +88,7 @@ export const makeGetStatus = () => {
getFilters,
],
(state, statusBase, quotedStatus, statusRepost, accountBase, accountQuoted, accountRepost, username, filters) => {
(state, statusBase, group, quotedStatus, statusRepost, accountBase, accountQuoted, accountRepost, username, filters) => {
if (!statusBase) {
return null
}
@@ -122,16 +123,20 @@ export const makeGetStatus = () => {
quotedStatus = quotedStatus.set('account', accountQuoted);
}
console.log("group:", group)
//Find ancestor status
const regex = (accountRepost || accountBase).get('id') !== me && regexFromFilters(filters);
const filtered = regex && regex.test(statusBase.get('reblog') ? statusRepost.get('search_index') : statusBase.get('search_index'));
return statusBase.withMutations(map => {
return statusBase.withMutations((map) => {
map.set('quoted_status', quotedStatus);
map.set('reblog', statusRepost);
map.set('account', accountBase);
map.set('filtered', filtered);
map.set('group', group);
});
}
);

View File

@@ -50,13 +50,11 @@ self.addEventListener('fetch', function(event) {
url.pathname.startsWith('/about') ||
url.pathname.startsWith('/auth') ||
url.pathname.startsWith('/oauth') ||
url.pathname.startsWith('/invites') ||
url.pathname.startsWith('/pghero') ||
url.pathname.startsWith('/sidekiq') ||
url.pathname.startsWith('/filters') ||
url.pathname.startsWith('/tags') ||
url.pathname.startsWith('/emojis') ||
url.pathname.startsWith('/inbox') ||
url.pathname.startsWith('/accounts') ||
url.pathname.startsWith('/user') ||
url.pathname.startsWith('/users') ||
@@ -64,8 +62,7 @@ self.addEventListener('fetch', function(event) {
url.pathname.startsWith('/public') ||
url.pathname.startsWith('/avatars') ||
url.pathname.startsWith('/authorize_follow') ||
url.pathname.startsWith('/media_proxy') ||
url.pathname.startsWith('/relationships')) {
url.pathname.startsWith('/media_proxy')) {
//non-webapp routes
} else if (url.pathname.startsWith('/')) {
// : TODO : if is /web

View File

@@ -1,7 +0,0 @@
'use strict';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
const adapter = new Adapter();
configure({ adapter });

View File

@@ -1,10 +0,0 @@
import * as base64 from '../base64';
describe('base64', () => {
describe('decode', () => {
it('returns a uint8 array', () => {
const arr = base64.decode('dGVzdA==');
expect(arr).toEqual(new Uint8Array([116, 101, 115, 116]));
});
});
});

View File

@@ -1,10 +0,0 @@
import * as html from '../html';
describe('html', () => {
describe('unsecapeHTML', () => {
it('returns unescaped HTML', () => {
const output = html.unescapeHTML('<p>lorem</p><p>ipsum</p><br>&lt;br&gt;');
expect(output).toEqual('lorem\n\nipsum\n<br>');
});
});
});