Typescript, convert some components to functional

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
merge-requests/1206/head
marcin mikołajczak 2022-04-11 21:58:48 +02:00
rodzic 267ab4b153
commit 2943b91034
43 zmienionych plików z 508 dodań i 630 usunięć

Wyświetl plik

@ -93,7 +93,7 @@ const isBroken = status => {
// https://gitlab.com/soapbox-pub/soapbox/-/issues/28
if (status.reblog && !status.reblog.account.id) return true;
return false;
} catch(e) {
} catch (e) {
return true;
}
};

Wyświetl plik

@ -121,7 +121,7 @@ export function updateNotificationsQueue(notification, intlMessages, intlLocale,
}).catch(console.error);
}).catch(console.error);
}
} catch(e) {
} catch (e) {
console.warn(e);
}

Wyświetl plik

@ -31,7 +31,7 @@ const getToken = (state: RootState, authType: string) => {
const maybeParseJSON = (data: string) => {
try {
return JSON.parse(data);
} catch(Exception) {
} catch (Exception) {
return data;
}
};

Wyświetl plik

@ -1,6 +1,6 @@
'use strict';
export default function compareId(id1, id2) {
export default function compareId(id1: string, id2: string) {
if (id1 === id2) {
return 0;
}

Wyświetl plik

@ -1,43 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl } from 'react-intl';
import IconButton from './icon_button';
const messages = defineMessages({
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
});
export default @injectIntl
class Account extends ImmutablePureComponent {
static propTypes = {
domain: PropTypes.string,
onUnblockDomain: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
};
handleDomainUnblock = () => {
this.props.onUnblockDomain(this.props.domain);
}
render() {
const { domain, intl } = this.props;
return (
<div className='domain'>
<div className='domain__wrapper'>
<span className='domain__domain-name'>
<strong>{domain}</strong>
</span>
<div className='domain__buttons'>
<IconButton active src={require('@tabler/icons/icons/lock-open.svg')} title={intl.formatMessage(messages.unblockDomain, { domain })} onClick={this.handleDomainUnblock} />
</div>
</div>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,51 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { unblockDomain } from 'soapbox/actions/domain_blocks';
import IconButton from './icon_button';
const messages = defineMessages({
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
});
interface IDomain {
domain: string,
}
const Domain: React.FC<IDomain> = ({ domain }) => {
const dispatch = useDispatch();
const intl = useIntl();
// const onBlockDomain = () => {
// dispatch(openModal('CONFIRM', {
// icon: require('@tabler/icons/icons/ban.svg'),
// heading: <FormattedMessage id='confirmations.domain_block.heading' defaultMessage='Block {domain}' values={{ domain }} />,
// message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.' values={{ domain: <strong>{domain}</strong> }} />,
// confirm: intl.formatMessage(messages.blockDomainConfirm),
// onConfirm: () => dispatch(blockDomain(domain)),
// }));
// }
const handleDomainUnblock = () => {
dispatch(unblockDomain(domain));
};
return (
<div className='domain'>
<div className='domain__wrapper'>
<span className='domain__domain-name'>
<strong>{domain}</strong>
</span>
<div className='domain__buttons'>
<IconButton active src={require('@tabler/icons/icons/lock-open.svg')} title={intl.formatMessage(messages.unblockDomain, { domain })} onClick={handleDomainUnblock} />
</div>
</div>
</div>
);
};
export default Domain;

Wyświetl plik

@ -278,7 +278,7 @@ class Dropdown extends React.PureComponent<IDropdown, IDropdownState> {
onShiftClick(e);
} else if (this.state.id === openDropdownId) {
this.handleClose();
} else if(onOpen) {
} else if (onOpen) {
const { top } = e.currentTarget.getBoundingClientRect();
const placement: DropdownPlacement = top * 2 < innerHeight ? 'bottom' : 'top';

Wyświetl plik

@ -87,7 +87,7 @@ class StatusContent extends React.PureComponent {
&& this.state.collapsed === null
&& this.props.status.get('spoiler_text').length === 0
) {
if (node.clientHeight > MAX_HEIGHT){
if (node.clientHeight > MAX_HEIGHT) {
this.setState({ collapsed: true });
}
}

Wyświetl plik

@ -1,35 +0,0 @@
import React from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { blockDomain, unblockDomain } from '../actions/domain_blocks';
import { openModal } from '../actions/modals';
import Domain from '../components/domain';
const messages = defineMessages({
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
});
const makeMapStateToProps = () => {
const mapStateToProps = () => ({});
return mapStateToProps;
};
const mapDispatchToProps = (dispatch, { intl }) => ({
onBlockDomain(domain) {
dispatch(openModal('CONFIRM', {
icon: require('@tabler/icons/icons/ban.svg'),
heading: <FormattedMessage id='confirmations.domain_block.heading' defaultMessage='Block {domain}' values={{ domain }} />,
message: <FormattedMessage id='confirmations.domain_block.message' defaultMessage='Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.' values={{ domain: <strong>{domain}</strong> }} />,
confirm: intl.formatMessage(messages.blockDomainConfirm),
onConfirm: () => dispatch(blockDomain(domain)),
}));
},
onUnblockDomain(domain) {
dispatch(unblockDomain(domain));
},
});
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Domain));

Wyświetl plik

@ -1,38 +0,0 @@
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage } from 'react-intl';
import { NavLink } from 'react-router-dom';
import Icon from 'soapbox/components/icon';
import AvatarOverlay from '../../../components/avatar_overlay';
import DisplayName from '../../../components/display_name';
export default class MovedNote extends ImmutablePureComponent {
static propTypes = {
from: ImmutablePropTypes.map.isRequired,
to: ImmutablePropTypes.map.isRequired,
};
render() {
const { from, to } = this.props;
const displayNameHtml = { __html: from.get('display_name_html') };
return (
<div className='account__moved-note'>
<div className='account__moved-note__message'>
<div className='account__moved-note__icon-wrapper'><Icon src={require('feather-icons/dist/icons/briefcase.svg')} className='account__moved-note__icon' fixedWidth /></div>
<FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} />
</div>
<NavLink to={`/@${this.props.to.get('acct')}`} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><AvatarOverlay account={to} friend={from} /></div>
<DisplayName account={to} />
</NavLink>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,34 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { NavLink } from 'react-router-dom';
import AvatarOverlay from 'soapbox/components/avatar_overlay';
import DisplayName from 'soapbox/components/display_name';
import Icon from 'soapbox/components/icon';
import type { Account as AccountEntity } from 'soapbox/types/entities';
interface IMovedNote {
from: AccountEntity,
to: AccountEntity,
}
const MovedNote: React.FC<IMovedNote> = ({ from, to }) => {
const displayNameHtml = { __html: from.display_name_html };
return (
<div className='account__moved-note'>
<div className='account__moved-note__message'>
<div className='account__moved-note__icon-wrapper'><Icon src={require('feather-icons/dist/icons/briefcase.svg')} className='account__moved-note__icon' fixedWidth /></div>
<FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} />
</div>
<NavLink to={`/@${to.acct}`} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><AvatarOverlay account={to} friend={from} /></div>
<DisplayName account={to} />
</NavLink>
</div>
);
};
export default MovedNote;

Wyświetl plik

@ -255,7 +255,7 @@ class Audio extends React.PureComponent {
handleMouseVolSlide = throttle(e => {
const { x } = getPointerPosition(this.volume, e);
if(!isNaN(x)) {
if (!isNaN(x)) {
this.setState({ volume: x }, () => {
this.audio.volume = x;
});

Wyświetl plik

@ -1,88 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import Avatar from 'soapbox/components/avatar';
import DisplayName from 'soapbox/components/display_name';
import Icon from 'soapbox/components/icon';
import Permalink from 'soapbox/components/permalink';
import { makeGetAccount } from 'soapbox/selectors';
const messages = defineMessages({
birthday: { id: 'account.birthday', defaultMessage: 'Born {date}' },
});
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
const mapStateToProps = (state, { accountId }) => {
const account = getAccount(state, accountId);
return {
account,
};
};
return mapStateToProps;
};
export default @connect(makeMapStateToProps)
@injectIntl
class Account extends ImmutablePureComponent {
static propTypes = {
accountId: PropTypes.string.isRequired,
intl: PropTypes.object.isRequired,
account: ImmutablePropTypes.record,
};
static defaultProps = {
added: false,
};
componentDidMount() {
const { account, accountId } = this.props;
if (accountId && !account) {
this.props.fetchAccount(accountId);
}
}
render() {
const { account, intl } = this.props;
if (!account) return null;
const birthday = account.get('birthday');
if (!birthday) return null;
const formattedBirthday = intl.formatDate(birthday, { day: 'numeric', month: 'short', year: 'numeric' });
return (
<div className='account'>
<div className='account__wrapper'>
<Permalink className='account__display-name' title={account.get('acct')} href={`/@${account.get('acct')}`} to={`/@${account.get('acct')}`}>
<div className='account__display-name'>
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
<DisplayName account={account} />
</div>
</Permalink>
<div
className='account__birthday'
title={intl.formatMessage(messages.birthday, {
date: formattedBirthday,
})}
>
<Icon src={require('@tabler/icons/icons/ballon.svg')} />
{formattedBirthday}
</div>
</div>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,64 @@
import React from 'react';
import { useEffect } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import Avatar from 'soapbox/components/avatar';
import DisplayName from 'soapbox/components/display_name';
import Icon from 'soapbox/components/icon';
import Permalink from 'soapbox/components/permalink';
import { useAppSelector } from 'soapbox/hooks';
import { makeGetAccount } from 'soapbox/selectors';
const messages = defineMessages({
birthday: { id: 'account.birthday', defaultMessage: 'Born {date}' },
});
const getAccount = makeGetAccount();
interface IAccount {
accountId: string,
fetchAccount: (id: string) => void,
}
const Account: React.FC<IAccount> = ({ accountId, fetchAccount }) => {
const intl = useIntl();
const account = useAppSelector((state) => getAccount(state, accountId));
useEffect(() => {
if (accountId && !account) {
fetchAccount(accountId);
}
}, [accountId]);
if (!account) return null;
const birthday = account.get('birthday');
if (!birthday) return null;
const formattedBirthday = intl.formatDate(birthday, { day: 'numeric', month: 'short', year: 'numeric' });
return (
<div className='account'>
<div className='account__wrapper'>
<Permalink className='account__display-name' title={account.get('acct')} href={`/@${account.get('acct')}`} to={`/@${account.get('acct')}`}>
<div className='account__display-name'>
<div className='account__avatar-wrapper'><Avatar account={account} size={36} /></div>
<DisplayName account={account} />
</div>
</Permalink>
<div
className='account__birthday'
title={intl.formatMessage(messages.birthday, {
date: formattedBirthday,
})}
>
<Icon src={require('@tabler/icons/icons/ballon.svg')} />
{formattedBirthday}
</div>
</div>
</div>
);
};
export default Account;

Wyświetl plik

@ -1,74 +0,0 @@
import { debounce } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Column, Spinner } from 'soapbox/components/ui';
import { fetchBlocks, expandBlocks } from '../../actions/blocks';
import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
const messages = defineMessages({
heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
});
const mapStateToProps = state => ({
accountIds: state.getIn(['user_lists', 'blocks', 'items']),
hasMore: !!state.getIn(['user_lists', 'blocks', 'next']),
});
export default @connect(mapStateToProps)
@injectIntl
class Blocks extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.orderedSet,
hasMore: PropTypes.bool,
intl: PropTypes.object.isRequired,
};
componentDidMount() {
this.props.dispatch(fetchBlocks());
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandBlocks());
}, 300, { leading: true });
render() {
const { intl, accountIds, hasMore } = this.props;
if (!accountIds) {
return (
<Column>
<Spinner />
</Column>
);
}
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
return (
<Column label={intl.formatMessage(messages.heading)}>
<ScrollableList
scrollKey='blocks'
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
emptyMessage={emptyMessage}
className='space-y-4'
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} />,
)}
</ScrollableList>
</Column>
);
}
}

Wyświetl plik

@ -0,0 +1,58 @@
import { debounce } from 'lodash';
import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchBlocks, expandBlocks } from 'soapbox/actions/blocks';
import ScrollableList from 'soapbox/components/scrollable_list';
import { Column, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import { useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
heading: { id: 'column.blocks', defaultMessage: 'Blocked users' },
});
const handleLoadMore = debounce((dispatch) => {
dispatch(expandBlocks());
}, 300, { leading: true });
const Blocks: React.FC = () => {
const dispatch = useDispatch();
const intl = useIntl();
const accountIds = useAppSelector((state) => state.user_lists.getIn(['blocks', 'items']));
const hasMore = useAppSelector((state) => !!state.user_lists.getIn(['blocks', 'next']));
React.useEffect(() => {
dispatch(fetchBlocks());
}, []);
if (!accountIds) {
return (
<Column>
<Spinner />
</Column>
);
}
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
return (
<Column label={intl.formatMessage(messages.heading)}>
<ScrollableList
scrollKey='blocks'
onLoadMore={() => handleLoadMore(dispatch)}
hasMore={hasMore}
emptyMessage={emptyMessage}
className='space-y-4'
>
{accountIds.map((id: string) =>
<AccountContainer key={id} id={id} />,
)}
</ScrollableList>
</Column>
);
};
export default Blocks;

Wyświetl plik

@ -1,83 +0,0 @@
import { debounce } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import SubNavigation from 'soapbox/components/sub_navigation';
import { Column } from 'soapbox/components/ui';
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from '../../actions/bookmarks';
import StatusList from '../../components/status_list';
const messages = defineMessages({
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
});
const mapStateToProps = state => ({
statusIds: state.getIn(['status_lists', 'bookmarks', 'items']),
isLoading: state.getIn(['status_lists', 'bookmarks', 'isLoading'], true),
hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']),
});
export default @connect(mapStateToProps)
@injectIntl
class Bookmarks extends ImmutablePureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
shouldUpdateScroll: PropTypes.func,
statusIds: ImmutablePropTypes.orderedSet.isRequired,
intl: PropTypes.object.isRequired,
columnId: PropTypes.string,
multiColumn: PropTypes.bool,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
};
fetchData = () => {
const { dispatch } = this.props;
return dispatch(fetchBookmarkedStatuses());
}
componentDidMount() {
this.fetchData();
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandBookmarkedStatuses());
}, 300, { leading: true })
handleRefresh = () => {
return this.fetchData();
}
render() {
const { intl, shouldUpdateScroll, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.bookmarks' defaultMessage="You don't have any bookmarks yet. When you add one, it will show up here." />;
return (
<Column transparent>
<SubNavigation message={intl.formatMessage(messages.heading)} />
<StatusList
trackScroll={!pinned}
statusIds={statusIds}
scrollKey={`bookmarked_statuses-${columnId}`}
hasMore={hasMore}
isLoading={isLoading}
onLoadMore={this.handleLoadMore}
onRefresh={this.handleRefresh}
shouldUpdateScroll={shouldUpdateScroll}
emptyMessage={emptyMessage}
bindToDocument={!multiColumn}
divideType='space'
/>
</Column>
);
}
}

