Merge branch 'ts' into 'develop'
Reducers: TypeScript See merge request soapbox-pub/soapbox-fe!1498
This commit is contained in:
commit
bdb958a613
|
@ -142,7 +142,7 @@ export function expandDomainBlocks() {
|
|||
return (dispatch, getState) => {
|
||||
if (!isLoggedIn(getState)) return;
|
||||
|
||||
const url = getState().getIn(['domain_lists', 'blocks', 'next']);
|
||||
const url = getState().domain_lists.blocks.next;
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
|
|
|
@ -249,7 +249,7 @@ export interface IDropdown extends RouteComponentProps {
|
|||
) => void,
|
||||
onClose?: (id: number) => void,
|
||||
dropdownPlacement?: string,
|
||||
openDropdownId?: number,
|
||||
openDropdownId?: number | null,
|
||||
openedViaKeyboard?: boolean,
|
||||
text?: string,
|
||||
onShiftClick?: React.EventHandler<React.MouseEvent | React.KeyboardEvent>,
|
||||
|
|
|
@ -92,7 +92,7 @@ export const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }
|
|||
|
||||
if (!account) return null;
|
||||
const accountBio = { __html: account.note_emojified };
|
||||
const followedBy = me !== account.id && account.relationship.get('followed_by') === true;
|
||||
const followedBy = me !== account.id && account.relationship?.followed_by === true;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -581,7 +581,7 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
|
|||
const favouriteCount = status.favourites_count;
|
||||
|
||||
const emojiReactCount = reduceEmoji(
|
||||
(status.getIn(['pleroma', 'emoji_reactions']) || ImmutableList()) as ImmutableList<any>,
|
||||
(status.pleroma.get('emoji_reactions') || ImmutableList()) as ImmutableList<any>,
|
||||
favouriteCount,
|
||||
status.favourited,
|
||||
allowedEmoji,
|
||||
|
|
|
@ -35,7 +35,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
onFollow(account) {
|
||||
dispatch((_, getState) => {
|
||||
const unfollowModal = getSettings(getState()).get('unfollowModal');
|
||||
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
|
||||
if (account.relationship?.following || account.relationship?.requested) {
|
||||
if (unfollowModal) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
icon: require('@tabler/icons/icons/minus.svg'),
|
||||
|
@ -54,7 +54,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onBlock(account) {
|
||||
if (account.getIn(['relationship', 'blocking'])) {
|
||||
if (account.relationship?.blocking) {
|
||||
dispatch(unblockAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(blockAccount(account.get('id')));
|
||||
|
@ -62,7 +62,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onMute(account) {
|
||||
if (account.getIn(['relationship', 'muting'])) {
|
||||
if (account.relationship?.muting) {
|
||||
dispatch(unmuteAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(initMuteModal(account));
|
||||
|
|
|
@ -11,9 +11,9 @@ import type { RootState } from 'soapbox/store';
|
|||
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
isModalOpen: Boolean(state.modals.size && state.modals.last().modalType === 'ACTIONS'),
|
||||
dropdownPlacement: state.dropdown_menu.get('placement'),
|
||||
openDropdownId: state.dropdown_menu.get('openId'),
|
||||
openedViaKeyboard: state.dropdown_menu.get('keyboard'),
|
||||
dropdownPlacement: state.dropdown_menu.placement,
|
||||
openDropdownId: state.dropdown_menu.openId,
|
||||
openedViaKeyboard: state.dropdown_menu.keyboard,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch, { status, items }: Partial<IDropdown>) => ({
|
||||
|
|
|
@ -235,8 +235,8 @@ class Header extends ImmutablePureComponent {
|
|||
// });
|
||||
// }
|
||||
|
||||
if (account.getIn(['relationship', 'following'])) {
|
||||
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
||||
if (account.relationship?.following) {
|
||||
if (account.relationship?.showing_reblogs) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.hideReblogs, { name: account.get('username') }),
|
||||
action: this.props.onReblogToggle,
|
||||
|
@ -251,7 +251,7 @@ class Header extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
if (features.accountSubscriptions) {
|
||||
if (account.getIn(['relationship', 'subscribing'])) {
|
||||
if (account.relationship?.subscribing) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.unsubscribe, { name: account.get('username') }),
|
||||
action: this.props.onSubscriptionToggle,
|
||||
|
@ -274,7 +274,7 @@ class Header extends ImmutablePureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
// menu.push({ text: intl.formatMessage(account.getIn(['relationship', 'endorsed']) ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
|
||||
// menu.push({ text: intl.formatMessage(account.relationship?.endorsed ? messages.unendorse : messages.endorse), action: this.props.onEndorseToggle });
|
||||
menu.push(null);
|
||||
} else if (features.lists && features.unrestrictedLists) {
|
||||
menu.push({
|
||||
|
@ -284,7 +284,7 @@ class Header extends ImmutablePureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
if (features.removeFromFollowers && account.getIn(['relationship', 'followed_by'])) {
|
||||
if (features.removeFromFollowers && account.relationship?.followed_by) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.removeFromFollowers),
|
||||
action: this.props.onRemoveFromFollowers,
|
||||
|
@ -292,7 +292,7 @@ class Header extends ImmutablePureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
if (account.getIn(['relationship', 'muting'])) {
|
||||
if (account.relationship?.muting) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.unmute, { name: account.get('username') }),
|
||||
action: this.props.onMute,
|
||||
|
@ -306,7 +306,7 @@ class Header extends ImmutablePureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
if (account.getIn(['relationship', 'blocking'])) {
|
||||
if (account.relationship?.blocking) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.unblock, { name: account.get('username') }),
|
||||
action: this.props.onBlock,
|
||||
|
@ -332,7 +332,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
menu.push(null);
|
||||
|
||||
if (account.getIn(['relationship', 'domain_blocking'])) {
|
||||
if (account.relationship?.domain_blocking) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(messages.unblockDomain, { domain }),
|
||||
action: this.props.onUnblockDomain,
|
||||
|
@ -463,7 +463,7 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
if (!account || !me) return info;
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
||||
if (me !== account.get('id') && account.relationship?.followed_by) {
|
||||
info.push(
|
||||
<Badge
|
||||
key='followed_by'
|
||||
|
@ -471,7 +471,7 @@ class Header extends ImmutablePureComponent {
|
|||
title={<FormattedMessage id='account.follows_you' defaultMessage='Follows you' />}
|
||||
/>,
|
||||
);
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
|
||||
} else if (me !== account.get('id') && account.relationship?.blocking) {
|
||||
info.push(
|
||||
<Badge
|
||||
key='blocked'
|
||||
|
@ -481,7 +481,7 @@ class Header extends ImmutablePureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
|
||||
if (me !== account.get('id') && account.relationship?.muting) {
|
||||
info.push(
|
||||
<Badge
|
||||
key='muted'
|
||||
|
@ -489,7 +489,7 @@ class Header extends ImmutablePureComponent {
|
|||
title={<FormattedMessage id='account.muted' defaultMessage='Muted' />}
|
||||
/>,
|
||||
);
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
|
||||
} else if (me !== account.get('id') && account.relationship?.domain_blocking) {
|
||||
info.push(
|
||||
<Badge
|
||||
key='domain_blocked'
|
||||
|
|
|
@ -75,7 +75,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
onFollow(account) {
|
||||
dispatch((_, getState) => {
|
||||
const unfollowModal = getSettings(getState()).get('unfollowModal');
|
||||
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
|
||||
if (account.relationship?.following || account.relationship?.requested) {
|
||||
if (unfollowModal) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: <FormattedMessage id='confirmations.unfollow.message' defaultMessage='Are you sure you want to unfollow {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||
|
@ -92,7 +92,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onBlock(account) {
|
||||
if (account.getIn(['relationship', 'blocking'])) {
|
||||
if (account.relationship?.blocking) {
|
||||
dispatch(unblockAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
|
@ -119,7 +119,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onReblogToggle(account) {
|
||||
if (account.getIn(['relationship', 'showing_reblogs'])) {
|
||||
if (account.relationship?.showing_reblogs) {
|
||||
dispatch(followAccount(account.get('id'), { reblogs: false }));
|
||||
} else {
|
||||
dispatch(followAccount(account.get('id'), { reblogs: true }));
|
||||
|
@ -127,7 +127,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onSubscriptionToggle(account) {
|
||||
if (account.getIn(['relationship', 'subscribing'])) {
|
||||
if (account.relationship?.subscribing) {
|
||||
dispatch(unsubscribeAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(subscribeAccount(account.get('id')));
|
||||
|
@ -135,7 +135,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onNotifyToggle(account) {
|
||||
if (account.getIn(['relationship', 'notifying'])) {
|
||||
if (account.relationship?.notifying) {
|
||||
dispatch(followAccount(account.get('id'), { notify: false }));
|
||||
} else {
|
||||
dispatch(followAccount(account.get('id'), { notify: true }));
|
||||
|
@ -143,7 +143,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onEndorseToggle(account) {
|
||||
if (account.getIn(['relationship', 'endorsed'])) {
|
||||
if (account.relationship?.endorsed) {
|
||||
dispatch(unpinAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(pinAccount(account.get('id')));
|
||||
|
@ -155,7 +155,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
},
|
||||
|
||||
onMute(account) {
|
||||
if (account.getIn(['relationship', 'muting'])) {
|
||||
if (account.relationship?.muting) {
|
||||
dispatch(unmuteAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(initMuteModal(account));
|
||||
|
|
|
@ -39,7 +39,7 @@ const ReportStatus: React.FC<IReportStatus> = ({ status }) => {
|
|||
|
||||
return [{
|
||||
text: intl.formatMessage(messages.viewStatus, { acct: `@${acct}` }),
|
||||
to: `/@${acct}/posts/${status.get('id')}`,
|
||||
to: `/@${acct}/posts/${status.id}`,
|
||||
icon: require('@tabler/icons/icons/pencil.svg'),
|
||||
}, {
|
||||
text: intl.formatMessage(messages.deleteStatus, { acct: `@${acct}` }),
|
||||
|
|
|
@ -7,8 +7,6 @@ import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
|||
|
||||
import Column from '../ui/components/column';
|
||||
|
||||
import type { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'column.admin.moderation_log', defaultMessage: 'Moderation Log' },
|
||||
emptyMessage: { id: 'admin.moderation_log.empty_message', defaultMessage: 'You have not performed any moderation actions yet. When you do, a history will be shown here.' },
|
||||
|
@ -18,8 +16,10 @@ const ModerationLog = () => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const items = useAppSelector((state) => state.admin_log.get('index').map((i: number) => state.admin_log.getIn(['items', String(i)]))) as ImmutableMap<string, any>;
|
||||
const hasMore = useAppSelector((state) => state.admin_log.get('total', 0) - state.admin_log.get('index').count() > 0);
|
||||
const items = useAppSelector((state) => {
|
||||
return state.admin_log.index.map((i) => state.admin_log.items.get(String(i)));
|
||||
});
|
||||
const hasMore = useAppSelector((state) => state.admin_log.total - state.admin_log.index.count() > 0);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [lastPage, setLastPage] = useState(0);
|
||||
|
@ -56,12 +56,12 @@ const ModerationLog = () => {
|
|||
hasMore={hasMore}
|
||||
onLoadMore={handleLoadMore}
|
||||
>
|
||||
{items.map((item, i) => (
|
||||
<div className='logentry' key={i}>
|
||||
<div className='logentry__message'>{item.get('message')}</div>
|
||||
{items.map((item) => item && (
|
||||
<div className='logentry' key={item.id}>
|
||||
<div className='logentry__message'>{item.message}</div>
|
||||
<div className='logentry__timestamp'>
|
||||
<FormattedDate
|
||||
value={new Date(item.get('time') * 1000)}
|
||||
value={new Date(item.time * 1000)}
|
||||
hour12={false}
|
||||
year='numeric'
|
||||
month='short'
|
||||
|
|
|
@ -17,7 +17,7 @@ const Search: React.FC = () => {
|
|||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const value = useAppSelector(state => state.aliases.getIn(['suggestions', 'value'])) as string;
|
||||
const value = useAppSelector(state => state.aliases.suggestions.value);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
dispatch(changeAliasesSuggestions(e.target.value));
|
||||
|
|
|
@ -35,11 +35,11 @@ const Aliases = () => {
|
|||
const instance = state.instance;
|
||||
const features = getFeatures(instance);
|
||||
|
||||
if (features.accountMoving) return state.aliases.getIn(['aliases', 'items'], ImmutableList());
|
||||
if (features.accountMoving) return state.aliases.aliases.items;
|
||||
return account!.pleroma.get('also_known_as');
|
||||
}) as ImmutableList<string>;
|
||||
const searchAccountIds = useAppSelector((state) => state.aliases.getIn(['suggestions', 'items'])) as ImmutableList<string>;
|
||||
const loaded = useAppSelector((state) => state.aliases.getIn(['suggestions', 'loaded']));
|
||||
const searchAccountIds = useAppSelector((state) => state.aliases.suggestions.items);
|
||||
const loaded = useAppSelector((state) => state.aliases.suggestions.loaded);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchAliases);
|
||||
|
|
|
@ -20,9 +20,9 @@ const Bookmarks: React.FC = () => {
|
|||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const statusIds = useAppSelector((state) => state.status_lists.getIn(['bookmarks', 'items']));
|
||||
const isLoading = useAppSelector((state) => state.status_lists.getIn(['bookmarks', 'isLoading'], true));
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.getIn(['bookmarks', 'next']));
|
||||
const statusIds = useAppSelector((state) => state.status_lists.get('bookmarks')!.items);
|
||||
const isLoading = useAppSelector((state) => state.status_lists.get('bookmarks')!.isLoading);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.get('bookmarks')!.next);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchBookmarkedStatuses());
|
||||
|
@ -43,7 +43,7 @@ const Bookmarks: React.FC = () => {
|
|||
statusIds={statusIds}
|
||||
scrollKey='bookmarked_statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => handleLoadMore(dispatch)}
|
||||
onRefresh={handleRefresh}
|
||||
emptyMessage={emptyMessage}
|
||||
|
|
|
@ -26,7 +26,7 @@ const AccountCard: React.FC<IAccountCard> = ({ id }) => {
|
|||
|
||||
if (!account) return null;
|
||||
|
||||
const followedBy = me !== account.id && account.relationship.get('followed_by');
|
||||
const followedBy = me !== account.id && account.relationship?.followed_by;
|
||||
|
||||
return (
|
||||
<div className='directory__card'>
|
||||
|
|
|
@ -24,8 +24,8 @@ const DomainBlocks: React.FC = () => {
|
|||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const domains = useAppSelector((state) => state.domain_lists.getIn(['blocks', 'items'])) as string[];
|
||||
const hasMore = useAppSelector((state) => !!state.domain_lists.getIn(['blocks', 'next']));
|
||||
const domains = useAppSelector((state) => state.domain_lists.blocks.items);
|
||||
const hasMore = useAppSelector((state) => !!state.domain_lists.blocks.next);
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch(fetchDomainBlocks());
|
||||
|
|
|
@ -32,9 +32,9 @@ const mapStateToProps = (state, { params }) => {
|
|||
if (isMyAccount) {
|
||||
return {
|
||||
isMyAccount,
|
||||
statusIds: state.getIn(['status_lists', 'favourites', 'items']),
|
||||
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
|
||||
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
|
||||
statusIds: state.status_lists.get('favourites').items,
|
||||
isLoading: state.status_lists.get('favourites').isLoading,
|
||||
hasMore: !!state.status_lists.get('favourites').next,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -57,9 +57,9 @@ const mapStateToProps = (state, { params }) => {
|
|||
unavailable,
|
||||
username,
|
||||
isAccount: !!state.getIn(['accounts', accountId]),
|
||||
statusIds: state.getIn(['status_lists', `favourites:${accountId}`, 'items'], []),
|
||||
isLoading: state.getIn(['status_lists', `favourites:${accountId}`, 'isLoading'], true),
|
||||
hasMore: !!state.getIn(['status_lists', `favourites:${accountId}`, 'next']),
|
||||
statusIds: state.status_lists.get(`favourites:${accountId}`)?.items || [],
|
||||
isLoading: state.status_lists.get(`favourites:${accountId}`)?.isLoading,
|
||||
hasMore: !!state.status_lists.get(`favourites:${accountId}`)?.next,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -147,7 +147,7 @@ class Favourites extends ImmutablePureComponent {
|
|||
statusIds={statusIds}
|
||||
scrollKey='favourited_statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={this.handleLoadMore}
|
||||
emptyMessage={emptyMessage}
|
||||
/>
|
||||
|
|
|
@ -11,8 +11,8 @@ import Account from './account';
|
|||
const FollowRecommendationsList: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const suggestions = useAppSelector((state) => state.suggestions.get('items'));
|
||||
const isLoading = useAppSelector((state) => state.suggestions.get('isLoading'));
|
||||
const suggestions = useAppSelector((state) => state.suggestions.items);
|
||||
const isLoading = useAppSelector((state) => state.suggestions.isLoading);
|
||||
|
||||
useEffect(() => {
|
||||
if (suggestions.size === 0) {
|
||||
|
@ -30,8 +30,8 @@ const FollowRecommendationsList: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className='column-list'>
|
||||
{suggestions.size > 0 ? suggestions.map((suggestion: { account: string }, idx: number) => (
|
||||
<Account key={idx} id={suggestion.account} />
|
||||
{suggestions.size > 0 ? suggestions.map((suggestion) => (
|
||||
<Account key={suggestion.account} id={suggestion.account} />
|
||||
)) : (
|
||||
<div className='column-list__empty-message'>
|
||||
<FormattedMessage id='empty_column.follow_recommendations' defaultMessage='Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.' />
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
import debounce from 'lodash/debounce';
|
||||
import * as React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
@ -13,9 +12,9 @@ import { useAppSelector } from 'soapbox/hooks';
|
|||
const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const suggestions = useAppSelector((state) => state.suggestions.get('items'));
|
||||
const hasMore = useAppSelector((state) => !!state.suggestions.get('next'));
|
||||
const isLoading = useAppSelector((state) => state.suggestions.get('isLoading'));
|
||||
const suggestions = useAppSelector((state) => state.suggestions.items);
|
||||
const hasMore = useAppSelector((state) => !!state.suggestions.next);
|
||||
const isLoading = useAppSelector((state) => state.suggestions.isLoading);
|
||||
|
||||
const handleLoadMore = debounce(() => {
|
||||
if (isLoading) {
|
||||
|
@ -40,11 +39,11 @@ const SuggestedAccountsStep = ({ onNext }: { onNext: () => void }) => {
|
|||
useWindowScroll={false}
|
||||
style={{ height: 320 }}
|
||||
>
|
||||
{suggestions.map((suggestion: ImmutableMap<string, any>) => (
|
||||
<div key={suggestion.get('account')} className='py-2'>
|
||||
{suggestions.map((suggestion) => (
|
||||
<div key={suggestion.account} className='py-2'>
|
||||
<AccountContainer
|
||||
// @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't
|
||||
id={suggestion.get('account')}
|
||||
id={suggestion.account}
|
||||
showProfileHoverCard={false}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -21,8 +21,8 @@ const mapStateToProps = (state, { params }) => {
|
|||
const meUsername = state.getIn(['accounts', me, 'username'], '');
|
||||
return {
|
||||
isMyAccount: (username.toLowerCase() === meUsername.toLowerCase()),
|
||||
statusIds: state.getIn(['status_lists', 'pins', 'items']),
|
||||
hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
|
||||
statusIds: state.status_lists.get('pins').items,
|
||||
hasMore: !!state.status_lists.get('pins').next,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ const ScheduledStatuses = () => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const statusIds = useAppSelector((state) => state.status_lists.getIn(['scheduled_statuses', 'items']));
|
||||
const isLoading = useAppSelector((state) => state.status_lists.getIn(['scheduled_statuses', 'isLoading']));
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.getIn(['scheduled_statuses', 'next']));
|
||||
const statusIds = useAppSelector((state) => state.status_lists.get('scheduled_statuses')!.items);
|
||||
const isLoading = useAppSelector((state) => state.status_lists.get('scheduled_statuses')!.isLoading);
|
||||
const hasMore = useAppSelector((state) => !!state.status_lists.get('scheduled_statuses')!.next);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchScheduledStatuses());
|
||||
|
@ -37,7 +37,7 @@ const ScheduledStatuses = () => {
|
|||
<ScrollableList
|
||||
scrollKey='scheduled_statuses'
|
||||
hasMore={hasMore}
|
||||
isLoading={isLoading}
|
||||
isLoading={typeof isLoading === 'boolean' ? isLoading : true}
|
||||
onLoadMore={() => handleLoadMore(dispatch)}
|
||||
emptyMessage={emptyMessage}
|
||||
>
|
||||
|
|
|
@ -387,9 +387,9 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
|||
if (me) {
|
||||
if (features.bookmarks) {
|
||||
menu.push({
|
||||
text: intl.formatMessage(status.get('bookmarked') ? messages.unbookmark : messages.bookmark),
|
||||
text: intl.formatMessage(status.bookmarked ? messages.unbookmark : messages.bookmark),
|
||||
action: this.handleBookmarkClick,
|
||||
icon: require(status.get('bookmarked') ? '@tabler/icons/icons/bookmark-off.svg' : '@tabler/icons/icons/bookmark.svg'),
|
||||
icon: require(status.bookmarked ? '@tabler/icons/icons/bookmark-off.svg' : '@tabler/icons/icons/bookmark.svg'),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -406,7 +406,7 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
|||
menu.push(null);
|
||||
} else if (status.visibility === 'private') {
|
||||
menu.push({
|
||||
text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private),
|
||||
text: intl.formatMessage(status.reblogged ? messages.cancel_reblog_private : messages.reblog_private),
|
||||
action: this.handleReblogClick,
|
||||
icon: require('@tabler/icons/icons/repeat.svg'),
|
||||
});
|
||||
|
@ -496,7 +496,7 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
|||
}
|
||||
|
||||
menu.push({
|
||||
text: intl.formatMessage(status.get('sensitive') === false ? messages.markStatusSensitive : messages.markStatusNotSensitive),
|
||||
text: intl.formatMessage(status.sensitive === false ? messages.markStatusSensitive : messages.markStatusNotSensitive),
|
||||
action: this.handleToggleStatusSensitivity,
|
||||
icon: require('@tabler/icons/icons/alert-triangle.svg'),
|
||||
});
|
||||
|
@ -523,18 +523,18 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
|
|||
}
|
||||
}
|
||||
|
||||
const canShare = ('share' in navigator) && status.get('visibility') === 'public';
|
||||
const canShare = ('share' in navigator) && status.visibility === 'public';
|
||||
|
||||
|
||||
let reblogIcon = require('@tabler/icons/icons/repeat.svg');
|
||||
|
||||
if (status.get('visibility') === 'direct') {
|
||||
if (status.visibility === 'direct') {
|
||||
reblogIcon = require('@tabler/icons/icons/mail.svg');
|
||||
} else if (status.get('visibility') === 'private') {
|
||||
} else if (status.visibility === 'private') {
|
||||
reblogIcon = require('@tabler/icons/icons/lock.svg');
|
||||
}
|
||||
|
||||
const reblog_disabled = (status.get('visibility') === 'direct' || status.get('visibility') === 'private');
|
||||
const reblog_disabled = (status.visibility === 'direct' || status.visibility === 'private');
|
||||
|
||||
const reblogMenu: Menu = [{
|
||||
text: intl.formatMessage(status.reblogged ? messages.cancel_reblog_private : messages.reblog),
|
||||
|
|
|
@ -53,7 +53,7 @@ const StatusInteractionBar: React.FC<IStatusInteractionBar> = ({ status }): JSX.
|
|||
|
||||
const getNormalizedReacts = () => {
|
||||
return reduceEmoji(
|
||||
ImmutableList(status.getIn(['pleroma', 'emoji_reactions']) as any),
|
||||
ImmutableList(status.pleroma.get('emoji_reactions') as any),
|
||||
status.favourites_count,
|
||||
status.favourited,
|
||||
allowedEmoji,
|
||||
|
|
|
@ -332,7 +332,7 @@ class Status extends ImmutablePureComponent<IStatus, IStatusState> {
|
|||
handleEditClick = (status: StatusEntity) => {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(editStatus(status.get('id')));
|
||||
dispatch(editStatus(status.id));
|
||||
}
|
||||
|
||||
handleDirectClick = (account: AccountEntity, router: History) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import React from 'react';
|
||||
|
||||
import { render, screen } from '../../../../jest/test-helpers';
|
||||
|
@ -16,12 +16,12 @@ describe('<WhoToFollow />', () => {
|
|||
avatar: 'test.jpg',
|
||||
}),
|
||||
}),
|
||||
suggestions: ImmutableMap({
|
||||
items: fromJS([{
|
||||
suggestions: {
|
||||
items: ImmutableOrderedSet([{
|
||||
source: 'staff',
|
||||
account: '1',
|
||||
}]),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
render(<WhoToFollowPanel limit={1} />, null, store);
|
||||
|
@ -44,8 +44,8 @@ describe('<WhoToFollow />', () => {
|
|||
avatar: 'test.jpg',
|
||||
}),
|
||||
}),
|
||||
suggestions: ImmutableMap({
|
||||
items: fromJS([
|
||||
suggestions: {
|
||||
items: ImmutableOrderedSet([
|
||||
{
|
||||
source: 'staff',
|
||||
account: '1',
|
||||
|
@ -55,7 +55,7 @@ describe('<WhoToFollow />', () => {
|
|||
account: '2',
|
||||
},
|
||||
]),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
render(<WhoToFollowPanel limit={3} />, null, store);
|
||||
|
@ -78,8 +78,8 @@ describe('<WhoToFollow />', () => {
|
|||
avatar: 'test.jpg',
|
||||
}),
|
||||
}),
|
||||
suggestions: ImmutableMap({
|
||||
items: fromJS([
|
||||
suggestions: {
|
||||
items: ImmutableOrderedSet([
|
||||
{
|
||||
source: 'staff',
|
||||
account: '1',
|
||||
|
@ -89,7 +89,7 @@ describe('<WhoToFollow />', () => {
|
|||
account: '2',
|
||||
},
|
||||
]),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
render(<WhoToFollowPanel limit={1} />, null, store);
|
||||
|
@ -112,9 +112,9 @@ describe('<WhoToFollow />', () => {
|
|||
avatar: 'test.jpg',
|
||||
}),
|
||||
}),
|
||||
suggestions: ImmutableMap({
|
||||
items: fromJS([]),
|
||||
}),
|
||||
suggestions: {
|
||||
items: ImmutableOrderedSet([]),
|
||||
},
|
||||
};
|
||||
|
||||
render(<WhoToFollowPanel limit={1} />, null, store);
|
||||
|
|
|
@ -52,7 +52,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
const me = useAppSelector((state) => state.me);
|
||||
|
||||
const handleFollow = () => {
|
||||
if (account.getIn(['relationship', 'following']) || account.getIn(['relationship', 'requested'])) {
|
||||
if (account.relationship?.following || account.relationship?.requested) {
|
||||
dispatch(unfollowAccount(account.id));
|
||||
} else {
|
||||
dispatch(followAccount(account.id));
|
||||
|
@ -60,7 +60,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
};
|
||||
|
||||
const handleBlock = () => {
|
||||
if (account.getIn(['relationship', 'blocking'])) {
|
||||
if (account.relationship?.blocking) {
|
||||
dispatch(unblockAccount(account.id));
|
||||
} else {
|
||||
dispatch(blockAccount(account.id));
|
||||
|
@ -68,7 +68,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
};
|
||||
|
||||
const handleMute = () => {
|
||||
if (account.getIn(['relationship', 'muting'])) {
|
||||
if (account.relationship?.muting) {
|
||||
dispatch(unmuteAccount(account.id));
|
||||
} else {
|
||||
dispatch(muteAccount(account.id));
|
||||
|
@ -85,7 +85,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
|
||||
/** Handles actionType='muting' */
|
||||
const mutingAction = () => {
|
||||
const isMuted = account.getIn(['relationship', 'muting']);
|
||||
const isMuted = account.relationship?.muting;
|
||||
const messageKey = isMuted ? messages.unmute : messages.mute;
|
||||
const text = intl.formatMessage(messageKey, { name: account.username });
|
||||
|
||||
|
@ -101,7 +101,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
|
||||
/** Handles actionType='blocking' */
|
||||
const blockingAction = () => {
|
||||
const isBlocked = account.getIn(['relationship', 'blocking']);
|
||||
const isBlocked = account.relationship?.blocking;
|
||||
const messageKey = isBlocked ? messages.unblock : messages.block;
|
||||
const text = intl.formatMessage(messageKey, { name: account.username });
|
||||
|
||||
|
@ -154,8 +154,8 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
}
|
||||
|
||||
if (me !== account.id) {
|
||||
const isFollowing = account.getIn(['relationship', 'following']);
|
||||
const blockedBy = account.getIn(['relationship', 'blocked_by']) as boolean;
|
||||
const isFollowing = account.relationship?.following;
|
||||
const blockedBy = account.relationship?.blocked_by as boolean;
|
||||
|
||||
if (actionType) {
|
||||
if (actionType === 'muting') {
|
||||
|
@ -165,10 +165,10 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
}
|
||||
}
|
||||
|
||||
if (account.relationship.isEmpty()) {
|
||||
if (!account.relationship) {
|
||||
// Wait until the relationship is loaded
|
||||
return null;
|
||||
} else if (account.getIn(['relationship', 'requested'])) {
|
||||
} else if (account.relationship?.requested) {
|
||||
// Awaiting acceptance
|
||||
return (
|
||||
<Button
|
||||
|
@ -178,7 +178,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
onClick={handleFollow}
|
||||
/>
|
||||
);
|
||||
} else if (!account.getIn(['relationship', 'blocking']) && !account.getIn(['relationship', 'muting'])) {
|
||||
} else if (!account.relationship?.blocking && !account.relationship?.muting) {
|
||||
// Follow & Unfollow
|
||||
return (
|
||||
<Button
|
||||
|
@ -195,7 +195,7 @@ const ActionButton: React.FC<IActionButton> = ({ account, actionType, small }) =
|
|||
)}
|
||||
</Button>
|
||||
);
|
||||
} else if (account.getIn(['relationship', 'blocking'])) {
|
||||
} else if (account.relationship?.blocking) {
|
||||
// Unblock
|
||||
return (
|
||||
<Button
|
||||
|
|
|
@ -47,9 +47,9 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => {
|
|||
return (
|
||||
<Stack space={2} className='p-4 rounded-lg bg-gray-100 dark:bg-slate-700'>
|
||||
<AccountContainer
|
||||
id={status.get('account') as any}
|
||||
id={status.account as any}
|
||||
showProfileHoverCard={false}
|
||||
timestamp={status.get('created_at')}
|
||||
timestamp={status.created_at}
|
||||
hideActions
|
||||
/>
|
||||
|
||||
|
@ -59,10 +59,10 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => {
|
|||
collapsable
|
||||
/>
|
||||
|
||||
{status.get('media_attachments').size > 0 && (
|
||||
{status.media_attachments.size > 0 && (
|
||||
<AttachmentThumbs
|
||||
media={status.get('media_attachments')}
|
||||
sensitive={status.get('sensitive')}
|
||||
media={status.media_attachments}
|
||||
sensitive={status.sensitive}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
|
|
@ -29,14 +29,14 @@ const mapStateToProps = state => {
|
|||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onSubscriptionToggle(account) {
|
||||
if (account.getIn(['relationship', 'subscribing'])) {
|
||||
if (account.relationship?.subscribing) {
|
||||
dispatch(unsubscribeAccount(account.get('id')));
|
||||
} else {
|
||||
dispatch(subscribeAccount(account.get('id')));
|
||||
}
|
||||
},
|
||||
onNotifyToggle(account) {
|
||||
if (account.getIn(['relationship', 'notifying'])) {
|
||||
if (account.relationship?.notifying) {
|
||||
dispatch(followAccount(account.get('id'), { notify: false }));
|
||||
} else {
|
||||
dispatch(followAccount(account.get('id'), { notify: true }));
|
||||
|
@ -60,9 +60,9 @@ class SubscriptionButton extends ImmutablePureComponent {
|
|||
|
||||
render() {
|
||||
const { account, intl, features } = this.props;
|
||||
const subscribing = features.accountNotifies ? account.getIn(['relationship', 'notifying']) : account.getIn(['relationship', 'subscribing']);
|
||||
const following = account.getIn(['relationship', 'following']);
|
||||
const requested = account.getIn(['relationship', 'requested']);
|
||||
const subscribing = features.accountNotifies ? account.relationship?.notifying : account.relationship?.subscribing;
|
||||
const following = account.relationship?.following;
|
||||
const requested = account.relationship?.requested;
|
||||
|
||||
if (requested || following) {
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
import * as React from 'react';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { fetchSuggestions, dismissSuggestion } from 'soapbox/actions/suggestions';
|
||||
|
@ -8,6 +7,8 @@ import { Widget } from 'soapbox/components/ui';
|
|||
import AccountContainer from 'soapbox/containers/account_container';
|
||||
import { useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import type { Account as AccountEntity } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
|
||||
});
|
||||
|
@ -20,11 +21,11 @@ const WhoToFollowPanel = ({ limit }: IWhoToFollowPanel) => {
|
|||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const suggestions = useAppSelector((state) => state.suggestions.get('items'));
|
||||
const suggestions = useAppSelector((state) => state.suggestions.items);
|
||||
const suggestionsToRender = suggestions.slice(0, limit);
|
||||
|
||||
const handleDismiss = (account: ImmutableMap<string, any>) => {
|
||||
dispatch(dismissSuggestion(account.get('id')));
|
||||
const handleDismiss = (account: AccountEntity) => {
|
||||
dispatch(dismissSuggestion(account.id));
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
|
@ -45,11 +46,11 @@ const WhoToFollowPanel = ({ limit }: IWhoToFollowPanel) => {
|
|||
title={<FormattedMessage id='who_to_follow.title' defaultMessage='People To Follow' />}
|
||||
// onAction={handleAction}
|
||||
>
|
||||
{suggestionsToRender.map((suggestion: ImmutableMap<string, any>) => (
|
||||
{suggestionsToRender.map((suggestion) => (
|
||||
<AccountContainer
|
||||
key={suggestion.get('account')}
|
||||
key={suggestion.account}
|
||||
// @ts-ignore: TS thinks `id` is passed to <Account>, but it isn't
|
||||
id={suggestion.get('account')}
|
||||
id={suggestion.account}
|
||||
actionIcon={require('@tabler/icons/icons/x.svg')}
|
||||
actionTitle={intl.formatMessage(messages.dismissSuggestion)}
|
||||
onActionClick={handleDismiss}
|
||||
|
|
|
@ -343,7 +343,7 @@ const UI: React.FC = ({ children }) => {
|
|||
const features = useFeatures();
|
||||
const vapidKey = useAppSelector(state => getVapidKey(state));
|
||||
|
||||
const dropdownMenuIsOpen = useAppSelector(state => state.dropdown_menu.get('openId') !== null);
|
||||
const dropdownMenuIsOpen = useAppSelector(state => state.dropdown_menu.openId !== null);
|
||||
const accessToken = useAppSelector(state => getAccessToken(state));
|
||||
const streamingUrl = useAppSelector(state => state.instance.urls.get('streaming_api'));
|
||||
const standalone = useAppSelector(isStandalone);
|
||||
|
|
|
@ -17,7 +17,7 @@ import { unescapeHTML } from 'soapbox/utils/html';
|
|||
import { mergeDefined, makeEmojiMap } from 'soapbox/utils/normalizers';
|
||||
|
||||
import type { PatronAccount } from 'soapbox/reducers/patron';
|
||||
import type { Emoji, Field, EmbeddedEntity } from 'soapbox/types/entities';
|
||||
import type { Emoji, Field, EmbeddedEntity, Relationship } from 'soapbox/types/entities';
|
||||
|
||||
// https://docs.joinmastodon.org/entities/account/
|
||||
export const AccountRecord = ImmutableRecord({
|
||||
|
@ -61,7 +61,7 @@ export const AccountRecord = ImmutableRecord({
|
|||
note_emojified: '',
|
||||
note_plain: '',
|
||||
patron: null as PatronAccount | null,
|
||||
relationship: ImmutableMap<string, any>(),
|
||||
relationship: null as Relationship | null,
|
||||
should_refetch: false,
|
||||
staff: false,
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ export { ListRecord, normalizeList } from './list';
|
|||
export { MentionRecord, normalizeMention } from './mention';
|
||||
export { NotificationRecord, normalizeNotification } from './notification';
|
||||
export { PollRecord, PollOptionRecord, normalizePoll } from './poll';
|
||||
export { RelationshipRecord, normalizeRelationship } from './relationship';
|
||||
export { StatusRecord, normalizeStatus } from './status';
|
||||
export { StatusEditRecord, normalizeStatusEdit } from './status_edit';
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Relationship normalizer:
|
||||
* Converts API relationships into our internal format.
|
||||
* @see {@link https://docs.joinmastodon.org/entities/relationship/}
|
||||
*/
|
||||
import {
|
||||
Map as ImmutableMap,
|
||||
Record as ImmutableRecord,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
|
||||
// https://docs.joinmastodon.org/entities/relationship/
|
||||
// https://api.pleroma.social/#operation/AccountController.relationships
|
||||
export const RelationshipRecord = ImmutableRecord({
|
||||
blocked_by: false,
|
||||
blocking: false,
|
||||
domain_blocking: false,
|
||||
endorsed: false,
|
||||
followed_by: false,
|
||||
following: false,
|
||||
id: '',
|
||||
muting: false,
|
||||
muting_notifications: false,
|
||||
note: '',
|
||||
notifying: false,
|
||||
requested: false,
|
||||
showing_reblogs: false,
|
||||
subscribing: false,
|
||||
});
|
||||
|
||||
export const normalizeRelationship = (relationship: Record<string, any>) => {
|
||||
return RelationshipRecord(
|
||||
ImmutableMap(fromJS(relationship)),
|
||||
);
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
|
||||
import reducer from '../domain_lists';
|
||||
|
||||
describe('domain_lists reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(ImmutableMap({
|
||||
blocks: ImmutableMap({
|
||||
items: ImmutableOrderedSet(),
|
||||
}),
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import reducer from '../domain_lists';
|
||||
|
||||
describe('domain_lists reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {} as any).toJS()).toEqual({
|
||||
blocks: {
|
||||
items: [],
|
||||
next: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,13 +1,11 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import reducer from '../dropdown_menu';
|
||||
|
||||
describe('dropdown_menu reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(ImmutableMap({
|
||||
expect(reducer(undefined, {} as any).toJS()).toEqual({
|
||||
openId: null,
|
||||
placement: null,
|
||||
keyboard: false,
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import lain from 'soapbox/__fixtures__/lain.json';
|
||||
|
||||
|
@ -9,7 +9,7 @@ import reducer from '../relationships';
|
|||
|
||||
describe('relationships reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(ImmutableMap());
|
||||
expect(reducer(undefined, {} as any)).toEqual(ImmutableMap());
|
||||
});
|
||||
|
||||
describe('ACCOUNT_IMPORT', () => {
|
||||
|
@ -18,8 +18,8 @@ describe('relationships reducer', () => {
|
|||
type: ACCOUNT_IMPORT,
|
||||
account: lain,
|
||||
};
|
||||
const state = ImmutableMap();
|
||||
expect(reducer(state, action)).toEqual(fromJS({
|
||||
const state = ImmutableMap<string, any>();
|
||||
expect(reducer(state, action).toJS()).toEqual({
|
||||
'9v5bqYwY2jfmvPNhTM': {
|
||||
blocked_by: false,
|
||||
blocking: false,
|
||||
|
@ -30,11 +30,13 @@ describe('relationships reducer', () => {
|
|||
id: '9v5bqYwY2jfmvPNhTM',
|
||||
muting: false,
|
||||
muting_notifications: false,
|
||||
note: '',
|
||||
notifying: false,
|
||||
requested: false,
|
||||
showing_reblogs: true,
|
||||
subscribing: false,
|
||||
},
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
|
||||
import reducer from '../status_lists';
|
||||
|
||||
describe('status_lists reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(ImmutableMap({
|
||||
favourites: ImmutableMap({
|
||||
next: null,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet(),
|
||||
}),
|
||||
bookmarks: ImmutableMap({
|
||||
next: null,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet(),
|
||||
}),
|
||||
pins: ImmutableMap({
|
||||
next: null,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet(),
|
||||
}),
|
||||
scheduled_statuses: ImmutableMap({
|
||||
next: null,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet(),
|
||||
}),
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,32 @@
|
|||
import reducer from '../status_lists';
|
||||
|
||||
describe('status_lists reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {} as any).toJS()).toEqual({
|
||||
favourites: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
bookmarks: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
pins: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
scheduled_statuses: {
|
||||
next: null,
|
||||
loaded: false,
|
||||
isLoading: null,
|
||||
items: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,40 +0,0 @@
|
|||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
|
||||
import { SUGGESTIONS_DISMISS } from 'soapbox/actions/suggestions';
|
||||
|
||||
import reducer from '../suggestions';
|
||||
|
||||
describe('suggestions reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {})).toEqual(ImmutableMap({
|
||||
items: ImmutableOrderedSet(),
|
||||
next: null,
|
||||
isLoading: false,
|
||||
}));
|
||||
});
|
||||
|
||||
describe('SUGGESTIONS_DISMISS', () => {
|
||||
it('should remove the account', () => {
|
||||
const action = { type: SUGGESTIONS_DISMISS, id: '123' };
|
||||
|
||||
const state = fromJS({
|
||||
items: [
|
||||
{ account: '123', source: 'past_interactions' },
|
||||
{ account: '456', source: 'past_interactions' },
|
||||
{ account: '789', source: 'past_interactions' },
|
||||
],
|
||||
isLoading: false,
|
||||
});
|
||||
|
||||
const expected = fromJS({
|
||||
items: [
|
||||
{ account: '456', source: 'past_interactions' },
|
||||
{ account: '789', source: 'past_interactions' },
|
||||
],
|
||||
isLoading: false,
|
||||
});
|
||||
|
||||
expect(reducer(state, action)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
import { SUGGESTIONS_FETCH_SUCCESS, SUGGESTIONS_DISMISS } from 'soapbox/actions/suggestions';
|
||||
|
||||
import reducer from '../suggestions';
|
||||
|
||||
describe('suggestions reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {} as any).toJS()).toEqual({
|
||||
items: [],
|
||||
next: null,
|
||||
isLoading: false,
|
||||
});
|
||||
});
|
||||
|
||||
describe('SUGGESTIONS_DISMISS', () => {
|
||||
it('should remove the account', () => {
|
||||
let state = reducer(undefined, {} as any);
|
||||
|
||||
state = reducer(state, {
|
||||
type: SUGGESTIONS_FETCH_SUCCESS,
|
||||
accounts: [
|
||||
{ id: '123' },
|
||||
{ id: '456' },
|
||||
{ id: '789' },
|
||||
],
|
||||
});
|
||||
|
||||
const action = { type: SUGGESTIONS_DISMISS, id: '123' };
|
||||
|
||||
const expected = {
|
||||
items: [
|
||||
{ account: '456', source: 'past_interactions' },
|
||||
{ account: '789', source: 'past_interactions' },
|
||||
],
|
||||
isLoading: false,
|
||||
next: null,
|
||||
};
|
||||
|
||||
expect(reducer(state, action).toJS()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -27,9 +27,9 @@ describe('timelines reducer', () => {
|
|||
|
||||
describe('TIMELINE_EXPAND_FAIL', () => {
|
||||
it('sets loading to false', () => {
|
||||
const state = fromJS({
|
||||
const state = ImmutableMap(fromJS({
|
||||
home: { isLoading: true },
|
||||
});
|
||||
}));
|
||||
|
||||
const action = {
|
||||
type: TIMELINE_EXPAND_FAIL,
|
||||
|
@ -43,9 +43,9 @@ describe('timelines reducer', () => {
|
|||
|
||||
describe('TIMELINE_EXPAND_SUCCESS', () => {
|
||||
it('sets loading to false', () => {
|
||||
const state = fromJS({
|
||||
const state = ImmutableMap(fromJS({
|
||||
home: { isLoading: true },
|
||||
});
|
||||
}));
|
||||
|
||||
const action = {
|
||||
type: TIMELINE_EXPAND_SUCCESS,
|
||||
|
@ -70,9 +70,9 @@ describe('timelines reducer', () => {
|
|||
});
|
||||
|
||||
it('merges new status IDs', () => {
|
||||
const state = fromJS({
|
||||
const state = ImmutableMap(fromJS({
|
||||
home: { items: ImmutableOrderedSet(['5', '2', '1']) },
|
||||
});
|
||||
}));
|
||||
|
||||
const expected = ImmutableOrderedSet(['6', '5', '4', '2', '1']);
|
||||
|
||||
|
@ -87,9 +87,9 @@ describe('timelines reducer', () => {
|
|||
});
|
||||
|
||||
it('merges old status IDs', () => {
|
||||
const state = fromJS({
|
||||
const state = ImmutableMap(fromJS({
|
||||
home: { items: ImmutableOrderedSet(['6', '4', '3']) },
|
||||
});
|
||||
}));
|
||||
|
||||
const expected = ImmutableOrderedSet(['6', '4', '3', '5', '2', '1']);
|
||||
|
||||
|
@ -104,9 +104,9 @@ describe('timelines reducer', () => {
|
|||
});
|
||||
|
||||
it('overrides pinned post IDs', () => {
|
||||
const state = fromJS({
|
||||
const state = ImmutableMap(fromJS({
|
||||
'account:1:pinned': { items: ImmutableOrderedSet(['5', '2', '1']) },
|
||||
});
|
||||
}));
|
||||
|
||||
const expected = ImmutableOrderedSet(['9', '8', '7']);
|
||||
|
|
@ -30,15 +30,14 @@ import {
|
|||
ADMIN_USERS_UNSUGGEST_FAIL,
|
||||
} from 'soapbox/actions/admin';
|
||||
import { CHATS_FETCH_SUCCESS, CHATS_EXPAND_SUCCESS, CHAT_FETCH_SUCCESS } from 'soapbox/actions/chats';
|
||||
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
|
||||
import { normalizeAccount } from 'soapbox/normalizers/account';
|
||||
import { normalizeId } from 'soapbox/utils/normalizers';
|
||||
|
||||
import {
|
||||
ACCOUNT_IMPORT,
|
||||
ACCOUNTS_IMPORT,
|
||||
ACCOUNT_FETCH_FAIL_FOR_USERNAME_LOOKUP,
|
||||
} from '../actions/importer';
|
||||
} from 'soapbox/actions/importer';
|
||||
import { STREAMING_CHAT_UPDATE } from 'soapbox/actions/streaming';
|
||||
import { normalizeAccount } from 'soapbox/normalizers/account';
|
||||
import { normalizeId } from 'soapbox/utils/normalizers';
|
||||
|
||||
type AccountRecord = ReturnType<typeof normalizeAccount>;
|
||||
type AccountMap = ImmutableMap<string, any>;
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
||||
|
||||
import {
|
||||
ACCOUNT_FOLLOW_SUCCESS,
|
||||
ACCOUNT_UNFOLLOW_SUCCESS,
|
||||
} from '../actions/accounts';
|
||||
import { ACCOUNT_IMPORT, ACCOUNTS_IMPORT } from '../actions/importer';
|
||||
|
||||
const normalizeAccount = (state, account) => state.set(account.id, fromJS({
|
||||
followers_count: account.followers_count,
|
||||
following_count: account.following_count,
|
||||
statuses_count: account.statuses_count,
|
||||
}));
|
||||
|
||||
const normalizeAccounts = (state, accounts) => {
|
||||
accounts.forEach(account => {
|
||||
state = normalizeAccount(state, account);
|
||||
});
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
const updateFollowCounters = (state, counterUpdates) => {
|
||||
return state.withMutations(state => {
|
||||
counterUpdates.forEach(counterUpdate => {
|
||||
state.update(counterUpdate.id, ImmutableMap(), counters => counters.merge({
|
||||
followers_count: counterUpdate.follower_count,
|
||||
following_count: counterUpdate.following_count,
|
||||
}));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function accountsCounters(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case ACCOUNT_IMPORT:
|
||||
return normalizeAccount(state, action.account);
|
||||
case ACCOUNTS_IMPORT:
|
||||
return normalizeAccounts(state, action.accounts);
|
||||
case ACCOUNT_FOLLOW_SUCCESS:
|
||||
return action.alreadyFollowing ? state :
|
||||
state.updateIn([action.relationship.id, 'followers_count'], num => num + 1);
|
||||
case ACCOUNT_UNFOLLOW_SUCCESS:
|
||||
return state.updateIn([action.relationship.id, 'followers_count'], num => Math.max(0, num - 1));
|
||||
case STREAMING_FOLLOW_RELATIONSHIPS_UPDATE:
|
||||
return updateFollowCounters(state, [action.follower, action.following]);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
import { List as ImmutableList, Map as ImmutableMap, Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
ACCOUNT_FOLLOW_SUCCESS,
|
||||
ACCOUNT_UNFOLLOW_SUCCESS,
|
||||
} from 'soapbox/actions/accounts';
|
||||
import { ACCOUNT_IMPORT, ACCOUNTS_IMPORT } from 'soapbox/actions/importer';
|
||||
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const CounterRecord = ImmutableRecord({
|
||||
followers_count: 0,
|
||||
following_count: 0,
|
||||
statuses_count: 0,
|
||||
});
|
||||
|
||||
type Counter = ReturnType<typeof CounterRecord>;
|
||||
type State = ImmutableMap<string, Counter>;
|
||||
type APIEntity = Record<string, any>;
|
||||
type APIEntities = Array<APIEntity>;
|
||||
|
||||
const normalizeAccount = (state: State, account: APIEntity) => state.set(account.id, CounterRecord({
|
||||
followers_count: account.followers_count,
|
||||
following_count: account.following_count,
|
||||
statuses_count: account.statuses_count,
|
||||
}));
|
||||
|
||||
const normalizeAccounts = (state: State, accounts: ImmutableList<APIEntities>) => {
|
||||
accounts.forEach(account => {
|
||||
state = normalizeAccount(state, account);
|
||||
});
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
const updateFollowCounters = (state: State, counterUpdates: APIEntities) => {
|
||||
return state.withMutations(state => {
|
||||
counterUpdates.forEach((counterUpdate) => {
|
||||
state.update(counterUpdate.id, CounterRecord(), counters => counters.merge({
|
||||
followers_count: counterUpdate.follower_count,
|
||||
following_count: counterUpdate.following_count,
|
||||
}));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default function accountsCounters(state: State = ImmutableMap<string, Counter>(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case ACCOUNT_IMPORT:
|
||||
return normalizeAccount(state, action.account);
|
||||
case ACCOUNTS_IMPORT:
|
||||
return normalizeAccounts(state, action.accounts);
|
||||
case ACCOUNT_FOLLOW_SUCCESS:
|
||||
return action.alreadyFollowing ? state :
|
||||
state.updateIn([action.relationship.id, 'followers_count'], 0, (count) => typeof count === 'number' ? count + 1 : 0);
|
||||
case ACCOUNT_UNFOLLOW_SUCCESS:
|
||||
return state.updateIn([action.relationship.id, 'followers_count'], 0, (count) => typeof count === 'number' ? Math.max(0, count - 1) : 0);
|
||||
case STREAMING_FOLLOW_RELATIONSHIPS_UPDATE:
|
||||
return updateFollowCounters(state, [action.follower, action.following]);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Accounts Meta: private user data only the owner should see.
|
||||
* @module soapbox/reducers/accounts_meta
|
||||
*/
|
||||
|
||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import { VERIFY_CREDENTIALS_SUCCESS, AUTH_ACCOUNT_REMEMBER_SUCCESS } from 'soapbox/actions/auth';
|
||||
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'soapbox/actions/me';
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
const importAccount = (state, account) => {
|
||||
const accountId = account.get('id');
|
||||
|
||||
return state.set(accountId, ImmutableMap({
|
||||
pleroma: account.get('pleroma', ImmutableMap()).delete('settings_store'),
|
||||
source: account.get('source', ImmutableMap()),
|
||||
}));
|
||||
};
|
||||
|
||||
export default function accounts_meta(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case ME_FETCH_SUCCESS:
|
||||
case ME_PATCH_SUCCESS:
|
||||
return importAccount(state, fromJS(action.me));
|
||||
case VERIFY_CREDENTIALS_SUCCESS:
|
||||
case AUTH_ACCOUNT_REMEMBER_SUCCESS:
|
||||
return importAccount(state, fromJS(action.account));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Accounts Meta: private user data only the owner should see.
|
||||
* @module soapbox/reducers/accounts_meta
|
||||
*/
|
||||
|
||||
import { Map as ImmutableMap, Record as ImmutableRecord, fromJS } from 'immutable';
|
||||
|
||||
import { VERIFY_CREDENTIALS_SUCCESS, AUTH_ACCOUNT_REMEMBER_SUCCESS } from 'soapbox/actions/auth';
|
||||
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS } from 'soapbox/actions/me';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const MetaRecord = ImmutableRecord({
|
||||
pleroma: ImmutableMap<string, any>(),
|
||||
source: ImmutableMap<string, any>(),
|
||||
});
|
||||
|
||||
type Meta = ReturnType<typeof MetaRecord>;
|
||||
type State = ImmutableMap<string, Meta>;
|
||||
|
||||
const importAccount = (state: State, account: ImmutableMap<string, any>) => {
|
||||
const accountId = account.get('id');
|
||||
|
||||
return state.set(accountId, MetaRecord({
|
||||
pleroma: account.get('pleroma', ImmutableMap()).delete('settings_store'),
|
||||
source: account.get('source', ImmutableMap()),
|
||||
}));
|
||||
};
|
||||
|
||||
export default function accounts_meta(state: State = ImmutableMap<string, Meta>(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case ME_FETCH_SUCCESS:
|
||||
case ME_PATCH_SUCCESS:
|
||||
return importAccount(state, ImmutableMap(fromJS(action.me)));
|
||||
case VERIFY_CREDENTIALS_SUCCESS:
|
||||
case AUTH_ACCOUNT_REMEMBER_SUCCESS:
|
||||
return importAccount(state, ImmutableMap(fromJS(action.account)));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -2,40 +2,53 @@ import {
|
|||
Map as ImmutableMap,
|
||||
Record as ImmutableRecord,
|
||||
OrderedSet as ImmutableOrderedSet,
|
||||
fromJS,
|
||||
} from 'immutable';
|
||||
|
||||
import { ADMIN_LOG_FETCH_SUCCESS } from 'soapbox/actions/admin';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const LogEntryRecord = ImmutableRecord({
|
||||
data: ImmutableMap<string, any>(),
|
||||
id: 0,
|
||||
message: '',
|
||||
time: 0,
|
||||
});
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
items: ImmutableMap(),
|
||||
index: ImmutableOrderedSet(),
|
||||
items: ImmutableMap<string, LogEntry>(),
|
||||
index: ImmutableOrderedSet<number>(),
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const parseItems = items => {
|
||||
const ids = [];
|
||||
const map = {};
|
||||
type LogEntry = ReturnType<typeof LogEntryRecord>;
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
type APIEntity = Record<string, any>;
|
||||
type APIEntities = Array<APIEntity>;
|
||||
|
||||
const parseItems = (items: APIEntities) => {
|
||||
const ids: Array<number> = [];
|
||||
const map: Record<string, LogEntry> = {};
|
||||
|
||||
items.forEach(item => {
|
||||
ids.push(item.id);
|
||||
map[item.id] = item;
|
||||
map[item.id] = LogEntryRecord(item);
|
||||
});
|
||||
|
||||
return { ids: ids, map: map };
|
||||
};
|
||||
|
||||
const importItems = (state, items, total) => {
|
||||
const importItems = (state: State, items: APIEntities, total: number) => {
|
||||
const { ids, map } = parseItems(items);
|
||||
|
||||
return state.withMutations(state => {
|
||||
state.update('index', v => v.union(ids));
|
||||
state.update('items', v => v.merge(fromJS(map)));
|
||||
state.update('items', v => v.merge(map));
|
||||
state.set('total', total);
|
||||
});
|
||||
};
|
||||
|
||||
export default function admin_log(state = ReducerRecord(), action) {
|
||||
export default function admin_log(state = ReducerRecord(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case ADMIN_LOG_FETCH_SUCCESS:
|
||||
return importItems(state, action.items, action.total);
|
|
@ -1,4 +1,4 @@
|
|||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
ALIASES_SUGGESTIONS_READY,
|
||||
|
@ -7,19 +7,23 @@ import {
|
|||
ALIASES_FETCH_SUCCESS,
|
||||
} from '../actions/aliases';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
aliases: ImmutableMap({
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
aliases: ImmutableRecord({
|
||||
items: ImmutableList<string>(),
|
||||
loaded: false,
|
||||
items: ImmutableList(),
|
||||
}),
|
||||
suggestions: ImmutableMap({
|
||||
})(),
|
||||
suggestions: ImmutableRecord({
|
||||
items: ImmutableList<string>(),
|
||||
value: '',
|
||||
loaded: false,
|
||||
items: ImmutableList(),
|
||||
}),
|
||||
})(),
|
||||
});
|
||||
|
||||
export default function aliasesReducer(state = initialState, action) {
|
||||
export default function aliasesReducer(state = ReducerRecord(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case ALIASES_FETCH_SUCCESS:
|
||||
return state
|
||||
|
@ -30,7 +34,7 @@ export default function aliasesReducer(state = initialState, action) {
|
|||
.setIn(['suggestions', 'loaded'], false);
|
||||
case ALIASES_SUGGESTIONS_READY:
|
||||
return state
|
||||
.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map(item => item.id)))
|
||||
.setIn(['suggestions', 'items'], ImmutableList(action.accounts.map((item: APIEntity) => item.id)))
|
||||
.setIn(['suggestions', 'loaded'], true);
|
||||
case ALIASES_SUGGESTIONS_CLEAR:
|
||||
return state.update('suggestions', suggestions => suggestions.withMutations(map => {
|
|
@ -1,26 +0,0 @@
|
|||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
|
||||
import {
|
||||
DOMAIN_BLOCKS_FETCH_SUCCESS,
|
||||
DOMAIN_BLOCKS_EXPAND_SUCCESS,
|
||||
DOMAIN_UNBLOCK_SUCCESS,
|
||||
} from '../actions/domain_blocks';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
blocks: ImmutableMap({
|
||||
items: ImmutableOrderedSet(),
|
||||
}),
|
||||
});
|
||||
|
||||
export default function domainLists(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case DOMAIN_BLOCKS_FETCH_SUCCESS:
|
||||
return state.setIn(['blocks', 'items'], ImmutableOrderedSet(action.domains)).setIn(['blocks', 'next'], action.next);
|
||||
case DOMAIN_BLOCKS_EXPAND_SUCCESS:
|
||||
return state.updateIn(['blocks', 'items'], set => set.union(action.domains)).setIn(['blocks', 'next'], action.next);
|
||||
case DOMAIN_UNBLOCK_SUCCESS:
|
||||
return state.updateIn(['blocks', 'items'], set => set.delete(action.domain));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
DOMAIN_BLOCKS_FETCH_SUCCESS,
|
||||
DOMAIN_BLOCKS_EXPAND_SUCCESS,
|
||||
DOMAIN_UNBLOCK_SUCCESS,
|
||||
} from '../actions/domain_blocks';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const BlocksRecord = ImmutableRecord({
|
||||
items: ImmutableOrderedSet<string>(),
|
||||
next: null as string | null,
|
||||
});
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
blocks: BlocksRecord(),
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
export default function domainLists(state: State = ReducerRecord(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case DOMAIN_BLOCKS_FETCH_SUCCESS:
|
||||
return state.setIn(['blocks', 'items'], ImmutableOrderedSet(action.domains)).setIn(['blocks', 'next'], action.next);
|
||||
case DOMAIN_BLOCKS_EXPAND_SUCCESS:
|
||||
return state.updateIn(['blocks', 'items'], set => (set as ImmutableOrderedSet<string>).union(action.domains)).setIn(['blocks', 'next'], action.next);
|
||||
case DOMAIN_UNBLOCK_SUCCESS:
|
||||
return state.updateIn(['blocks', 'items'], set => (set as ImmutableOrderedSet<string>).delete(action.domain));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import { Map as ImmutableMap } from 'immutable';
|
||||
|
||||
import {
|
||||
DROPDOWN_MENU_OPEN,
|
||||
DROPDOWN_MENU_CLOSE,
|
||||
} from '../actions/dropdown_menu';
|
||||
|
||||
const initialState = ImmutableMap({ openId: null, placement: null, keyboard: false });
|
||||
|
||||
export default function dropdownMenu(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case DROPDOWN_MENU_OPEN:
|
||||
return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard });
|
||||
case DROPDOWN_MENU_CLOSE:
|
||||
return state.get('openId') === action.id ? state.set('openId', null) : state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
DROPDOWN_MENU_OPEN,
|
||||
DROPDOWN_MENU_CLOSE,
|
||||
} from '../actions/dropdown_menu';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { DropdownPlacement } from 'soapbox/components/dropdown_menu';
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
openId: null as number | null,
|
||||
placement: null as any as DropdownPlacement,
|
||||
keyboard: false,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
export default function dropdownMenu(state: State = ReducerRecord(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case DROPDOWN_MENU_OPEN:
|
||||
return state.merge({ openId: action.id, placement: action.placement, keyboard: action.keyboard });
|
||||
case DROPDOWN_MENU_CLOSE:
|
||||
return state.openId === action.id ? state.set('openId', null) : state;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||
import { List as ImmutableList, Map as ImmutableMap } from 'immutable';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { STREAMING_FOLLOW_RELATIONSHIPS_UPDATE } from 'soapbox/actions/streaming';
|
||||
import { normalizeRelationship } from 'soapbox/normalizers/relationship';
|
||||
|
||||
import { ACCOUNT_NOTE_SUBMIT_SUCCESS } from '../actions/account-notes';
|
||||
import {
|
||||
|
@ -31,17 +32,22 @@ import {
|
|||
ACCOUNTS_IMPORT,
|
||||
} from '../actions/importer';
|
||||
|
||||
const normalizeRelationship = (state, relationship) => state.set(relationship.id, fromJS(relationship));
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const normalizeRelationships = (state, relationships) => {
|
||||
type Relationship = ReturnType<typeof normalizeRelationship>;
|
||||
type State = ImmutableMap<string, Relationship>;
|
||||
type APIEntity = Record<string, any>;
|
||||
type APIEntities = Array<APIEntity>;
|
||||
|
||||
const normalizeRelationships = (state: State, relationships: APIEntities) => {
|
||||
relationships.forEach(relationship => {
|
||||
state = normalizeRelationship(state, relationship);
|
||||
state = state.set(relationship.id, normalizeRelationship(relationship));
|
||||
});
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
const setDomainBlocking = (state, accounts, blocking) => {
|
||||
const setDomainBlocking = (state: State, accounts: ImmutableList<string>, blocking: boolean) => {
|
||||
return state.withMutations(map => {
|
||||
accounts.forEach(id => {
|
||||
map.setIn([id, 'domain_blocking'], blocking);
|
||||
|
@ -49,14 +55,14 @@ const setDomainBlocking = (state, accounts, blocking) => {
|
|||
});
|
||||
};
|
||||
|
||||
const importPleromaAccount = (state, account) => {
|
||||
const importPleromaAccount = (state: State, account: APIEntity) => {
|
||||
const relationship = get(account, ['pleroma', 'relationship'], {});
|
||||
if (relationship.id && relationship !== {})
|
||||
return normalizeRelationship(state, relationship);
|
||||
return normalizeRelationships(state, [relationship]);
|
||||
return state;
|
||||
};
|
||||
|
||||
const importPleromaAccounts = (state, accounts) => {
|
||||
const importPleromaAccounts = (state: State, accounts: APIEntities) => {
|
||||
accounts.forEach(account => {
|
||||
state = importPleromaAccount(state, account);
|
||||
});
|
||||
|
@ -64,7 +70,7 @@ const importPleromaAccounts = (state, accounts) => {
|
|||
return state;
|
||||
};
|
||||
|
||||
const followStateToRelationship = followState => {
|
||||
const followStateToRelationship = (followState: string) => {
|
||||
switch (followState) {
|
||||
case 'follow_pending':
|
||||
return { following: false, requested: true };
|
||||
|
@ -77,14 +83,12 @@ const followStateToRelationship = followState => {
|
|||
}
|
||||
};
|
||||
|
||||
const updateFollowRelationship = (state, id, followState) => {
|
||||
const updateFollowRelationship = (state: State, id: string, followState: string) => {
|
||||
const map = followStateToRelationship(followState);
|
||||
return state.update(id, ImmutableMap(), relationship => relationship.merge(map));
|
||||
return state.update(id, normalizeRelationship({}), relationship => relationship.merge(map));
|
||||
};
|
||||
|
||||
const initialState = ImmutableMap();
|
||||
|
||||
export default function relationships(state = initialState, action) {
|
||||
export default function relationships(state: State = ImmutableMap<string, Relationship>(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case ACCOUNT_IMPORT:
|
||||
return importPleromaAccount(state, action.account);
|
||||
|
@ -110,7 +114,7 @@ export default function relationships(state = initialState, action) {
|
|||
case ACCOUNT_UNPIN_SUCCESS:
|
||||
case ACCOUNT_NOTE_SUBMIT_SUCCESS:
|
||||
case ACCOUNT_REMOVE_FROM_FOLLOWERS_SUCCESS:
|
||||
return normalizeRelationship(state, action.relationship);
|
||||
return normalizeRelationships(state, [action.relationship]);
|
||||
case RELATIONSHIPS_FETCH_SUCCESS:
|
||||
return normalizeRelationships(state, action.relationships);
|
||||
case DOMAIN_BLOCK_SUCCESS:
|
|
@ -1,4 +1,8 @@
|
|||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';
|
||||
import {
|
||||
Map as ImmutableMap,
|
||||
OrderedSet as ImmutableOrderedSet,
|
||||
Record as ImmutableRecord,
|
||||
} from 'immutable';
|
||||
|
||||
import {
|
||||
BOOKMARKED_STATUSES_FETCH_REQUEST,
|
||||
|
@ -44,29 +48,38 @@ import {
|
|||
SCHEDULED_STATUS_CANCEL_SUCCESS,
|
||||
} from '../actions/scheduled_statuses';
|
||||
|
||||
const initialMap = ImmutableMap({
|
||||
next: null,
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
||||
|
||||
const StatusListRecord = ImmutableRecord({
|
||||
next: null as string | null,
|
||||
loaded: false,
|
||||
items: ImmutableOrderedSet(),
|
||||
isLoading: null as boolean | null,
|
||||
items: ImmutableOrderedSet<string>(),
|
||||
});
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
favourites: initialMap,
|
||||
bookmarks: initialMap,
|
||||
pins: initialMap,
|
||||
scheduled_statuses: initialMap,
|
||||
type State = ImmutableMap<string, StatusList>;
|
||||
type StatusList = ReturnType<typeof StatusListRecord>;
|
||||
type Status = string | StatusEntity;
|
||||
type Statuses = Array<string | StatusEntity>;
|
||||
|
||||
const initialState: State = ImmutableMap({
|
||||
favourites: StatusListRecord(),
|
||||
bookmarks: StatusListRecord(),
|
||||
pins: StatusListRecord(),
|
||||
scheduled_statuses: StatusListRecord(),
|
||||
});
|
||||
|
||||
const getStatusId = status => typeof status === 'string' ? status : status.get('id');
|
||||
const getStatusId = (status: string | StatusEntity) => typeof status === 'string' ? status : status.id;
|
||||
|
||||
const getStatusIds = (statuses = []) => (
|
||||
ImmutableOrderedSet(statuses.map(status => status.id))
|
||||
const getStatusIds = (statuses: Statuses = []) => (
|
||||
ImmutableOrderedSet(statuses.map(getStatusId))
|
||||
);
|
||||
|
||||
const setLoading = (state, listType, loading) => state.setIn([listType, 'isLoading'], loading);
|
||||
const setLoading = (state: State, listType: string, loading: boolean) => state.setIn([listType, 'isLoading'], loading);
|
||||
|
||||
const normalizeList = (state, listType, statuses, next) => {
|
||||
return state.update(listType, initialMap, listMap => listMap.withMutations(map => {
|
||||
const normalizeList = (state: State, listType: string, statuses: Statuses, next: string | null) => {
|
||||
return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
||||
map.set('next', next);
|
||||
map.set('loaded', true);
|
||||
map.set('isLoading', false);
|
||||
|
@ -74,29 +87,29 @@ const normalizeList = (state, listType, statuses, next) => {
|
|||
}));
|
||||
};
|
||||
|
||||
const appendToList = (state, listType, statuses, next) => {
|
||||
const appendToList = (state: State, listType: string, statuses: Statuses, next: string | null) => {
|
||||
const newIds = getStatusIds(statuses);
|
||||
|
||||
return state.update(listType, initialMap, listMap => listMap.withMutations(map => {
|
||||
return state.update(listType, StatusListRecord(), listMap => listMap.withMutations(map => {
|
||||
map.set('next', next);
|
||||
map.set('isLoading', false);
|
||||
map.update('items', ImmutableOrderedSet(), items => items.union(newIds));
|
||||
map.update('items', items => items.union(newIds));
|
||||
}));
|
||||
};
|
||||
|
||||
const prependOneToList = (state, listType, status) => {
|
||||
const prependOneToList = (state: State, listType: string, status: Status) => {
|
||||
const statusId = getStatusId(status);
|
||||
return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => {
|
||||
return ImmutableOrderedSet([statusId]).union(items);
|
||||
return ImmutableOrderedSet([statusId]).union(items as ImmutableOrderedSet<string>);
|
||||
});
|
||||
};
|
||||
|
||||
const removeOneFromList = (state, listType, status) => {
|
||||
const removeOneFromList = (state: State, listType: string, status: Status) => {
|
||||
const statusId = getStatusId(status);
|
||||
return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => items.delete(statusId));
|
||||
return state.updateIn([listType, 'items'], ImmutableOrderedSet(), items => (items as ImmutableOrderedSet<string>).delete(statusId));
|
||||
};
|
||||
|
||||
export default function statusLists(state = initialState, action) {
|
||||
export default function statusLists(state = initialState, action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case FAVOURITED_STATUSES_FETCH_REQUEST:
|
||||
case FAVOURITED_STATUSES_EXPAND_REQUEST:
|
||||
|
@ -154,7 +167,7 @@ export default function statusLists(state = initialState, action) {
|
|||
return appendToList(state, 'scheduled_statuses', action.statuses, action.next);
|
||||
case SCHEDULED_STATUS_CANCEL_REQUEST:
|
||||
case SCHEDULED_STATUS_CANCEL_SUCCESS:
|
||||
return removeOneFromList(state, 'scheduled_statuses', action.id || action.status.get('id'));
|
||||
return removeOneFromList(state, 'scheduled_statuses', action.id || action.status.id);
|
||||
default:
|
||||
return state;
|
||||
}
|
|
@ -116,7 +116,7 @@ export const calculateStatus = (
|
|||
|
||||
// Check whether a status is a quote by secondary characteristics
|
||||
const isQuote = (status: StatusRecord) => {
|
||||
return Boolean(status.getIn(['pleroma', 'quote_url']));
|
||||
return Boolean(status.pleroma.get('quote_url'));
|
||||
};
|
||||
|
||||
// Preserve quote if an existing status already has it
|
||||
|
@ -124,7 +124,7 @@ const fixQuote = (status: StatusRecord, oldStatus?: StatusRecord): StatusRecord
|
|||
if (oldStatus && !status.quote && isQuote(status)) {
|
||||
return status
|
||||
.set('quote', oldStatus.quote)
|
||||
.updateIn(['pleroma', 'quote_visible'], visible => visible || oldStatus.getIn(['pleroma', 'quote_visible']));
|
||||
.updateIn(['pleroma', 'quote_visible'], visible => visible || oldStatus.pleroma.get('quote_visible'));
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
|
||||
import { OrderedSet as ImmutableOrderedSet, Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
|
||||
import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'soapbox/actions/accounts';
|
||||
import { DOMAIN_BLOCK_SUCCESS } from 'soapbox/actions/domain_blocks';
|
||||
|
||||
import {
|
||||
SUGGESTIONS_FETCH_REQUEST,
|
||||
SUGGESTIONS_FETCH_SUCCESS,
|
||||
|
@ -11,46 +11,58 @@ import {
|
|||
SUGGESTIONS_V2_FETCH_REQUEST,
|
||||
SUGGESTIONS_V2_FETCH_SUCCESS,
|
||||
SUGGESTIONS_V2_FETCH_FAIL,
|
||||
} from '../actions/suggestions';
|
||||
} from 'soapbox/actions/suggestions';
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
items: ImmutableOrderedSet(),
|
||||
next: null,
|
||||
import type { AnyAction } from 'redux';
|
||||
import type { APIEntity } from 'soapbox/types/entities';
|
||||
|
||||
const SuggestionRecord = ImmutableRecord({
|
||||
source: '',
|
||||
account: '',
|
||||
});
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
items: ImmutableOrderedSet<Suggestion>(),
|
||||
next: null as string | null,
|
||||
isLoading: false,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
type Suggestion = ReturnType<typeof SuggestionRecord>;
|
||||
type APIEntities = Array<APIEntity>;
|
||||
|
||||
// Convert a v1 account into a v2 suggestion
|
||||
const accountToSuggestion = account => {
|
||||
const accountToSuggestion = (account: APIEntity) => {
|
||||
return {
|
||||
source: 'past_interactions',
|
||||
account: account.id,
|
||||
};
|
||||
};
|
||||
|
||||
const importAccounts = (state, accounts) => {
|
||||
const importAccounts = (state: State, accounts: APIEntities) => {
|
||||
return state.withMutations(state => {
|
||||
state.set('items', fromJS(accounts.map(accountToSuggestion)));
|
||||
state.set('items', ImmutableOrderedSet(accounts.map(accountToSuggestion).map(suggestion => SuggestionRecord(suggestion))));
|
||||
state.set('isLoading', false);
|
||||
});
|
||||
};
|
||||
|
||||
const importSuggestions = (state, suggestions, next) => {
|
||||
const importSuggestions = (state: State, suggestions: APIEntities, next: string | null) => {
|
||||
return state.withMutations(state => {
|
||||
state.update('items', items => items.concat(fromJS(suggestions.map(x => ({ ...x, account: x.account.id })))));
|
||||
state.update('items', items => items.concat(suggestions.map(x => ({ ...x, account: x.account.id })).map(suggestion => SuggestionRecord(suggestion))));
|
||||
state.set('isLoading', false);
|
||||
state.set('next', next);
|
||||
});
|
||||
};
|
||||
|
||||
const dismissAccount = (state, accountId) => {
|
||||
return state.update('items', items => items.filterNot(item => item.get('account') === accountId));
|
||||
const dismissAccount = (state: State, accountId: string) => {
|
||||
return state.update('items', items => items.filterNot(item => item.account === accountId));
|
||||
};
|
||||
|
||||
const dismissAccounts = (state, accountIds) => {
|
||||
return state.update('items', items => items.filterNot(item => accountIds.includes(item.get('account'))));
|
||||
const dismissAccounts = (state: State, accountIds: Array<string>) => {
|
||||
return state.update('items', items => items.filterNot(item => accountIds.includes(item.account)));
|
||||
};
|
||||
|
||||
export default function suggestionsReducer(state = initialState, action) {
|
||||
export default function suggestionsReducer(state: State = ReducerRecord(), action: AnyAction) {
|
||||
switch (action.type) {
|
||||
case SUGGESTIONS_FETCH_REQUEST:
|
||||
case SUGGESTIONS_V2_FETCH_REQUEST:
|
|
@ -22,7 +22,7 @@ const getAccountBase = (state: RootState, id: string) => state.accounts.
|
|||
const getAccountCounters = (state: RootState, id: string) => state.accounts_counters.get(id);
|
||||
const getAccountRelationship = (state: RootState, id: string) => state.relationships.get(id);
|
||||
const getAccountMoved = (state: RootState, id: string) => state.accounts.get(state.accounts.get(id)?.moved || '');
|
||||
const getAccountMeta = (state: RootState, id: string) => state.accounts_meta.get(id, ImmutableMap());
|
||||
const getAccountMeta = (state: RootState, id: string) => state.accounts_meta.get(id);
|
||||
const getAccountAdminData = (state: RootState, id: string) => state.admin.users.get(id);
|
||||
const getAccountPatron = (state: RootState, id: string) => {
|
||||
const url = state.accounts.get(id)?.url;
|
||||
|
@ -42,10 +42,12 @@ export const makeGetAccount = () => {
|
|||
if (!base) return null;
|
||||
|
||||
return base.withMutations(map => {
|
||||
map.merge(counters);
|
||||
map.merge(meta);
|
||||
map.set('pleroma', meta.get('pleroma', ImmutableMap()).merge(base.get('pleroma', ImmutableMap()))); // Lol, thanks Pleroma
|
||||
map.set('relationship', relationship);
|
||||
if (counters) map.merge(counters);
|
||||
if (meta) {
|
||||
map.merge(meta);
|
||||
map.set('pleroma', meta.pleroma.merge(base.get('pleroma', ImmutableMap()))); // Lol, thanks Pleroma
|
||||
}
|
||||
if (relationship) map.set('relationship', relationship);
|
||||
map.set('moved', moved || null);
|
||||
map.set('patron', patron || null);
|
||||
map.setIn(['pleroma', 'admin'], admin);
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
NotificationRecord,
|
||||
PollRecord,
|
||||
PollOptionRecord,
|
||||
RelationshipRecord,
|
||||
StatusEditRecord,
|
||||
StatusRecord,
|
||||
} from 'soapbox/normalizers';
|
||||
|
@ -34,6 +35,7 @@ type Mention = ReturnType<typeof MentionRecord>;
|
|||
type Notification = ReturnType<typeof NotificationRecord>;
|
||||
type Poll = ReturnType<typeof PollRecord>;
|
||||
type PollOption = ReturnType<typeof PollOptionRecord>;
|
||||
type Relationship = ReturnType<typeof RelationshipRecord>;
|
||||
type StatusEdit = ReturnType<typeof StatusEditRecord>;
|
||||
|
||||
interface Account extends ReturnType<typeof AccountRecord> {
|
||||
|
@ -68,6 +70,7 @@ export {
|
|||
Notification,
|
||||
Poll,
|
||||
PollOption,
|
||||
Relationship,
|
||||
Status,
|
||||
StatusEdit,
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { fromJS } from 'immutable';
|
||||
import { List as ImmutableList, Map as ImmutableMap, fromJS } from 'immutable';
|
||||
|
||||
import { normalizeStatus } from 'soapbox/normalizers';
|
||||
|
||||
import {
|
||||
sortEmoji,
|
||||
|
@ -11,7 +13,7 @@ import {
|
|||
simulateUnEmojiReact,
|
||||
} from '../emoji_reacts';
|
||||
|
||||
const ALLOWED_EMOJI = fromJS([
|
||||
const ALLOWED_EMOJI = ImmutableList([
|
||||
'👍',
|
||||
'❤',
|
||||
'😂',
|
||||
|
@ -30,7 +32,7 @@ describe('filterEmoji', () => {
|
|||
{ 'count': 1, 'me': true, 'name': '😡' },
|
||||
{ 'count': 1, 'me': true, 'name': '🔪' },
|
||||
{ 'count': 1, 'me': true, 'name': '😠' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
it('filters only allowed emoji', () => {
|
||||
expect(filterEmoji(emojiReacts, ALLOWED_EMOJI)).toEqual(fromJS([
|
||||
{ 'count': 1, 'me': true, 'name': '😂' },
|
||||
|
@ -49,7 +51,7 @@ describe('sortEmoji', () => {
|
|||
{ 'count': 20, 'me': true, 'name': '👍' },
|
||||
{ 'count': 7, 'me': true, 'name': '😂' },
|
||||
{ 'count': 15, 'me': true, 'name': '❤' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
it('sorts the emoji by count', () => {
|
||||
expect(sortEmoji(emojiReacts)).toEqual(fromJS([
|
||||
{ 'count': 20, 'me': true, 'name': '👍' },
|
||||
|
@ -72,7 +74,7 @@ describe('mergeEmojiFavourites', () => {
|
|||
{ 'count': 20, 'me': false, 'name': '👍' },
|
||||
{ 'count': 15, 'me': false, 'name': '❤' },
|
||||
{ 'count': 7, 'me': false, 'name': '😯' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
it('combines 👍 reacts with favourites', () => {
|
||||
expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([
|
||||
{ 'count': 32, 'me': true, 'name': '👍' },
|
||||
|
@ -86,7 +88,7 @@ describe('mergeEmojiFavourites', () => {
|
|||
const emojiReacts = fromJS([
|
||||
{ 'count': 15, 'me': false, 'name': '❤' },
|
||||
{ 'count': 7, 'me': false, 'name': '😯' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
it('adds 👍 reacts to the map equaling favourite count', () => {
|
||||
expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([
|
||||
{ 'count': 15, 'me': false, 'name': '❤' },
|
||||
|
@ -116,7 +118,7 @@ describe('reduceEmoji', () => {
|
|||
{ 'count': 15, 'me': true, 'name': '❤' },
|
||||
{ 'count': 1, 'me': false, 'name': '👀' },
|
||||
{ 'count': 1, 'me': false, 'name': '🍩' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
it('sorts, filters, and combines emoji and favourites', () => {
|
||||
expect(reduceEmoji(emojiReacts, 7, true, ALLOWED_EMOJI)).toEqual(fromJS([
|
||||
{ 'count': 27, 'me': true, 'name': '👍' },
|
||||
|
@ -138,7 +140,7 @@ describe('oneEmojiPerAccount', () => {
|
|||
{ 'count': 2, 'me': true, 'name': '❤', accounts: [{ id: '1' }, { id: '2' }] },
|
||||
{ 'count': 1, 'me': true, 'name': '😯', accounts: [{ id: '1' }] },
|
||||
{ 'count': 1, 'me': false, 'name': '😂', accounts: [{ id: '3' }] },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
expect(oneEmojiPerAccount(emojiReacts, '1')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': true, 'name': '👍', accounts: [{ id: '1' }, { id: '2' }] },
|
||||
{ 'count': 1, 'me': false, 'name': '😂', accounts: [{ id: '3' }] },
|
||||
|
@ -148,7 +150,7 @@ describe('oneEmojiPerAccount', () => {
|
|||
|
||||
describe('getReactForStatus', () => {
|
||||
it('returns a single owned react (including favourite) for the status', () => {
|
||||
const status = fromJS({
|
||||
const status = normalizeStatus(fromJS({
|
||||
favourited: false,
|
||||
pleroma: {
|
||||
emoji_reactions: [
|
||||
|
@ -158,27 +160,27 @@ describe('getReactForStatus', () => {
|
|||
{ 'count': 7, 'me': false, 'name': '😂' },
|
||||
],
|
||||
},
|
||||
});
|
||||
}));
|
||||
expect(getReactForStatus(status, ALLOWED_EMOJI)).toEqual('❤');
|
||||
});
|
||||
|
||||
it('returns a thumbs-up for a favourite', () => {
|
||||
const status = fromJS({ favourites_count: 1, favourited: true });
|
||||
const status = normalizeStatus(fromJS({ favourites_count: 1, favourited: true }));
|
||||
expect(getReactForStatus(status)).toEqual('👍');
|
||||
});
|
||||
|
||||
it('returns undefined when a status has no reacts (or favourites)', () => {
|
||||
const status = fromJS({});
|
||||
const status = normalizeStatus(fromJS({}));
|
||||
expect(getReactForStatus(status)).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('returns undefined when a status has no valid reacts (or favourites)', () => {
|
||||
const status = fromJS([
|
||||
const status = normalizeStatus(fromJS([
|
||||
{ 'count': 1, 'me': true, 'name': '🔪' },
|
||||
{ 'count': 1, 'me': true, 'name': '🌵' },
|
||||
{ 'count': 1, 'me': false, 'name': '👀' },
|
||||
{ 'count': 1, 'me': false, 'name': '🍩' },
|
||||
]);
|
||||
]));
|
||||
expect(getReactForStatus(status)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
@ -188,7 +190,7 @@ describe('simulateEmojiReact', () => {
|
|||
const emojiReacts = fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
expect(simulateEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 3, 'me': true, 'name': '❤' },
|
||||
|
@ -199,7 +201,7 @@ describe('simulateEmojiReact', () => {
|
|||
const emojiReacts = fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
expect(simulateEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
|
@ -213,7 +215,7 @@ describe('simulateUnEmojiReact', () => {
|
|||
const emojiReacts = fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 3, 'me': true, 'name': '❤' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
expect(simulateUnEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
|
@ -225,7 +227,7 @@ describe('simulateUnEmojiReact', () => {
|
|||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
||||
{ 'count': 1, 'me': true, 'name': '😯' },
|
||||
]);
|
||||
]) as ImmutableList<ImmutableMap<string, any>>;
|
||||
expect(simulateUnEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
|
||||
{ 'count': 2, 'me': false, 'name': '👍' },
|
||||
{ 'count': 2, 'me': false, 'name': '❤' },
|
|
@ -1,71 +1,73 @@
|
|||
import { fromJS } from 'immutable';
|
||||
|
||||
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||
|
||||
import { shouldFilter } from '../timelines';
|
||||
|
||||
describe('shouldFilter', () => {
|
||||
it('returns false under normal circumstances', () => {
|
||||
const columnSettings = fromJS({});
|
||||
const status = fromJS({});
|
||||
const status = normalizeStatus(fromJS({}));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(false);
|
||||
});
|
||||
|
||||
it('reblog: returns true when `shows.reblog == false`', () => {
|
||||
const columnSettings = fromJS({ shows: { reblog: false } });
|
||||
const status = fromJS({ reblog: {} });
|
||||
const status = normalizeStatus(fromJS({ reblog: {} }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(true);
|
||||
});
|
||||
|
||||
it('reblog: returns false when `shows.reblog == true`', () => {
|
||||
const columnSettings = fromJS({ shows: { reblog: true } });
|
||||
const status = fromJS({ reblog: {} });
|
||||
const status = normalizeStatus(fromJS({ reblog: {} }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(false);
|
||||
});
|
||||
|
||||
it('reply: returns true when `shows.reply == false`', () => {
|
||||
const columnSettings = fromJS({ shows: { reply: false } });
|
||||
const status = fromJS({ in_reply_to_id: '1234' });
|
||||
const status = normalizeStatus(fromJS({ in_reply_to_id: '1234' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(true);
|
||||
});
|
||||
|
||||
it('reply: returns false when `shows.reply == true`', () => {
|
||||
const columnSettings = fromJS({ shows: { reply: true } });
|
||||
const status = fromJS({ in_reply_to_id: '1234' });
|
||||
const status = normalizeStatus(fromJS({ in_reply_to_id: '1234' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(false);
|
||||
});
|
||||
|
||||
it('direct: returns true when `shows.direct == false`', () => {
|
||||
const columnSettings = fromJS({ shows: { direct: false } });
|
||||
const status = fromJS({ visibility: 'direct' });
|
||||
const status = normalizeStatus(fromJS({ visibility: 'direct' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(true);
|
||||
});
|
||||
|
||||
it('direct: returns false when `shows.direct == true`', () => {
|
||||
const columnSettings = fromJS({ shows: { direct: true } });
|
||||
const status = fromJS({ visibility: 'direct' });
|
||||
const status = normalizeStatus(fromJS({ visibility: 'direct' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(false);
|
||||
});
|
||||
|
||||
it('direct: returns false for a public post when `shows.direct == false`', () => {
|
||||
const columnSettings = fromJS({ shows: { direct: false } });
|
||||
const status = fromJS({ visibility: 'public' });
|
||||
const status = normalizeStatus(fromJS({ visibility: 'public' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(false);
|
||||
});
|
||||
|
||||
it('multiple settings', () => {
|
||||
const columnSettings = fromJS({ shows: { reblog: false, reply: false, direct: false } });
|
||||
const status = fromJS({ reblog: null, in_reply_to_id: null, visibility: 'direct' });
|
||||
const status = normalizeStatus(fromJS({ reblog: null, in_reply_to_id: null, visibility: 'direct' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(true);
|
||||
});
|
||||
|
||||
it('multiple settings', () => {
|
||||
const columnSettings = fromJS({ shows: { reblog: false, reply: true, direct: false } });
|
||||
const status = fromJS({ reblog: null, in_reply_to_id: '1234', visibility: 'public' });
|
||||
const status = normalizeStatus(fromJS({ reblog: null, in_reply_to_id: '1234', visibility: 'public' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(false);
|
||||
});
|
||||
|
||||
it('multiple settings', () => {
|
||||
const columnSettings = fromJS({ shows: { reblog: true, reply: false, direct: true } });
|
||||
const status = fromJS({ reblog: {}, in_reply_to_id: '1234', visibility: 'direct' });
|
||||
const status = normalizeStatus(fromJS({ reblog: {}, in_reply_to_id: '1234', visibility: 'direct' }));
|
||||
expect(shouldFilter(status, columnSettings)).toBe(true);
|
||||
});
|
||||
});
|
|
@ -82,9 +82,9 @@ export const reduceEmoji = (emojiReacts: ImmutableList<EmojiReact>, favouritesCo
|
|||
|
||||
export const getReactForStatus = (status: any, allowedEmoji = ALLOWED_EMOJI): string | undefined => {
|
||||
const result = reduceEmoji(
|
||||
status.getIn(['pleroma', 'emoji_reactions'], ImmutableList()),
|
||||
status.get('favourites_count', 0),
|
||||
status.get('favourited'),
|
||||
status.pleroma.get('emoji_reactions', ImmutableList()),
|
||||
status.favourites_count || 0,
|
||||
status.favourited,
|
||||
allowedEmoji,
|
||||
).filter(e => e.get('me') === true)
|
||||
.getIn([0, 'name']);
|
||||
|
|
|
@ -4,9 +4,9 @@ import type { Status as StatusEntity } from 'soapbox/types/entities';
|
|||
|
||||
export const shouldFilter = (status: StatusEntity, columnSettings: any) => {
|
||||
const shows = ImmutableMap({
|
||||
reblog: status.get('reblog') !== null,
|
||||
reply: status.get('in_reply_to_id') !== null,
|
||||
direct: status.get('visibility') === 'direct',
|
||||
reblog: status.reblog !== null,
|
||||
reply: status.in_reply_to_id !== null,
|
||||
direct: status.visibility === 'direct',
|
||||
});
|
||||
|
||||
return shows.some((value, key) => {
|
||||
|
|
Loading…
Reference in New Issue