Commiting

This commit is contained in:
mgabdev
2020-11-25 15:22:37 -06:00
parent fb612f60c8
commit b4e370d3d3
136 changed files with 4171 additions and 3231 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -33,20 +33,17 @@ export const fetchBookmarkedStatuses = () => (dispatch, getState) => {
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,
})
/**

View File

@@ -0,0 +1,28 @@
import api from '../api'
import { me } from '../initial_state'
export const MESSAGE_INPUT_CHANGE = 'MESSAGE_INPUT_CHANGE'
export const MESSAGE_INPUT_RESET = 'MESSAGE_INPUT_RESET'
/**
*
*/
export const messageInputChange = (text) => (dispatch, getState) => {
if (!me) return
//Ensure has conversation
const conversationId = getState().getIn(['chat_conversations', 'current', 'conversation_id'], null)
if (!conversationId) return
dispatch({
type: MESSAGE_INPUT_CHANGE,
text,
})
}
/**
*
*/
export const messageInputReset = (dispatch) => {
dispatch({ type: MESSAGE_INPUT_RESET })
}

View File

@@ -0,0 +1,189 @@
import api, { getLinks } from '../api'
import { fetchRelationships } from './accounts'
import { importFetchedAccounts } from './importer'
import { me } from '../initial_state'
export const CONVERSATION_BLOCKS_FETCH_REQUEST = 'CONVERSATION_BLOCKS_FETCH_REQUEST'
export const CONVERSATION_BLOCKS_FETCH_SUCCESS = 'CONVERSATION_BLOCKS_FETCH_SUCCESS'
export const CONVERSATION_BLOCKS_FETCH_FAIL = 'CONVERSATION_BLOCKS_FETCH_FAIL'
export const CONVERSATION_BLOCKS_EXPAND_REQUEST = 'CONVERSATION_BLOCKS_EXPAND_REQUEST'
export const CONVERSATION_BLOCKS_EXPAND_SUCCESS = 'CONVERSATION_BLOCKS_EXPAND_SUCCESS'
export const CONVERSATION_BLOCKS_EXPAND_FAIL = 'CONVERSATION_BLOCKS_EXPAND_FAIL'
export const BLOCK_MESSAGER_REQUEST = 'BLOCK_MESSAGER_REQUEST'
export const BLOCK_MESSAGER_SUCCESS = 'BLOCK_MESSAGER_SUCCESS'
export const BLOCK_MESSAGER_FAIL = 'BLOCK_MESSAGER_FAIL'
export const UNBLOCK_MESSAGER_REQUEST = 'UNBLOCK_MESSAGER_REQUEST'
export const UNBLOCK_MESSAGER_SUCCESS = 'UNBLOCK_MESSAGER_SUCCESS'
export const UNBLOCK_MESSAGER_FAIL = 'UNBLOCK_MESSAGER_FAIL'
//
export const CONVERSATION_MUTES_FETCH_REQUEST = 'CONVERSATION_MUTES_FETCH_REQUEST'
export const CONVERSATION_MUTES_FETCH_SUCCESS = 'CONVERSATION_MUTES_FETCH_SUCCESS'
export const CONVERSATION_MUTES_FETCH_FAIL = 'CONVERSATION_MUTES_FETCH_FAIL'
export const CONVERSATION_MUTES_EXPAND_REQUEST = 'CONVERSATION_MUTES_EXPAND_REQUEST'
export const CONVERSATION_MUTES_EXPAND_SUCCESS = 'CONVERSATION_MUTES_EXPAND_SUCCESS'
export const CONVERSATION_MUTES_EXPAND_FAIL = 'CONVERSATION_MUTES_EXPAND_FAIL'
export const MUTE_MESSAGER_REQUEST = 'BLOCK_MESSAGER_REQUEST'
export const MUTE_MESSAGER_SUCCESS = 'BLOCK_MESSAGER_SUCCESS'
export const MUTE_MESSAGER_FAIL = 'BLOCK_MESSAGER_FAIL'
export const UNMUTE_MESSAGER_REQUEST = 'UNMUTE_MESSAGER_REQUEST'
export const UNMUTE_MESSAGER_SUCCESS = 'UNMUTE_MESSAGER_SUCCESS'
export const UNMUTE_MESSAGER_FAIL = 'UNMUTE_MESSAGER_FAIL'
//
export const CONVERSATION_REQUEST_APPROVE_SUCCESS = 'CONVERSATION_REQUEST_APPROVE_SUCCESS'
export const CONVERSATION_REQUEST_APPROVE_FAIL = 'CONVERSATION_REQUEST_APPROVE_FAIL'
export const CONVERSATION_REQUEST_REJECT_SUCCESS = 'CONVERSATION_REQUEST_REJECT_SUCCESS'
export const CONVERSATION_REQUEST_REJECT_FAIL = 'CONVERSATION_REQUEST_REJECT_FAIL'
export const CONVERSATION_DELETE_REQUEST = 'CONVERSATION_DELETE_REQUEST'
export const CONVERSATION_DELETE_SUCCESS = 'CONVERSATION_DELETE_SUCCESS'
export const CONVERSATION_DELETE_FAIL = 'CONVERSATION_DELETE_FAIL'
//
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_EXPAND_REQUEST = 'CONVERSATIONS_EXPAND_REQUEST'
export const CONVERSATIONS_EXPAND_SUCCESS = 'CONVERSATIONS_EXPAND_SUCCESS'
export const CONVERSATIONS_EXPAND_FAIL = 'CONVERSATIONS_EXPAND_FAIL'
/**
*
*/
export const blockMessenger = (accountId) => (dispatch, getState) => {
if (!accountId) return
dispatch(blockMessengerRequest(accountId))
api(getState).post(`/api/v1/messages/accounts/${accountId}/block`).then((response) => {
dispatch(blockMessengerSuccess(response.data))
}).catch((error) => {
dispatch(blockMessengerFail(accountId, error))
})
}
const blockMessengerRequest = (accountId) => ({
type: BLOCK_MESSAGER_REQUEST,
accountId,
})
const blockMessengerSuccess = (data) => ({
type: BLOCK_MESSAGER_REQUEST,
data,
})
const blockMessengerFail = (accountId, error) => ({
type: BLOCK_MESSAGER_REQUEST,
accountId,
error,
})
/**
*
*/
export const unblockMessenger = (accountId) => (dispatch, getState) => {
if (!accountId) return
dispatch(unblockMessengerRequest(accountId))
api(getState).post(`/api/v1/messages/accounts/${accountId}/unblock`).then((response) => {
dispatch(unblockMessengerSuccess(response.data))
}).catch((error) => {
dispatch(unblockMessengerFail(accountId, error))
})
}
const unblockMessengerRequest = (accountId) => ({
type: UNBLOCK_MESSAGER_REQUEST,
accountId,
})
const blockMessengerSuccess = (data) => ({
type: UNBLOCK_MESSAGER_REQUEST,
data,
})
const blockMessengerFail = (accountId, error) => ({
type: UNBLOCK_MESSAGER_REQUEST,
accountId,
error,
})
/**
*
*/
export const fetchBlocks = () => (dispatch, getState) => {
if (!me) return
dispatch(fetchBlocksRequest())
api(getState).get('/api/v1/blocks').then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedAccounts(response.data))
dispatch(fetchBlocksSuccess(response.data, next ? next.uri : null))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch(error => dispatch(fetchBlocksFail(error)))
}
export 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 const expandBlocksRequest = () => ({
type: BLOCKS_EXPAND_REQUEST,
})
export const expandBlocksSuccess = (accounts, next) => ({
type: BLOCKS_EXPAND_SUCCESS,
accounts,
next,
})
export const expandBlocksFail = (error) => ({
type: BLOCKS_EXPAND_FAIL,
error,
})