Wyświetl plik

@ -0,0 +1,56 @@
import { debounce } from 'lodash';
import React from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { useDispatch } from 'react-redux';
import SubNavigation from 'soapbox/components/sub_navigation';
import { Column } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from '../../actions/bookmarks';
import StatusList from '../../components/status_list';
const messages = defineMessages({
heading: { id: 'column.bookmarks', defaultMessage: 'Bookmarks' },
});
const handleLoadMore = debounce((dispatch) => {
dispatch(expandBookmarkedStatuses());
}, 300, { leading: true });
const Bookmarks: React.FC = () => {
const dispatch = useDispatch();
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']));
React.useEffect(() => {
dispatch(fetchBookmarkedStatuses());
}, []);
const handleRefresh = () => {
return dispatch(fetchBookmarkedStatuses());
};
const emptyMessage = <FormattedMessage id='empty_column.bookmarks' defaultMessage="You don't have any bookmarks yet. When you add one, it will show up here." />;
return (
<Column transparent>
<SubNavigation message={intl.formatMessage(messages.heading)} />
<StatusList
statusIds={statusIds}
scrollKey={'bookmarked_statuses'}
hasMore={hasMore}
isLoading={isLoading}
onLoadMore={() => handleLoadMore(dispatch)}
onRefresh={handleRefresh}
emptyMessage={emptyMessage}
divideType='space'
/>
</Column>
);
};
export default Bookmarks;

Wyświetl plik

@ -35,12 +35,12 @@ const chatDateComparator = (chatA, chatB) => {
return 0;
};
const makeMapStateToProps = () => {
const sortedChatIdsSelector = createSelector(
[getSortedChatIds],
chats => chats,
);
const sortedChatIdsSelector = createSelector(
[getSortedChatIds],
chats => chats,
);
const makeMapStateToProps = () => {
const mapStateToProps = state => ({
chatIds: sortedChatIdsSelector(state.getIn(['chats', 'items'])),
hasMore: !!state.getIn(['chats', 'next']),

Wyświetl plik

@ -1,75 +0,0 @@
import { debounce } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Spinner } from 'soapbox/components/ui';
import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks';
import ScrollableList from '../../components/scrollable_list';
import DomainContainer from '../../containers/domain_container';
import Column from '../ui/components/column';
const messages = defineMessages({
heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' },
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
});
const mapStateToProps = state => ({
domains: state.getIn(['domain_lists', 'blocks', 'items']),
hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']),
});
export default @connect(mapStateToProps)
@injectIntl
class Blocks extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
hasMore: PropTypes.bool,
domains: ImmutablePropTypes.orderedSet,
intl: PropTypes.object.isRequired,
};
componentDidMount() {
this.props.dispatch(fetchDomainBlocks());
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandDomainBlocks());
}, 300, { leading: true });
render() {
const { intl, domains, hasMore } = this.props;
if (!domains) {
return (
<Column>
<Spinner />
</Column>
);
}
const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />;
return (
<Column icon='minus-circle' label={intl.formatMessage(messages.heading)}>
<ScrollableList
scrollKey='domain_blocks'
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
emptyMessage={emptyMessage}
>
{domains.map(domain =>
<DomainContainer key={domain} domain={domain} />,
)}
</ScrollableList>
</Column>
);
}
}