View File

@@ -0,0 +1,86 @@
import api from '../api'
import { me } from '../initial_state'
export const MESSAGE_SEND_REQUEST = 'MESSAGE_SEND_REQUEST'
export const MESSAGE_SEND_SUCCESS = 'MESSAGE_SEND_SUCCESS'
export const MESSAGE_SEND_FAIL = 'MESSAGE_SEND_FAIL'
export const MESSAGE_DELETE_REQUEST = 'MESSAGE_DELETE_REQUEST'
export const MESSAGE_DELETE_SUCCESS = 'MESSAGE_DELETE_SUCCESS'
export const MESSAGE_DELETE_FAIL = 'MESSAGE_DELETE_FAIL'
/**
*
*/
const sendMessage = (text, conversationId) => (dispatch, getState) => {
if (!me) return
// : todo :
// let text = getState().getIn(['chat_messages', 'text'], '')
// let conversationId = getState().getIn(['chat_messags', 'conversation_id'], '')
dispatch(sendMessageRequest())
api(getState).put('/api/v1/messages/chat', {
text,
conversationId,
}, {
headers: {
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
},
}).then((response) => {
sendMessageSuccess(response)
}).catch((error) => {
dispatch(sendMessageFail(error))
})
}
const sendMessageRequest = (text, conversationId) => ({
type: MESSAGE_SEND_REQUEST,
text,
conversationId,
})
const sendMessageSuccess = () => ({
type: MESSAGE_SEND_SUCCESS,
})
const sendMessageFail = (error) => ({
type: MESSAGE_SEND_FAIL,
error,
})
/**
*
*/
const deleteMessage = (messageId) => (dispatch, getState) => {
if (!me || !messageId) return
// : todo :
dispatch(sendMessageRequest())
api(getState).delete(`/api/v1/messages/chat/${messageId}`, {}, {
headers: {
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
},
}).then((response) => {
sendMessageSuccess(response)
}).catch((error) => {
dispatch(sendMessageFail(error))
})
}
const deleteMessageRequest = (messageId) => ({
type: MESSAGE_DELETE_REQUEST,
messageId,
})
const deleteMessageSuccess = () => ({
type: MESSAGE_DELETE_SUCCESS,
})
const deleteMessageFail = (error) => ({
type: MESSAGE_DELETE_FAIL,
error,
})

File diff suppressed because it is too large Load Diff

View File