Wyświetl plik

@ -0,0 +1,60 @@
import { debounce } from 'lodash';
import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchDomainBlocks, expandDomainBlocks } from 'soapbox/actions/domain_blocks';
import Domain from 'soapbox/components/domain';
import ScrollableList from 'soapbox/components/scrollable_list';
import { Spinner } from 'soapbox/components/ui';
import { useAppSelector } from 'soapbox/hooks';
import Column from '../ui/components/column';
const messages = defineMessages({
heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' },
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
});
const handleLoadMore = debounce((dispatch) => {
dispatch(expandDomainBlocks());
}, 300, { leading: true });
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']));
React.useEffect(() => {
dispatch(fetchDomainBlocks());
}, []);
if (!domains) {
return (
<Column>
<Spinner />
</Column>
);
}
const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />;
return (
<Column icon='minus-circle' label={intl.formatMessage(messages.heading)}>
<ScrollableList
scrollKey='domain_blocks'
onLoadMore={() => handleLoadMore(dispatch)}
hasMore={hasMore}
emptyMessage={emptyMessage}
>
{domains.map((domain) =>
<Domain key={domain} domain={domain} />,
)}
</ScrollableList>
</Column>
);
};
export default DomainBlocks;

Wyświetl plik

@ -14,7 +14,7 @@ const { unicodeToFilename } = require('./unicode_to_filename');
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
if(data.compressed) {
if (data.compressed) {
data = emojiMartUncompress(data);
}

Wyświetl plik

@ -1,74 +0,0 @@
import { debounce } from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { Column, Spinner } from 'soapbox/components/ui';
import { fetchMutes, expandMutes } from '../../actions/mutes';
import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container';
const messages = defineMessages({
heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
});
const mapStateToProps = state => ({
accountIds: state.getIn(['user_lists', 'mutes', 'items']),
hasMore: !!state.getIn(['user_lists', 'mutes', 'next']),
});
export default @connect(mapStateToProps)
@injectIntl
class Mutes extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
hasMore: PropTypes.bool,
accountIds: ImmutablePropTypes.orderedSet,
intl: PropTypes.object.isRequired,
};
componentDidMount() {
this.props.dispatch(fetchMutes());
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandMutes());
}, 300, { leading: true });
render() {
const { intl, hasMore, accountIds } = this.props;
if (!accountIds) {
return (
<Column>
<Spinner />
</Column>
);
}
const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />;
return (
<Column label={intl.formatMessage(messages.heading)}>
<ScrollableList
scrollKey='mutes'
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
emptyMessage={emptyMessage}
className='space-y-4'
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} />,
)}
</ScrollableList>
</Column>
);
}
}