@@ -16,17 +16,14 @@ export const fetchCustomEmojis = () => (dispatch, getState) => {
const fetchCustomEmojisRequest = () => ({
type: CUSTOM_EMOJIS_FETCH_REQUEST,
skipLoading: true,
})
const fetchCustomEmojisSuccess = (custom_emojis) => ({
type: CUSTOM_EMOJIS_FETCH_SUCCESS,
custom_emojis,
skipLoading: true,
})
const fetchCustomEmojisFail = (error) => ({
type: CUSTOM_EMOJIS_FETCH_FAIL,
error,
skipLoading: true,
})

View File

@@ -33,20 +33,17 @@ export const fetchFavoritedStatuses = () => (dispatch, getState) => {
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,
})
/**

View File

@@ -23,18 +23,15 @@ export const fetchFilters = () => (dispatch, getState) => {
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,
})

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,10 @@ 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) {
/**
*
*/
const pushUnique = (array, object) => {
if (array.every(element => element.id !== object.id)) {
array.push(object);
}

View File

@@ -5,20 +5,26 @@ import { expandSpoilers } from '../../initial_state'
const domParser = new DOMParser()
/**
*
*/
const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
obj[`:${emoji.shortcode}:`] = emoji;
return obj;
}, {});
obj[`:${emoji.shortcode}:`] = emoji
return obj
}, {})
export function normalizeAccount(account) {
account = { ...account };
/**
*
*/
export const normalizeAccount = (account) => {
account = { ...account }
const emojiMap = makeEmojiMap(account);
const displayName = account.display_name.trim().length === 0 ? account.username : account.display_name;
const emojiMap = makeEmojiMap(account)
const displayName = account.display_name.trim().length === 0 ? account.username : account.display_name
account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap);
account.display_name_plain = emojify(escapeTextContentForBrowser(displayName), emojiMap, true);
account.note_emojified = emojify(account.note, emojiMap);
account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap)
account.display_name_plain = emojify(escapeTextContentForBrowser(displayName), emojiMap, true)
account.note_emojified = emojify(account.note, emojiMap)
account.note_plain = unescapeHTML(account.note)
if (account.fields) {
@@ -27,67 +33,73 @@ export function normalizeAccount(account) {
name_emojified: emojify(escapeTextContentForBrowser(pair.name)),
value_emojified: emojify(pair.value, emojiMap),
value_plain: unescapeHTML(pair.value),
}));
}))
}
if (account.moved) {
account.moved = account.moved.id;
account.moved = account.moved.id
}
return account;
return account
}
export function normalizeStatus(status, normalOldStatus) {
const normalStatus = { ...status };
normalStatus.account = status.account_id || status.account.id;
/**
*
*/
export const normalizeStatus = (status, normalOldStatus) => {
const normalStatus = { ...status }
normalStatus.account = status.account_id || status.account.id
if (status.reblog && status.reblog.id) {
normalStatus.reblog = status.reblog.id;
normalStatus.reblog = status.reblog.id
}
if (status.quote && status.quote.id) {
normalStatus.quote = status.quote.id;
normalStatus.quote = status.quote.id
}
if (status.poll && status.poll.id) {
normalStatus.poll = status.poll.id;
normalStatus.poll = status.poll.id
}
if (!!status.group || !!status.group_id) {
normalStatus.group = status.group_id || 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) {
normalStatus.search_index = normalOldStatus.get('search_index');
normalStatus.contentHtml = normalOldStatus.get('contentHtml');
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
normalStatus.hidden = normalOldStatus.get('hidden');
normalStatus.search_index = normalOldStatus.get('search_index')
normalStatus.contentHtml = normalOldStatus.get('contentHtml')
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml')
normalStatus.hidden = normalOldStatus.get('hidden')
} else {
const spoilerText = normalStatus.spoiler_text || '';
const searchContent = [spoilerText, status.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
const emojiMap = makeEmojiMap(normalStatus);
const theContent = !!normalStatus.rich_content ? normalStatus.rich_content : normalStatus.content;
const spoilerText = normalStatus.spoiler_text || ''
const searchContent = [spoilerText, status.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n')
const emojiMap = makeEmojiMap(normalStatus)
const theContent = !!normalStatus.rich_content ? normalStatus.rich_content : normalStatus.content
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
normalStatus.contentHtml = emojify(theContent, emojiMap, false, true);
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent
normalStatus.contentHtml = emojify(theContent, emojiMap, false, true)
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap)
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive
}
return normalStatus;
return normalStatus
}
export function normalizePoll(poll) {
const normalPoll = { ...poll };
/**
*
*/
export const normalizePoll = (poll) => {
const normalPoll = { ...poll }
const emojiMap = makeEmojiMap(normalPoll);
const emojiMap = makeEmojiMap(normalPoll)
normalPoll.options = poll.options.map(option => ({
normalPoll.options = poll.options.map((option) => ({
...option,
title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap),
}));
}))
return normalPoll;
return normalPoll
}

View File

@@ -1,5 +1,9 @@
import api from '../api'
import { importFetchedAccounts, importFetchedStatus } from './importer'
import api, { getLinks } from '../api'
import {
importFetchedAccounts,
importFetchedStatus,
} from './importer'
import { fetchRelationships } from './accounts'
import { updateStatusStats } from './statuses'
import { me } from '../initial_state'
@@ -23,6 +27,10 @@ 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_EXPAND_REQUEST = 'REPOSTS_EXPAND_REQUEST'
export const REPOSTS_EXPAND_SUCCESS = 'REPOSTS_EXPAND_SUCCESS'
export const REPOSTS_EXPAND_FAIL = 'REPOSTS_EXPAND_FAIL'
export const PIN_REQUEST = 'PIN_REQUEST'
export const PIN_SUCCESS = 'PIN_SUCCESS'
export const PIN_FAIL = 'PIN_FAIL'
@@ -31,6 +39,10 @@ export const UNPIN_REQUEST = 'UNPIN_REQUEST'
export const UNPIN_SUCCESS = 'UNPIN_SUCCESS'
export const UNPIN_FAIL = 'UNPIN_FAIL'
export const IS_PIN_REQUEST = 'IS_PIN_REQUEST'
export const IS_PIN_SUCCESS = 'IS_PIN_SUCCESS'
export const IS_PIN_FAIL = 'IS_PIN_FAIL'
export const BOOKMARK_REQUEST = 'BOOKMARK_REQUEST'
export const BOOKMARK_SUCCESS = 'BOOKMARK_SUCCESS'
export const BOOKMARK_FAIL = 'BOOKMARK_FAIL'
@@ -39,342 +51,512 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARK_REQUEST'
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARK_SUCCESS'
export const UNBOOKMARK_FAIL = 'UNBOOKMARK_FAIL'
export const IS_BOOKMARK_REQUEST = 'IS_BOOKMARK_REQUEST'
export const IS_BOOKMARK_SUCCESS = 'IS_BOOKMARK_SUCCESS'
export const IS_BOOKMARK_FAIL = 'IS_BOOKMARK_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_EXPAND_REQUEST = 'LIKES_EXPAND_REQUEST'
export const LIKES_EXPAND_SUCCESS = 'LIKES_EXPAND_SUCCESS'
export const LIKES_EXPAND_FAIL = 'LIKES_EXPAND_FAIL'
/**
*
* @description Repost the given status. Set status to status.reblogged:true and
* increment status.reblogs_count by 1 on success.
* @param {ImmutableMap} status
*/
export const repost = (status) => (dispatch, getState) => {
if (!me) return
if (!me || !status) return
dispatch(repostRequest(status))
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(updateStatusStats(response.data))
dispatch(repostSuccess(status))
}).catch((error) => {
dispatch(repostFail(status, error))
})
}
export const repostRequest = (status) => ({
const repostRequest = (status) => ({
type: REPOST_REQUEST,
status: status,
skipLoading: true,
status,
})
export const repostSuccess = (status) => ({
const repostSuccess = (status) => ({
type: REPOST_SUCCESS,
status: status,
skipLoading: true,
status,
})
export const repostFail = (status, error) => ({
const repostFail = (status, error) => ({
type: REPOST_FAIL,
status: status,
error: error,
skipLoading: true,
status,
error,
})
/**
*
* @description Unrepost the given status. Set status to status.reblogged:false and
* decrement status.reblogs_count by 1 on success.
* @param {ImmutableMap} status
*/
export const unrepost = (status) => (dispatch, getState) => {
if (!me) return
if (!me || !status) return
dispatch(unrepostRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(updateStatusStats(response.data))
dispatch(unrepostSuccess(status))
}).catch((error) => {
dispatch(unrepostFail(status, error))
})
}
export const unrepostRequest = (status) => ({
const unrepostRequest = (status) => ({
type: UNREPOST_REQUEST,
status: status,
skipLoading: true,
status,
})
export const unrepostSuccess = (status) => ({
const unrepostSuccess = (status) => ({
type: UNREPOST_SUCCESS,
status: status,
skipLoading: true,
status,
})
export const unrepostFail = (status, error) => ({
const unrepostFail = (status, error) => ({
type: UNREPOST_FAIL,
status: status,
error: error,
skipLoading: true,
status,
error,
})
/**
*
* @description Favorite the given status. Set status to status.favourited:true and
* increment status.favourites_count by 1 on success.
* @param {ImmutableMap} status
*/
export const favorite = (status) => (dispatch, getState) => {
if (!me) return
if (!me || !status) return
dispatch(favoriteRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then((response) => {
dispatch(updateStatusStats(response.data))
dispatch(favoriteSuccess(status))
dispatch(favoriteSuccess(response.data))
}).catch((error) => {
dispatch(favoriteFail(status, error))
})
}
export const favoriteRequest = (status) => ({
const favoriteRequest = (status) => ({
type: FAVORITE_REQUEST,
status: status,
skipLoading: true,
status,
})
export const favoriteSuccess = (status) => ({
const favoriteSuccess = (data) => ({
type: FAVORITE_SUCCESS,
status: status,
skipLoading: true,
data,
})
export const favoriteFail = (status, error) => ({
const favoriteFail = (status, error) => ({
type: FAVORITE_FAIL,
status: status,
error: error,
skipLoading: true,
status,
error,
})
/**
*
* @description Unfavorite the given status. Set status to status.favourited:false and
* decrement status.favourites_count by 1 on success.
* @param {ImmutableMap} status
*/
export const unfavorite = (status) => (dispatch, getState) => {
if (!me) return
if (!me || !status) return
dispatch(unfavoriteRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(updateStatusStats(response.data))
dispatch(unfavoriteSuccess(status))
}).catch((error) => {
dispatch(unfavoriteFail(status, error))
})
}
export const unfavoriteRequest = (status) => ({
const unfavoriteRequest = (status) => ({
type: UNFAVORITE_REQUEST,
status: status,
skipLoading: true,
status,
})
export const unfavoriteSuccess = (status) => ({
const unfavoriteSuccess = (status) => ({
type: UNFAVORITE_SUCCESS,
status: status,
skipLoading: true,
status,
})
export const unfavoriteFail = (status, error) => ({
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 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,
status,
error,
})
/**
*
* @description Pin the given status to your profile. Set status to status.pinned:true
* on success.
* @param {ImmutableMap} status
*/
export const pin = (status) => (dispatch, getState) => {
if (!me) return
if (!me || !status) return
dispatch(pinRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/pin`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(updateStatusStats(response.data))
dispatch(pinSuccess(status))
}).catch((error) => {
dispatch(pinFail(status, error))
})
}
export const pinRequest = (status) => ({
const pinRequest = (status) => ({
type: PIN_REQUEST,
status,
skipLoading: true,
})
export const pinSuccess = (status) => ({
const pinSuccess = (status) => ({
type: PIN_SUCCESS,
status,
skipLoading: true,
})
export const pinFail = (status, error) => ({
const pinFail = (status, error) => ({
type: PIN_FAIL,
status,
error,
skipLoading: true,
})
/**
*
* @description Unpin the given status from your profile. Set status to status.pinned:false
* on success and remove from account pins in timeline reducer.
* @param {ImmutableMap} status
*/
export const unpin = (status) => (dispatch, getState) => {
if (!me) return
if (!me || !status) return
dispatch(unpinRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unpin`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(unpinSuccess(status))
dispatch(updateStatusStats(response.data))
dispatch(unpinSuccess(status, response.data.account_id))
}).catch((error) => {
dispatch(unpinFail(status, error))
})
}
export const unpinRequest = (status) => ({
const unpinRequest = (status) => ({
type: UNPIN_REQUEST,
status,
skipLoading: true,
})
export const unpinSuccess = (status) => ({
const unpinSuccess = (status, accountId) => ({
type: UNPIN_SUCCESS,
accountId,
status,
skipLoading: true,
})
export const unpinFail = (status, error) => ({
const unpinFail = (status, error) => ({
type: UNPIN_FAIL,
status,
error,
skipLoading: true,
})
/**
*
* @description Check if a status is pinned to the current user account.
* @param {String} statusId
*/
export const fetchLikes = (id) => (dispatch, getState) => {
dispatch(fetchLikesRequest(id))
export const isPin = (statusId) => (dispatch, getState) => {
if (!me || !statusId) return
api(getState).get(`/api/v1/statuses/${id}/favourited_by`).then((response) => {
dispatch(importFetchedAccounts(response.data))
dispatch(fetchLikesSuccess(id, response.data))
dispatch(isPinRequest(statusId))
api(getState).get(`/api/v1/statuses/${statusId}/pin`).then((response) => {
dispatch(updateStatusStats(response.data))
dispatch(isPinSuccess(statusId))
}).catch((error) => {
dispatch(fetchLikesFail(id, error))
dispatch(isPinFail(statusId, error))
})
}
export const fetchLikesRequest = (id) => ({
type: LIKES_FETCH_REQUEST,
id,
const isPinRequest = (statusId) => ({
type: IS_PIN_REQUEST,
statusId,
})
export const fetchLikesSuccess = (id, accounts) => ({
type: LIKES_FETCH_SUCCESS,
id,
accounts,
const isPinSuccess = (statusId) => ({
type: IS_PIN_SUCCESS,
statusId,
})
export const fetchLikesFail = (id, error) => ({
type: LIKES_FETCH_FAIL,
const isPinFail = (statusId, error) => ({
type: IS_PIN_FAIL,
statusId,
error,
})
/**
*
* @description Bookmark the given status in your profile if PRO. Set status to
* status.bookmarked:true on success.
* @param {ImmutableMap} status
*/
export const bookmark = (status) => (dispatch, getState) => {
if (!me || !status) return
dispatch(bookmarkRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/bookmark`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(bookmarkSuccess(status, response.data))
dispatch(updateStatusStats(response.data))
dispatch(bookmarkSuccess(status))
}).catch((error) => {
dispatch(bookmarkFail(status, error))
})
}
export const bookmarkRequest = (status) => ({
const bookmarkRequest = (status) => ({
type: BOOKMARK_REQUEST,
status: status,
status,
})
export const bookmarkSuccess = (status, response) => ({
const bookmarkSuccess = (status) => ({
type: BOOKMARK_SUCCESS,
status: status,
response: response,
status,
})
export const bookmarkFail = (status, error) => ({
const bookmarkFail = (status, error) => ({
type: BOOKMARK_FAIL,
status: status,
error: error,
status,
error,
})
/**
*
* @description Unbookmark the given status in your profile if PRO. Set status to
* status.bookmarked:false on success.
* @param {ImmutableMap} status
*/
export const unbookmark = (status) => (dispatch, getState) => {
if (!me || !status) return
dispatch(unbookmarkRequest(status))
api(getState).post(`/api/v1/statuses/${status.get('id')}/unbookmark`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(unbookmarkSuccess(status, response.data))
dispatch(updateStatusStats(response.data))
dispatch(unbookmarkSuccess(status))
}).catch((error) => {
dispatch(unbookmarkFail(status, error))
})
}
export const unbookmarkRequest = (status) => ({
const unbookmarkRequest = (status) => ({
type: UNBOOKMARK_REQUEST,
status: status,
status,
})
export const unbookmarkSuccess = (status, response) => ({
const unbookmarkSuccess = (status) => ({
type: UNBOOKMARK_SUCCESS,
status: status,
response: response,
status,
})
export const unbookmarkFail = (status, error) => ({
const unbookmarkFail = (status, error) => ({
type: UNBOOKMARK_FAIL,
status: status,
error: error,
})
status,
error,
})
/**
* @description Check if a status is bookmarked to the current user account.
* @param {String} statusId
*/
export const isBookmark = (statusId) => (dispatch, getState) => {
if (!me || !statusId) return
dispatch(isBookmarkRequest(statusId))
api(getState).get(`/api/v1/statuses/${statusId}/bookmark`).then((response) => {
dispatch(updateStatusStats(response.data))
dispatch(isBookmarkSuccess(statusId))
}).catch((error) => {
dispatch(isBookmarkFail(statusId, error))
})
}
const isBookmarkRequest = (statusId) => ({
type: IS_BOOKMARK_REQUEST,
statusId,
})
const isBookmarkSuccess = (statusId) => ({
type: IS_BOOKMARK_SUCCESS,
statusId,
})
const isBookmarkFail = (statusId, error) => ({
type: IS_BOOKMARK_FAIL,
statusId,
error,
})
/**
* @description Fetch reposts for the given statusId and imports paginated accounts
* and sets in user_lists reducer.
* @param {String} statusId
*/
export const fetchReposts = (statusId) => (dispatch, getState) => {
if (!me || !statusId) return
dispatch(fetchRepostsRequest(statusId))
api(getState).get(`/api/v1/statuses/${statusId}/reblogged_by`).then((response) => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedAccounts(response.data))
dispatch(fetchRepostsSuccess(statusId, response.data, next ? next.uri : null))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch((error) => {
dispatch(fetchRepostsFail(statusId, error))
})
}
const fetchRepostsRequest = (statusId) => ({
type: REPOSTS_FETCH_REQUEST,
statusId,
})
const fetchRepostsSuccess = (statusId, accounts, next) => ({
type: REPOSTS_FETCH_SUCCESS,
statusId,
accounts,
next,
})
const fetchRepostsFail = (statusId, error) => ({
type: REPOSTS_FETCH_FAIL,
statusId,
error,
})
/**
* @description Expand reposts for the given statusId and imports paginated accounts
* and sets in user_lists reducer.
* @param {String} statusId
*/
export const expandReposts = (statusId) => (dispatch, getState) => {
if (!me || !statusId) return
const url = getState().getIn(['user_lists', 'reblogged_by', statusId, 'next'])
const isLoading = getState().getIn(['user_lists', 'reblogged_by', statusId, 'isLoading'])
if (url === null || isLoading) return
dispatch(expandRepostsRequest(statusId))
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedAccounts(response.data))
dispatch(expandRepostsSuccess(statusId, response.data, next ? next.uri : null))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch(error => dispatch(expandRepostsFail(error)))
}
const expandRepostsRequest = (statusId) => ({
type: REPOSTS_EXPAND_REQUEST,
statusId,
})
const expandRepostsSuccess = (statusId, accounts, next) => ({
type: REPOSTS_EXPAND_SUCCESS,
statusId,
accounts,
next,
})
const expandRepostsFail = (statusId, error) => ({
type: REPOSTS_EXPAND_FAIL,
statusId,
error,
})
/**
* @description Fetch likes for the given statusId and imports paginated accounts
* and sets in user_lists reducer.
* @param {String} statusId
*/
export const fetchLikes = (statusId) => (dispatch, getState) => {
if (!me || !statusId) return
dispatch(fetchLikesRequest(statusId))
api(getState).get(`/api/v1/statuses/${statusId}/favourited_by`).then((response) => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedAccounts(response.data))
dispatch(fetchLikesSuccess(statusId, response.data, next ? next.uri : null))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch((error) => {
dispatch(fetchLikesFail(statusId, error))
})
}
const fetchLikesRequest = (statusId) => ({
type: LIKES_FETCH_REQUEST,
statusId,
})
const fetchLikesSuccess = (statusId, accounts, next) => ({
type: LIKES_FETCH_SUCCESS,
statusId,
accounts,
next,
})
const fetchLikesFail = (statusId, error) => ({
type: LIKES_FETCH_FAIL,
statusId,
error,
})
/**
* @description Expand likes for the given statusId and imports paginated accounts
* and sets in user_lists reducer.
* @param {String} statusId
*/
export const expandLikes = (statusId) => (dispatch, getState) => {
if (!me || !statusId) return
const url = getState().getIn(['user_lists', 'liked_by', statusId, 'next'])
const isLoading = getState().getIn(['user_lists', 'liked_by', statusId, 'isLoading'])
if (url === null || isLoading) return
dispatch(expandLikesRequest(statusId))
api(getState).get(url).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next')
dispatch(importFetchedAccounts(response.data))
dispatch(expandLikesSuccess(statusId, response.data, next ? next.uri : null))
dispatch(fetchRelationships(response.data.map(item => item.id)))
}).catch(error => dispatch(expandLikesFail(error)))
}
const expandLikesRequest = (statusId) => ({
type: LIKES_EXPAND_REQUEST,
statusId,
})
const expandLikesSuccess = (statusId, accounts, next) => ({
type: LIKES_EXPAND_SUCCESS,
statusId,
accounts,
next,
})
const expandLikesFail = (statusId, error) => ({
type: LIKES_EXPAND_FAIL,
statusId,
error,
})

View File

@@ -1,5 +1,6 @@
import api, { getLinks } from '../api'
import IntlMessageFormat from 'intl-messageformat'
import noop from 'lodash.noop'
import { fetchRelationships } from './accounts'
import {
importFetchedAccount,
@@ -49,8 +50,6 @@ const excludeTypesFromFilter = filter => {
return allTypes.filterNot(item => item === filter).toJS()
}
const noOp = () => {}
/**
*
*/
@@ -159,7 +158,7 @@ export const dequeueNotifications = () => (dispatch, getState) => {
/**
*
*/
export const expandNotifications = ({ maxId } = {}, done = noOp) => (dispatch, getState) => {
export const expandNotifications = ({ maxId } = {}, done = noop) => (dispatch, getState) => {
if (!me) return
const onlyVerified = getState().getIn(['notifications', 'filter', 'onlyVerified'])
@@ -204,19 +203,19 @@ export const expandNotifications = ({ maxId } = {}, done = noOp) => (dispatch, g
})
}
export const expandNotificationsRequest = (isLoadingMore) => ({
const expandNotificationsRequest = (isLoadingMore) => ({
type: NOTIFICATIONS_EXPAND_REQUEST,
skipLoading: !isLoadingMore,
})
export const expandNotificationsSuccess = (notifications, next, isLoadingMore) => ({
const expandNotificationsSuccess = (notifications, next, isLoadingMore) => ({
type: NOTIFICATIONS_EXPAND_SUCCESS,
notifications,
next,
skipLoading: !isLoadingMore,
})
export const expandNotificationsFail = (error, isLoadingMore) => ({
const expandNotificationsFail = (error, isLoadingMore) => ({
type: NOTIFICATIONS_EXPAND_FAIL,
error,
skipLoading: !isLoadingMore,

View File

@@ -1,15 +1,23 @@
import api from '../api'
export const STATUS_REVISIONS_LOAD = 'STATUS_REVISIONS_LOAD'
export const STATUS_REVISIONS_LOAD_REQUEST = 'STATUS_REVISIONS_LOAD_REQUEST'
export const STATUS_REVISIONS_LOAD_SUCCESS = 'STATUS_REVISIONS_SUCCESS'
export const STATUS_REVISIONS_LOAD_FAIL = 'STATUS_REVISIONS_FAIL'
/**
*
*/
export const loadStatusRevisions = (statusId) => (dispatch, getState) => {
dispatch(loadStatusRevisionsRequest())
api(getState).get(`/api/v1/statuses/${statusId}/revisions`)
.then(res => dispatch(loadStatusRevisionsSuccess(res.data)))
.catch(() => dispatch(loadStatusRevisionsFail()))
}
const loadStatusRevisionsRequest = () => ({
type: STATUS_REVISIONS_LOAD_REQUEST,
})
const loadStatusRevisionsSuccess = (data) => ({
type: STATUS_REVISIONS_LOAD_SUCCESS,
revisions: data,

View File

@@ -1,304 +1,292 @@
import api from '../api';
import openDB from '../storage/db';
import { evictStatus } from '../storage/modifier';
import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer';
import { openModal } from './modal';
import { me } from '../initial_state';
import api from '../api'
import openDB from '../storage/db'
import { evictStatus } from '../storage/modifier'
import { deleteFromTimelines } from './timelines'
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer'
import { openModal } from './modal'
import { me } from '../initial_state'
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
export const STATUS_FETCH_FAIL = 'STATUS_FETCH_FAIL';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS'
export const STATUS_FETCH_FAIL = 'STATUS_FETCH_FAIL'
export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
export const STATUS_DELETE_FAIL = 'STATUS_DELETE_FAIL';
export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST'
export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS'
export const STATUS_DELETE_FAIL = 'STATUS_DELETE_FAIL'
export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL';
export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST'
export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS'
export const CONTEXT_FETCH_FAIL = 'CONTEXT_FETCH_FAIL'
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 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_REVEAL = 'STATUS_REVEAL';
export const STATUS_HIDE = 'STATUS_HIDE';
export const STATUS_REVEAL = 'STATUS_REVEAL'
export const STATUS_HIDE = 'STATUS_HIDE'
export const STATUS_EDIT = 'STATUS_EDIT';
export const STATUS_EDIT = 'STATUS_EDIT'
export const UPDATE_STATUS_STATS = 'UPDATE_STATUS_STATS'
export function fetchStatusRequest(id, skipLoading) {
return {
type: STATUS_FETCH_REQUEST,
id,
skipLoading,
};
};
/**
*
*/
function getFromDB(dispatch, getState, accountIndex, index, id) {
return new Promise((resolve, reject) => {
const request = index.get(id);
const request = index.get(id)
request.onerror = reject;
request.onerror = reject
request.onsuccess = () => {
const promises = [];
const promises = []
if (!request.result) {
reject();
return;
reject()
return
}
dispatch(importStatus(request.result));
dispatch(importStatus(request.result))
if (getState().getIn(['accounts', request.result.account], null) === null) {
promises.push(new Promise((accountResolve, accountReject) => {
const accountRequest = accountIndex.get(request.result.account);
const accountRequest = accountIndex.get(request.result.account)
accountRequest.onerror = accountReject;
accountRequest.onerror = accountReject
accountRequest.onsuccess = () => {
if (!request.result) {
accountReject();
return;
accountReject()
return
}
dispatch(importAccount(accountRequest.result));
accountResolve();
};
}));
dispatch(importAccount(accountRequest.result))
accountResolve()
}
}))
}
if (request.result.reblog && getState().getIn(['statuses', request.result.reblog], null) === null) {
promises.push(getFromDB(dispatch, getState, accountIndex, index, request.result.reblog));
promises.push(getFromDB(dispatch, getState, accountIndex, index, request.result.reblog))
}
resolve(Promise.all(promises));
};
});
resolve(Promise.all(promises))
}
})
}
export function fetchStatus(id) {
return (dispatch, getState) => {
const skipLoading = getState().getIn(['statuses', id], null) !== null;
/**
*
*/
export const fetchStatus = (id) => (dispatch, getState) => {
const skipLoading = getState().getIn(['statuses', id], null) !== null
if (skipLoading) return
if (skipLoading) {
return;
dispatch(fetchStatusRequest(id, skipLoading))
openDB().then((db) => {
const transaction = db.transaction(['accounts', 'statuses'], 'read')
const accountIndex = transaction.objectStore('accounts').index('id')
const index = transaction.objectStore('statuses').index('id')
return getFromDB(dispatch, getState, accountIndex, index, id).then(() => {
db.close()
}, (error) => {
db.close()
throw error
})
}).then(() => {
dispatch(fetchStatusSuccess(skipLoading))
}, () => api(getState).get(`/api/v1/statuses/${id}`).then((response) => {
dispatch(importFetchedStatus(response.data))
dispatch(fetchStatusSuccess(skipLoading))
})).catch((error) => {
dispatch(fetchStatusFail(id, error, skipLoading))
})
}
const fetchStatusRequest = (id, skipLoading) => ({
type: STATUS_FETCH_REQUEST,
id,
skipLoading,
})
const fetchStatusSuccess = (skipLoading) => ({
type: STATUS_FETCH_SUCCESS,
skipLoading,
})
const fetchStatusFail = (id, error, skipLoading) => ({
type: STATUS_FETCH_FAIL,
id,
error,
skipLoading,
skipAlert: true,
})
/**
*
*/
export const editStatus = (status) => (dispatch) => {
dispatch({
type: STATUS_EDIT,
status,
})
dispatch(openModal('COMPOSE'))
}
/**
*
*/
export const deleteStatus = (id, routerHistory) => (dispatch, getState) => {
if (!me) return
let status = getState().getIn(['statuses', id])
if (status.get('poll')) {
status = status.set('poll', getState().getIn(['polls', status.get('poll')]))
}
dispatch(deleteStatusRequest(id))
api(getState).delete(`/api/v1/statuses/${id}`).then((response) => {
evictStatus(id)
dispatch(deleteStatusSuccess(id))
dispatch(deleteFromTimelines(id))
}).catch((error) => {
dispatch(deleteStatusFail(id, error))
})
}
const deleteStatusRequest = (id) => ({
type: STATUS_DELETE_REQUEST,
id: id,
})
const deleteStatusSuccess = (id) => ({
type: STATUS_DELETE_SUCCESS,
id: id,
})
const deleteStatusFail = (id, error) => ({
type: STATUS_DELETE_FAIL,
id: id,
error,
})
/**
*
*/
export const fetchContext = (id, ensureIsReply) => (dispatch, getState) => {
if (ensureIsReply) {
const isReply = !!getState().getIn(['statuses', id, 'in_reply_to_id'], null)
if (!isReply) return
}
dispatch(fetchContextRequest(id))
api(getState).get(`/api/v1/statuses/${id}/context`).then((response) => {
dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants)))
dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants))
}).catch((error) => {
if (error.response && error.response.status === 404) {
dispatch(deleteFromTimelines(id))
}
dispatch(fetchContextFail(id, error))
})
}
/**
*
*/
const fetchContextRequest = (id) => ({
type: CONTEXT_FETCH_REQUEST,
id,
})
const fetchContextSuccess = (id, ancestors, descendants) => ({
type: CONTEXT_FETCH_SUCCESS,
id,
ancestors,
descendants,
statuses: ancestors.concat(descendants),
})
const fetchContextFail = (id, error) => ({
type: CONTEXT_FETCH_FAIL,
id,
error,
skipAlert: true,
})
/**
*
*/
export const fetchComments = (id) => (dispatch, getState) => {
dispatch(fetchCommentsRequest(id))
api(getState).get(`/api/v1/statuses/${id}/comments`).then((response) => {
dispatch(importFetchedStatuses(response.data.descendants))
dispatch(fetchCommentsSuccess(id, response.data.descendants))
}).catch((error) => {
if (error.response && error.response.status === 404) {
dispatch(deleteFromTimelines(id))
}
dispatch(fetchStatusRequest(id, skipLoading));
dispatch(fetchCommentsFail(id, error))
})
}
openDB().then(db => {
const transaction = db.transaction(['accounts', 'statuses'], 'read');
const accountIndex = transaction.objectStore('accounts').index('id');
const index = transaction.objectStore('statuses').index('id');
const fetchCommentsRequest = (id) => ({
type: COMMENTS_FETCH_REQUEST,
id,
})
return getFromDB(dispatch, getState, accountIndex, index, id).then(() => {
db.close();
}, error => {
db.close();
throw error;
});
}).then(() => {
dispatch(fetchStatusSuccess(skipLoading));
}, () => api(getState).get(`/api/v1/statuses/${id}`).then(response => {
dispatch(importFetchedStatus(response.data));
dispatch(fetchStatusSuccess(skipLoading));
})).catch(error => {
dispatch(fetchStatusFail(id, error, skipLoading));
});
};
};
const fetchCommentsSuccess = (id, descendants) => ({
type: COMMENTS_FETCH_SUCCESS,
id,
descendants,
})
export function fetchStatusSuccess(skipLoading) {
return {
type: STATUS_FETCH_SUCCESS,
skipLoading,
};
};
const fetchCommentsFail = (id, error) => ({
type: COMMENTS_FETCH_FAIL,
id,
error,
skipAlert: true,
})
export function fetchStatusFail(id, error, skipLoading) {
return {
type: STATUS_FETCH_FAIL,
id,
error,
skipLoading,
skipAlert: true,
};
};
export function editStatus(status) {
return dispatch => {
dispatch({
type: STATUS_EDIT,
status,
});
dispatch(openModal('COMPOSE'));
};
};
export function deleteStatus(id, routerHistory) {
return (dispatch, getState) => {
if (!me) return;
let status = getState().getIn(['statuses', id]);
if (status.get('poll')) {
status = status.set('poll', getState().getIn(['polls', status.get('poll')]));
}
dispatch(deleteStatusRequest(id));
api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
evictStatus(id);
dispatch(deleteStatusSuccess(id));
dispatch(deleteFromTimelines(id));
}).catch(error => {
dispatch(deleteStatusFail(id, error));
});
};
};
export function deleteStatusRequest(id) {
return {
type: STATUS_DELETE_REQUEST,
id: id,
};
};
export function deleteStatusSuccess(id) {
return {
type: STATUS_DELETE_SUCCESS,
id: id,
};
};
export function deleteStatusFail(id, error) {
return {
type: STATUS_DELETE_FAIL,
id: id,
error: error,
};
};
export function fetchContext(id, ensureIsReply) {
return (dispatch, getState) => {
if (ensureIsReply) {
const isReply = !!getState().getIn(['statuses', id, 'in_reply_to_id'], null)
if (!isReply) return;
}
dispatch(fetchContextRequest(id));
api(getState).get(`/api/v1/statuses/${id}/context`).then(response => {
dispatch(importFetchedStatuses(response.data.ancestors.concat(response.data.descendants)));
dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants));
}).catch(error => {
if (error.response && error.response.status === 404) {
dispatch(deleteFromTimelines(id));
}
dispatch(fetchContextFail(id, error));
});
};
};
export function fetchComments(id) {
return (dispatch, getState) => {
dispatch(fetchCommentsRequest(id));
api(getState).get(`/api/v1/statuses/${id}/comments`).then(response => {
dispatch(importFetchedStatuses(response.data.descendants));
dispatch(fetchCommentsSuccess(id, response.data.descendants));
}).catch(error => {
if (error.response && error.response.status === 404) {
dispatch(deleteFromTimelines(id));
}
dispatch(fetchCommentsFail(id, error));
});
};
};
export function fetchContextRequest(id) {
return {
type: CONTEXT_FETCH_REQUEST,
id,
};
};
export function fetchContextSuccess(id, ancestors, descendants) {
return {
type: CONTEXT_FETCH_SUCCESS,
id,
ancestors,
descendants,
statuses: ancestors.concat(descendants),
};
};
export function fetchContextFail(id, error) {
return {
type: CONTEXT_FETCH_FAIL,
id,
error,
skipAlert: true,
};
};
export function fetchCommentsRequest(id) {
return {
type: COMMENTS_FETCH_REQUEST,
id,
};
};
export function fetchCommentsSuccess(id, descendants) {
return {
type: COMMENTS_FETCH_SUCCESS,
id,
descendants,
};
};
export function fetchCommentsFail(id, error) {
return {
type: COMMENTS_FETCH_FAIL,
id,
error,
skipAlert: true,
};
};
export function hideStatus(ids) {
/**
*
*/
export const hideStatus = (ids) => {
if (!Array.isArray(ids)) {
ids = [ids];
ids = [ids]
}
return {
type: STATUS_HIDE,
ids,
};
};
}
}
export function revealStatus(ids) {
/**
*
*/
export const revealStatus = (ids) => {
if (!Array.isArray(ids)) {
ids = [ids];
ids = [ids]
}
return {
type: STATUS_REVEAL,
ids,
};
};
}
}
export function updateStatusStats(data) {
return {
type: UPDATE_STATUS_STATS,
data,
};
};
/**
*
*/
export const updateStatusStats = (data) => ({
type: UPDATE_STATUS_STATS,
data,
})

View File

@@ -71,3 +71,20 @@ export const connectStatusUpdateStream = () => {
*
*/
export const connectUserStream = () => connectTimelineStream('home', 'user')
/**
*
*/
export const connectMessageStream = () => {
return connectStream('chat_messages', null, (dispatch, getState) => {
return {
onConnect() {},
onDisconnect() {},
onReceive (data) {
//
},
}
})
}

View File

@@ -44,20 +44,17 @@ const fetchSuggestions = (suggestionType, dispatch, getState, unlimited = false)
const fetchSuggestionsRequest = (suggestionType) => ({
type: SUGGESTIONS_FETCH_REQUEST,
skipLoading: true,
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,

View File

@@ -1,133 +1,139 @@
import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable';
import { importFetchedStatus, importFetchedStatuses } from './importer';
import api, { getLinks } from '../api';
import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable'
import noop from 'lodash.noop'
import { importFetchedStatus, importFetchedStatuses } from './importer'
import api, { getLinks } from '../api'
import { fetchRelationships } from './accounts'
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE'
export const TIMELINE_DELETE = 'TIMELINE_DELETE'
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR'
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE'
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE'
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP'
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST'
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS'
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL'
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT'
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'
export const MAX_QUEUED_ITEMS = 40;
export function updateTimeline(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
dispatch(importFetchedStatus(status));
dispatch({
type: TIMELINE_UPDATE,
timeline,
status,
});
};
};
export function updateTimelineQueue(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
status,
});
}
};
export function forceDequeueTimeline(timeline) {
return (dispatch) => {
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
})
}
}
export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
return (dispatch, getState) => {
const queuedItems = getState().getIn(['timelines', timeline, 'queuedItems'], ImmutableList());
const totalQueuedItemsCount = getState().getIn(['timelines', timeline, 'totalQueuedItemsCount'], 0);
let shouldDispatchDequeue = true;
if (totalQueuedItemsCount === 0) {
return;
} else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
queuedItems.forEach(status => {
dispatch(updateTimeline(timeline, status.toJS(), null));
});
} else {
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timeline));
expandFunc();
} else {
if (timeline === 'home') {
dispatch(clearTimeline(timeline));
dispatch(expandHomeTimeline(optionalExpandArgs));
} else if (timeline === 'community') {
dispatch(clearTimeline(timeline));
dispatch(expandCommunityTimeline(optionalExpandArgs));
} else {
shouldDispatchDequeue = false;
}
}
}
if (!shouldDispatchDequeue) return;
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
});
}
};
export function deleteFromTimelines(id) {
return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']);
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]);
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
dispatch({
type: TIMELINE_DELETE,
id,
accountId,
references,
reblogOf,
});
};
};
export function clearTimeline(timeline) {
return (dispatch) => {
dispatch({ type: TIMELINE_CLEAR, timeline });
};
};
const noOp = () => { };
export const MAX_QUEUED_ITEMS = 40
const parseTags = (tags = {}, mode) => {
return (tags[mode] || []).map((tag) => {
return tag.value;
});
};
return (tags[mode] || []).map((tag) => tag.value)
}
export const expandTimeline = (timelineId, path, params = {}, done = noOp) => (dispatch, getState) => {
/**
*
*/
export const updateTimeline = (timeline, status, accept) => (dispatch) => {
if (typeof accept === 'function' && !accept(status)) return
dispatch(importFetchedStatus(status))
dispatch({
type: TIMELINE_UPDATE,
timeline,
status,
})
}
/**
*
*/
export const updateTimelineQueue = (timeline, status, accept) => (dispatch) => {
if (typeof accept === 'function' && !accept(status)) return
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
status,
})
}
/**
*
*/
export const forceDequeueTimeline = (timeline) => (dispatch) => {
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
})
}
/**
*
*/
export const dequeueTimeline = (timeline, expandFunc, optionalExpandArgs) => (dispatch, getState) => {
const queuedItems = getState().getIn(['timelines', timeline, 'queuedItems'], ImmutableList())
const totalQueuedItemsCount = getState().getIn(['timelines', timeline, 'totalQueuedItemsCount'], 0)
let shouldDispatchDequeue = true
if (totalQueuedItemsCount === 0) return
if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
queuedItems.forEach((status) => {
dispatch(updateTimeline(timeline, status.toJS(), null))
})
} else {
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timeline))
expandFunc()
} else {
if (timeline === 'home') {
dispatch(clearTimeline(timeline))
dispatch(expandHomeTimeline(optionalExpandArgs))
} else if (timeline === 'community') {
dispatch(clearTimeline(timeline))
dispatch(expandCommunityTimeline(optionalExpandArgs))
} else {
shouldDispatchDequeue = false
}
}
}
if (!shouldDispatchDequeue) return
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
})
}
/**
*
*/
export const deleteFromTimelines = (id) => (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account'])
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')])
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null)
dispatch({
type: TIMELINE_DELETE,
id,
accountId,
references,
reblogOf,
})
}
/**
*
*/
export const clearTimeline = (timeline) => (dispatch) => {
dispatch({
type: TIMELINE_CLEAR,
timeline
})
}
/**
*
*/
export const expandTimeline = (timelineId, path, params = {}, done = noop) => (dispatch, getState) => {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap())
const isLoadingMore = !!params.max_id
@@ -156,74 +162,179 @@ export const expandTimeline = (timelineId, path, params = {}, done = noOp) => (d
})
}
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);
export const expandProTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('pro', '/api/v1/timelines/pro', { max_id: maxId }, done);
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
export const expandAccountTimeline = (accountId, { maxId, withReplies, commentsOnly } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}${commentsOnly ? ':comments_only' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { only_comments: commentsOnly, exclude_replies: (!withReplies && !commentsOnly), max_id: maxId });
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
export const expandAccountMediaTimeline = (accountId, { maxId, limit, mediaType } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: limit || 20, media_type: mediaType });
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
export const expandGroupTimeline = (id, { sortBy, maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { sort_by: sortBy, max_id: maxId, only_media: onlyMedia }, done);
export const expandGroupFeaturedTimeline = (groupId, done = noOp) => expandTimeline(`group:${groupId}:pinned`, `/api/v1/timelines/group_pins/${groupId}`, {}, done);
export const expandGroupCollectionTimeline = (collectionType, { sortBy, maxId } = {}, done = noOp) => expandTimeline(`group_collection:${collectionType}`, `/api/v1/timelines/group_collection/${collectionType}`, { sort_by: sortBy, max_id: maxId }, done);
export const expandLinkTimeline = (linkId, { maxId } = {}, done = noOp) => expandTimeline(`link:${linkId}`, `/api/v1/timelines/preview_card/${linkId}`, { max_id: maxId }, done);
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
const expandTimelineRequest = (timeline, isLoadingMore) => ({
type: TIMELINE_EXPAND_REQUEST,
timeline,
skipLoading: !isLoadingMore,
})
const expandTimelineSuccess = (timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) => ({
type: TIMELINE_EXPAND_SUCCESS,
timeline,
statuses,
next,
partial,
isLoadingRecent,
skipLoading: !isLoadingMore,
})
const expandTimelineFail = (timeline, error, isLoadingMore) => ({
type: TIMELINE_EXPAND_FAIL,
timeline,
error,
skipLoading: !isLoadingMore,
})
/**
*
*/
export const scrollTopTimeline = (timeline, top) => ({
type: TIMELINE_SCROLL_TOP,
timeline,
top,
})
/**
*
*/
export const connectTimeline = (timeline) => ({
type: TIMELINE_CONNECT,
timeline,
})
/**
*
*/
export const disconnectTimeline = (timeline) => ({
type: TIMELINE_DISCONNECT,
timeline,
})
/**
*
*/
export const expandHomeTimeline = ({ maxId } = {}, done = noop) => {
return expandTimeline('home', '/api/v1/timelines/home', {
max_id: maxId,
}, done)
}
/**
*
*/
export const expandExploreTimeline = ({ maxId, sortBy } = {}, done = noop) => {
return expandTimeline('explore', '/api/v1/timelines/explore', {
max_id: maxId,
sort_by: sortBy,
}, done)
}
/**
*
*/
export const expandProTimeline = ({ maxId } = {}, done = noop) => {
return expandTimeline('pro', '/api/v1/timelines/pro', {
max_id: maxId,
}, done)
}
/**
*
*/
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noop) => {
return expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', {
max_id: maxId,
only_media: !!onlyMedia,
}, done)
}
/**
*
*/
export const expandAccountTimeline = (accountId, { maxId, withReplies, commentsOnly } = {}) => {
let key = `account:${accountId}${withReplies ? ':with_replies' : ''}${commentsOnly ? ':comments_only' : ''}`
return expandTimeline(key, `/api/v1/accounts/${accountId}/statuses`, {
only_comments: commentsOnly,
exclude_replies: (!withReplies && !commentsOnly),
max_id: maxId,
})
}
/**
*
*/
export const expandAccountFeaturedTimeline = (accountId) => {
return expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, {
pinned: true,
})
}
/**
*
*/
export const expandAccountMediaTimeline = (accountId, { maxId, limit, mediaType } = {}) => {
return expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, {
max_id: maxId,
only_media: true,
limit: limit || 20,
media_type: mediaType
})
}
/**
*
*/
export const expandListTimeline = (id, { maxId } = {}, done = noop) => {
return expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, {
max_id: maxId,
}, done)
}
/**
*
*/
export const expandGroupTimeline = (id, { sortBy, maxId, onlyMedia } = {}, done = noop) => {
return expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, {
sort_by: sortBy,
max_id: maxId,
only_media: onlyMedia
}, done)
}
/**
*
*/
export const expandGroupFeaturedTimeline = (groupId, done = noop) => {
return expandTimeline(`group:${groupId}:pinned`, `/api/v1/timelines/group_pins/${groupId}`, {}, done)
}
/**
*
*/
export const expandGroupCollectionTimeline = (collectionType, { sortBy, maxId } = {}, done = noop) => {
return expandTimeline(`group_collection:${collectionType}`, `/api/v1/timelines/group_collection/${collectionType}`, {
sort_by: sortBy,
max_id: maxId,
}, done)
}
/**
*
*/
export const expandLinkTimeline = (linkId, { maxId } = {}, done = noop) => {
return expandTimeline(`link:${linkId}`, `/api/v1/timelines/preview_card/${linkId}`, {
max_id: maxId,
}, done)
}
/**
*
*/
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noop) => {
return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
max_id: maxId,
any: parseTags(tags, 'any'),
all: parseTags(tags, 'all'),
none: parseTags(tags, 'none'),
}, done);
};
export function expandTimelineRequest(timeline, isLoadingMore) {
return {
type: TIMELINE_EXPAND_REQUEST,
timeline,
skipLoading: !isLoadingMore,
};
};
export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
return {
type: TIMELINE_EXPAND_SUCCESS,
timeline,
statuses,
next,
partial,
isLoadingRecent,
skipLoading: !isLoadingMore,
};
};
export function expandTimelineFail(timeline, error, isLoadingMore) {
return {
type: TIMELINE_EXPAND_FAIL,
timeline,
error,
skipLoading: !isLoadingMore,
};
};
export function connectTimeline(timeline) {
return {
type: TIMELINE_CONNECT,
timeline,
};
};
export function disconnectTimeline(timeline) {
return {
type: TIMELINE_DISCONNECT,
timeline,
};
};
export function scrollTopTimeline(timeline, top) {
return {
type: TIMELINE_SCROLL_TOP,
timeline,
top,
};
};
}, done)
}