Wyświetl plik

@ -0,0 +1,58 @@
import { debounce } from 'lodash';
import React from 'react';
import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import { useDispatch } from 'react-redux';
import { fetchMutes, expandMutes } from 'soapbox/actions/mutes';
import ScrollableList from 'soapbox/components/scrollable_list';
import { Column, Spinner } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import { useAppSelector } from 'soapbox/hooks';
const messages = defineMessages({
heading: { id: 'column.mutes', defaultMessage: 'Muted users' },
});
const handleLoadMore = debounce((dispatch) => {
dispatch(expandMutes());
}, 300, { leading: true });
const Mutes: React.FC = () => {
const dispatch = useDispatch();
const intl = useIntl();
const accountIds = useAppSelector((state) => state.user_lists.getIn(['mutes', 'items']));
const hasMore = useAppSelector((state) => !!state.user_lists.getIn(['mutes', 'next']));
React.useEffect(() => {
dispatch(fetchMutes());
}, []);
if (!accountIds) {
return (
<Column>
<Spinner />
</Column>
);
}
const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />;
return (
<Column label={intl.formatMessage(messages.heading)}>
<ScrollableList
scrollKey='mutes'
onLoadMore={() => handleLoadMore(dispatch)}
hasMore={hasMore}
emptyMessage={emptyMessage}
className='space-y-4'
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} />,
)}
</ScrollableList>
</Column>
);
};
export default Mutes;

Wyświetl plik

@ -8,7 +8,8 @@ import { withRouter } from 'react-router-dom';
import AttachmentThumbs from 'soapbox/components/attachment_thumbs';
import { Stack, Text } from 'soapbox/components/ui';
import AccountContainer from 'soapbox/containers/account_container';
import { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities';
import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities';
const messages = defineMessages({
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },

Wyświetl plik

@ -22,7 +22,7 @@ const isSafeUrl = text => {
try {
const url = new URL(text);
return ['http:', 'https:'].includes(url.protocol);
} catch(e) {
} catch (e) {
return false;
}
};

Wyświetl plik

@ -216,12 +216,12 @@ class Video extends React.PureComponent {
handleMouseVolSlide = throttle(e => {
const { x } = getPointerPosition(this.volume, e);
if(!isNaN(x)) {
if (!isNaN(x)) {
let slideamt = x;
if(x > 1) {
if (x > 1) {
slideamt = 1;
} else if(x < 0) {
} else if (x < 0) {
slideamt = 0;
}

Wyświetl plik

@ -44,7 +44,7 @@ const localState = fromJS(JSON.parse(localStorage.getItem(STORAGE_KEY)));
const validUser = user => {
try {
return validId(user.get('id')) && validId(user.get('access_token'));
} catch(e) {
} catch (e) {
return false;
}
};

Wyświetl plik

@ -58,7 +58,7 @@ const expandNormalizedConversations = (state, conversations, next, isLoadingRece
list = list.concat(items);
return list.sortBy(x => x.get('last_status'), (a, b) => {
if(a === null || b === null) {
if (a === null || b === null) {
return -1;
}

Wyświetl plik

@ -93,7 +93,7 @@ export default function getStream(streamingAPIBaseURL, accessToken, stream, { co
if (!e.data) return;
try {
received(JSON.parse(e.data));
} catch(error) {
} catch (error) {
console.error(e);
console.error(`Could not parse the above streaming event.\n${error}`);
}

Wyświetl plik

@ -1,64 +0,0 @@
import { List as ImmutableList } from 'immutable';
export const validId = id => typeof id === 'string' && id !== 'null' && id !== 'undefined';
export const isURL = url => {
try {
new URL(url);
return true;
} catch {
return false;
}
};
export const parseBaseURL = url => {
try {
return new URL(url).origin;
} catch {
return '';
}
};
export const getLoggedInAccount = state => {
const me = state.get('me');
return state.getIn(['accounts', me]);
};
export const isLoggedIn = getState => {
return validId(getState().get('me'));
};
export const getAppToken = state => state.getIn(['auth', 'app', 'access_token']);
export const getUserToken = (state, accountId) => {
const accountUrl = state.getIn(['accounts', accountId, 'url']);
return state.getIn(['auth', 'users', accountUrl, 'access_token']);
};
export const getAccessToken = state => {
const me = state.get('me');
return getUserToken(state, me);
};
export const getAuthUserId = state => {
const me = state.getIn(['auth', 'me']);
return ImmutableList([
state.getIn(['auth', 'users', me, 'id']),
me,
]).find(validId);
};
export const getAuthUserUrl = state => {
const me = state.getIn(['auth', 'me']);
return ImmutableList([
state.getIn(['auth', 'users', me, 'url']),
me,
]).find(isURL);
};
/** Get the VAPID public key. */
export const getVapidKey = state => {
return state.getIn(['auth', 'app', 'vapid_key']) || state.getIn(['instance', 'pleroma', 'vapid_public_key']);
};

Wyświetl plik

@ -0,0 +1,66 @@
import { List as ImmutableList } from 'immutable';
import type { RootState } from 'soapbox/store';
export const validId = (id: any) => typeof id === 'string' && id !== 'null' && id !== 'undefined';
export const isURL = (url: string) => {
try {
new URL(url);
return true;
} catch {
return false;
}
};
export const parseBaseURL = (url: any) => {
try {
return new URL(url).origin;
} catch {
return '';
}
};
export const getLoggedInAccount = (state: RootState) => {
const me = state.me;
return state.accounts.get(me);
};
export const isLoggedIn = (getState: () => RootState) => {
return validId(getState().me);
};
export const getAppToken = (state: RootState) => state.auth.getIn(['app', 'access_token']);
export const getUserToken = (state: RootState, accountId?: string | false | null) => {
const accountUrl = state.accounts.getIn([accountId, 'url']);
return state.auth.getIn(['users', accountUrl, 'access_token']);
};
export const getAccessToken = (state: RootState) => {
const me = state.me;
return getUserToken(state, me);
};
export const getAuthUserId = (state: RootState) => {
const me = state.auth.get('me');
return ImmutableList([
state.auth.getIn(['users', me, 'id']),
me,
]).find(validId);
};
export const getAuthUserUrl = (state: RootState) => {
const me = state.auth.get('me');
return ImmutableList([
state.auth.getIn(['users', me, 'url']),
me,
]).find(isURL);
};
/** Get the VAPID public key. */
export const getVapidKey = (state: RootState) => {
return state.auth.getIn(['app', 'vapid_key']) || state.instance.getIn(['pleroma', 'vapid_public_key']);
};

Wyświetl plik

@ -1,4 +1,4 @@
export const decode = base64 => {
export const decode = (base64: string) => {
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);

Wyświetl plik

@ -15,7 +15,7 @@ export const addGreentext = html => {
} else {
return string;
}
} catch(e) {
} catch (e) {
return string;
}
});

Wyświetl plik

@ -1,11 +0,0 @@
export const getHost = instance => {
try {
return new URL(instance.get('uri')).host;
} catch {
try {
return new URL(`https://${instance.get('uri')}`).host;
} catch {
return null;
}
}
};

Wyświetl plik

@ -1,17 +1,17 @@
export const minimumAspectRatio = 9 / 16; // Portrait phone
export const maximumAspectRatio = 10; // Generous min-height
export const isPanoramic = ar => {
export const isPanoramic = (ar: number) => {
if (isNaN(ar)) return false;
return ar >= maximumAspectRatio;
};
export const isPortrait = ar => {
export const isPortrait = (ar: number) => {
if (isNaN(ar)) return false;
return ar <= minimumAspectRatio;
};
export const isNonConformingRatio = ar => {
export const isNonConformingRatio = (ar: number) => {
if (isNaN(ar)) return false;
return !isPanoramic(ar) && !isPortrait(ar);
};

Wyświetl plik

@ -1,6 +1,8 @@
import { isIntegerId } from 'soapbox/utils/numbers';
export const getFirstExternalLink = status => {
import type { Status as StatusEntity } from 'soapbox/types/entities';
export const getFirstExternalLink = (status: StatusEntity) => {
try {
// Pulled from Pleroma's media parser
const selector = 'a:not(.mention,.hashtag,.attachment,[rel~="tag"])';
@ -12,11 +14,11 @@ export const getFirstExternalLink = status => {
}
};
export const shouldHaveCard = status => {
export const shouldHaveCard = (status: StatusEntity) => {
return Boolean(getFirstExternalLink(status));
};
// https://gitlab.com/soapbox-pub/soapbox-fe/-/merge_requests/1087
export const hasIntegerMediaIds = status => {
export const hasIntegerMediaIds = (status: StatusEntity) => {
return status.media_attachments.some(({ id }) => isIntegerId(id));
};

Wyświetl plik

@ -1,13 +0,0 @@
import { Map as ImmutableMap } from 'immutable';
export const shouldFilter = (status, columnSettings) => {
const shows = ImmutableMap({
reblog: status.get('reblog') !== null,
reply: status.get('in_reply_to_id') !== null,
direct: status.get('visibility') === 'direct',
});
return shows.some((value, key) => {
return columnSettings.getIn(['shows', key]) === false && value;
});
};

Wyświetl plik

@ -0,0 +1,15 @@
import { Map as ImmutableMap } from 'immutable';
import type { Status as StatusEntity } from 'soapbox/types/entities';
export const shouldFilter = (status: StatusEntity, columnSettings: any) => {
const shows = ImmutableMap({
reblog: status.reblog !== null,
reply: status.in_reply_to_id !== null,
direct: status.visibility === 'direct',
});
return shows.some((value, key) => {
return columnSettings.getIn(['shows', key]) === false && value;
});
};

Wyświetl plik

@ -75,6 +75,7 @@
"@types/jest": "^27.4.1",
"@types/lodash": "^4.14.180",
"@types/qrcode.react": "^1.0.2",
"@types/react-datepicker": "^4.4.0",
"@types/react-helmet": "^6.1.5",
"@types/react-motion": "^0.0.32",
"@types/react-router-dom": "^5.3.3",
@ -145,7 +146,7 @@
"qrcode.react": "^1.0.0",
"react": "^16.13.1",
"react-color": "^2.18.1",
"react-datepicker": "^4.6.0",
"react-datepicker": "^4.7.0",
"react-dom": "^16.13.1",
"react-helmet": "^6.0.0",
"react-hotkeys": "^1.1.4",

Wyświetl plik

@ -2171,6 +2171,16 @@
dependencies:
"@types/react" "*"
"@types/react-datepicker@^4.4.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@types/react-datepicker/-/react-datepicker-4.4.0.tgz#0072e18536ad305fd57786f9b6f9e499eed2b475"
integrity sha512-wzmevaO51rLFwSZd5HSqBU0aAvZlRRkj6QhHqj0jfRDSKnN3y5IKXyhgxPS8R0LOWOtjdpirI1DBryjnIp/7gA==
dependencies:
"@popperjs/core" "^2.9.2"
"@types/react" "*"
date-fns "^2.0.1"
react-popper "^2.2.5"
"@types/react-dom@*":
version "17.0.14"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f"
@ -4006,7 +4016,7 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
date-fns@^2.24.0:
date-fns@^2.0.1, date-fns@^2.24.0:
version "2.28.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
@ -8662,7 +8672,7 @@ react-color@^2.18.1:
reactcss "^1.2.0"
tinycolor2 "^1.4.1"
react-datepicker@^4.6.0:
react-datepicker@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.7.0.tgz#75e03b0a6718b97b84287933307faf2ed5f03cf4"
integrity sha512-FS8KgbwqpxmJBv/bUdA42MYqYZa+fEYcpc746DZiHvVE2nhjrW/dg7c5B5fIUuI8gZET6FOzuDgezNcj568Czw==