This commit is contained in:
mgabdev 2020-04-02 12:57:04 -04:00
parent d79133c72d
commit 416bc3d603
19 changed files with 209 additions and 460 deletions

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class Api::V1::GabTrendsController < Api::BaseController
respond_to :json
skip_before_action :set_cache_headers
def index
uri = URI('https://trends.gab.com/trend-feed/json')
uri.query = URI.encode_www_form()
res = Net::HTTP.get_response(uri)
render json: res.body if res.is_a?(Net::HTTPSuccess)
end
end

View File

@ -1,220 +0,0 @@
import { importFetchedStatus, importFetchedStatuses } from './importer';
import api, { getLinks } from '../api';
import { Map as ImmutableMap, List as ImmutableList, toJS } from 'immutable';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_CLEAR = 'TIMELINE_CLEAR';
export const TIMELINE_UPDATE_QUEUE = 'TIMELINE_UPDATE_QUEUE';
export const TIMELINE_DEQUEUE = 'TIMELINE_DEQUEUE';
export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
export const TIMELINE_EXPAND_REQUEST = 'TIMELINE_EXPAND_REQUEST';
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
export const MAX_QUEUED_ITEMS = 40;
export function updateTimeline(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
dispatch(importFetchedStatus(status));
dispatch({
type: TIMELINE_UPDATE,
timeline,
status,
});
};
};
export function updateTimelineQueue(timeline, status, accept) {
return dispatch => {
if (typeof accept === 'function' && !accept(status)) {
return;
}
dispatch({
type: TIMELINE_UPDATE_QUEUE,
timeline,
status,
});
}
};
export function dequeueTimeline(timeline, expandFunc, optionalExpandArgs) {
return (dispatch, getState) => {
const queuedItems = getState().getIn(['timelines', timeline, 'queuedItems'], ImmutableList());
const totalQueuedItemsCount = getState().getIn(['timelines', timeline, 'totalQueuedItemsCount'], 0);
let shouldDispatchDequeue = true;
if (totalQueuedItemsCount == 0) {
return;
}
else if (totalQueuedItemsCount > 0 && totalQueuedItemsCount <= MAX_QUEUED_ITEMS) {
queuedItems.forEach(status => {
dispatch(updateTimeline(timeline, status.toJS(), null));
});
}
else {
if (typeof expandFunc === 'function') {
dispatch(clearTimeline(timeline));
expandFunc();
}
else {
if (timeline === 'home') {
dispatch(clearTimeline(timeline));
dispatch(expandHomeTimeline(optionalExpandArgs));
}
else if (timeline === 'community') {
dispatch(clearTimeline(timeline));
dispatch(expandCommunityTimeline(optionalExpandArgs));
}
else {
shouldDispatchDequeue = false;
}
}
}
if (!shouldDispatchDequeue) return;
dispatch({
type: TIMELINE_DEQUEUE,
timeline,
});
}
};
export function deleteFromTimelines(id) {
return (dispatch, getState) => {
const accountId = getState().getIn(['statuses', id, 'account']);
const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]);
const reblogOf = getState().getIn(['statuses', id, 'reblog'], null);
dispatch({
type: TIMELINE_DELETE,
id,
accountId,
references,
reblogOf,
});
};
};
export function clearTimeline(timeline) {
return (dispatch) => {
dispatch({ type: TIMELINE_CLEAR, timeline });
};
};
const noOp = () => { };
const parseTags = (tags = {}, mode) => {
return (tags[mode] || []).map((tag) => {
return tag.value;
});
};
export function expandTimeline(timelineId, path, params = {}, done = noOp) {
return (dispatch, getState) => {
const timeline = getState().getIn(['timelines', timelineId], ImmutableMap());
const isLoadingMore = !!params.max_id;
if (timeline.get('isLoading')) {
done();
return;
}
if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) {
params.since_id = timeline.getIn(['items', 0]);
}
const isLoadingRecent = !!params.since_id;
dispatch(expandTimelineRequest(timelineId, isLoadingMore));
api(getState).get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore));
done();
}).catch(error => {
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
done();
});
};
};
export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
export const expandAccountMediaTimeline = (accountId, { maxId, limit } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true, limit: limit || 20 });
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
export const expandGroupTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`group:${id}`, `/api/v1/timelines/group/${id}`, { max_id: maxId }, done);
export const expandHashtagTimeline = (hashtag, { maxId, tags } = {}, done = noOp) => {
return expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, {
max_id: maxId,
any: parseTags(tags, 'any'),
all: parseTags(tags, 'all'),
none: parseTags(tags, 'none'),
}, done);
};
export function expandTimelineRequest(timeline, isLoadingMore) {
return {
type: TIMELINE_EXPAND_REQUEST,
timeline,
skipLoading: !isLoadingMore,
};
};
export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
return {
type: TIMELINE_EXPAND_SUCCESS,
timeline,
statuses,
next,
partial,
isLoadingRecent,
skipLoading: !isLoadingMore,
};
};
export function expandTimelineFail(timeline, error, isLoadingMore) {
return {
type: TIMELINE_EXPAND_FAIL,
timeline,
error,
skipLoading: !isLoadingMore,
};
};
export function connectTimeline(timeline) {
return {
type: TIMELINE_CONNECT,
timeline,
};
};
export function disconnectTimeline(timeline) {
return {
type: TIMELINE_DISCONNECT,
timeline,
};
};
export function scrollTopTimeline(timeline, top) {
return {
type: TIMELINE_SCROLL_TOP,
timeline,
top,
};
};

View File

@ -0,0 +1,26 @@
const LikedIcon = ({
className = '',
width = '26px',
height = '26px',
viewBox = '0 0 48 48',
title = 'Liked',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 25.398438 0.214844 C 24.757812 0.488281 24.449219 0.703125 23.945312 1.238281 C 22.875 2.34375 22.414062 3.507812 21.601562 7.117188 C 21.375 8.109375 21.09375 9.140625 20.972656 9.414062 C 20.851562 9.675781 20.382812 10.3125 19.921875 10.828125 C 19.472656 11.34375 18.195312 12.890625 17.101562 14.269531 C 15.992188 15.648438 14.746094 17.136719 14.316406 17.578125 L 13.527344 18.375 L 9.601562 18.375 C 7.445312 18.382812 5.382812 18.421875 5.035156 18.460938 C 3.46875 18.664062 2.210938 19.820312 1.882812 21.355469 C 1.742188 22.011719 1.734375 40.621094 1.875 41.351562 C 2.089844 42.476562 2.933594 43.539062 4.003906 44.042969 L 4.546875 44.296875 L 9.46875 44.34375 C 13.847656 44.390625 14.445312 44.417969 14.90625 44.570312 C 15.1875 44.652344 16.753906 45.167969 18.375 45.703125 C 24.777344 47.804688 25.867188 48 31.246094 48 C 34.367188 48 34.800781 47.980469 35.652344 47.804688 C 37.265625 47.484375 38.644531 46.800781 39.769531 45.757812 C 41.296875 44.34375 42.132812 42.523438 42.253906 40.3125 C 42.289062 39.523438 42.328125 39.375 42.5625 39.039062 C 42.964844 38.464844 43.453125 37.445312 43.679688 36.710938 C 43.941406 35.878906 44.101562 34.433594 44.015625 33.636719 C 43.96875 33.09375 43.988281 32.953125 44.183594 32.558594 C 44.710938 31.527344 44.925781 30.601562 44.980469 29.195312 C 45.007812 28.386719 44.980469 27.683594 44.914062 27.382812 C 44.8125 26.898438 44.8125 26.886719 45.28125 25.941406 C 45.964844 24.5625 46.144531 23.765625 46.152344 22.21875 C 46.164062 21.179688 46.125 20.820312 45.957031 20.222656 C 45.5625 18.835938 44.933594 17.839844 43.742188 16.707031 C 43.117188 16.117188 42.730469 15.84375 42.066406 15.515625 C 40.613281 14.804688 40.492188 14.785156 36.957031 14.738281 C 34.085938 14.710938 33.835938 14.691406 33.871094 14.550781 C 33.898438 14.464844 34.078125 13.996094 34.265625 13.519531 C 34.996094 11.617188 35.242188 9.945312 35.070312 7.902344 C 34.828125 5.070312 34.191406 3.421875 32.804688 2.054688 C 32.316406 1.566406 31.949219 1.320312 31.199219 0.957031 C 29.839844 0.28125 28.960938 0.0742188 27.28125 0.0273438 C 26.015625 -0.0078125 25.882812 0.0078125 25.398438 0.214844 Z M 7.820312 37.039062 C 8.324219 37.179688 8.972656 37.808594 9.101562 38.277344 C 9.320312 39.085938 8.960938 39.898438 8.222656 40.3125 C 7.105469 40.949219 5.738281 40.210938 5.644531 38.914062 C 5.578125 38.035156 6.09375 37.285156 6.9375 37.050781 C 7.378906 36.917969 7.398438 36.917969 7.820312 37.039062 Z M 7.820312 37.039062' />
</g>
</svg>
)
export default LikedIcon

View File

@ -38,6 +38,7 @@ export default class Button extends PureComponent {
underlineOnHover: PropTypes.bool,
radiusSmall: PropTypes.bool,
noClasses: PropTypes.bool,
buttonRef: PropTypes.func,
}
static defaultProps = {

View File

@ -1,47 +0,0 @@
import { defineMessages, injectIntl } from 'react-intl';
import Button from './button';
const messages = defineMessages({
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
});
export default
@injectIntl
class Domain extends PureComponent {
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__name'>
<strong>{domain}</strong>
</span>
<div className='domain__buttons'>
<Button
active
icon='unlock'
title={intl.formatMessage(messages.unblockDomain, {
domain,
})}
onClick={this.handleDomainUnblock}
/>
</div>
</div>
</div>
);
}
}

View File

@ -26,6 +26,7 @@ import HomeIcon from '../assets/home_icon'
import InvestorIcon from '../assets/investor_icon'
import ItalicIcon from '../assets/italic_icon'
import LikeIcon from '../assets/like_icon'
import LikedIcon from '../assets/liked_icon'
import LinkIcon from '../assets/link_icon'
import ListIcon from '../assets/list_icon'
import ListAddIcon from '../assets/list_add_icon'
@ -84,6 +85,7 @@ const ICONS = {
'investor': InvestorIcon,
'italic': ItalicIcon,
'like': LikeIcon,
'liked': LikedIcon,
'link': LinkIcon,
'list': ListIcon,
'list-add': ListAddIcon,

View File

@ -10,14 +10,25 @@ export default class List extends ImmutablePureComponent {
scrollKey: PropTypes.string,
emptyMessage: PropTypes.any,
small: PropTypes.bool,
onLoadMore: PropTypes.func,
hasMore: PropTypes.bool,
}
render() {
const { items, scrollKey, emptyMessage, small } = this.props
const {
items,
scrollKey,
emptyMessage,
hasMore,
small,
onLoadMore
} = this.props
return (
<Block>
<ScrollableList
onLoadMore={onLoadMore}
hasMore={hasMore}
scrollKey={scrollKey}
emptyMessage={emptyMessage}
>

View File

@ -1,70 +1,50 @@
import { injectIntl, defineMessages } from 'react-intl'
import { muteAccount } from '../../actions/accounts'
import { blockDomain } from '../../actions/domain_blocks'
import ConfirmationModal from './confirmation_modal'
const messages = defineMessages({
muteMessage: { id: 'confirmations.mute.message', defaultMessage: 'Are you sure you want to mute {name}?' },
blockDomain: { id: 'block_domain', defaultMessage: 'Block {domain}' },
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
blockDomainMessage: { 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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' },
cancel: { id: 'confirmation_modal.cancel', defaultMessage: 'Cancel' },
confirm: { id: 'confirmations.mute.confirm', defaultMessage: 'Mute' },
})
const mapStateToProps = state => {
return {
isSubmitting: state.getIn(['reports', 'new', 'isSubmitting']),
account: state.getIn(['mutes', 'new', 'account']),
}
}
const mapDispatchToProps = dispatch => {
return {
onConfirm(account, notifications) {
dispatch(muteAccount(account.get('id'), notifications))
onConfirm(domain) {
dispatch(blockDomain(domain))
},
}
}
export default
@connect(mapStateToProps, mapDispatchToProps)
@connect(null, mapDispatchToProps)
@injectIntl
class BlockDomainModal extends PureComponent {
static propTypes = {
isSubmitting: PropTypes.bool.isRequired,
account: PropTypes.object.isRequired,
domain: PropTypes.string.isRequired,
onConfirm: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
componentDidMount() {
this.button.focus()
}
handleClick = () => {
this.props.onClose()
this.props.onConfirm(this.props.account, this.props.notifications)
}
handleCancel = () => {
this.props.onClose()
this.props.onConfirm(this.props.domain)
}
render() {
const { account, intl } = this.props
const { onClose, domain, intl } = this.props
// dispatch(openModal('CONFIRM', {
// 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. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.' values={{ domain: <strong>{domain}</strong> }} />,
// confirm: intl.formatMessage(messages.blockDomainConfirm),
// onConfirm: () => dispatch(blockDomain(domain)),
// }));
console.log("this.props: ", this.props)
return (
<ConfirmationModal
title={`Mute @${account.get('acct')}`}
message={<FormattedMessage id='confirmations.mute.message' defaultMessage='Are you sure you want to mute @{name}?' values={{ name: account.get('acct') }} />}
confirm={<FormattedMessage id='mute' defaultMessage='Mute' />}
onConfirm={() => {
// dispatch(blockDomain(domain))
// dispatch(blockDomain(domain))
}}
title={intl.formatMessage(messages.blockDomain, { domain })}
message={intl.formatMessage(messages.blockDomainMessage, { domain })}
confirm={intl.formatMessage(messages.blockDomainConfirm)}
onConfirm={this.handleClick}
onClose={onClose}
/>
)
}

View File

@ -216,7 +216,7 @@ class ProfileOptionsPopover extends PureComponent {
title: intl.formatMessage(isBlockingDomain ? messages.unblockDomain : messages.blockDomain, {
domain,
}),
onClick: this.handleUnblockDomain
onClick: isBlockingDomain ? this.handleUnblockDomain : this.handleBlockDomain,
})
}
@ -275,12 +275,14 @@ class ProfileOptionsPopover extends PureComponent {
}
handleBlockDomain = () => {
const domain = this.props.account.get('acct').split('@')[1];
const domain = this.props.account.get('acct').split('@')[1]
console.log("handleBlockDomain:", domain)
// : todo : alert
if (!domain) return;
if (!domain) return
this.props.onBlockDomain(domain);
this.props.onBlockDomain(domain)
}
handleUnblockDomain = () => {

View File

@ -27,6 +27,9 @@ const messages = defineMessages({
cannot_repost: { id: 'status.cannot_repost', defaultMessage: 'This post cannot be reposted' },
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
like: { id: 'status.like', defaultMessage: 'Like' },
likesLabel: { id: 'likes.label', defaultMessage: '{number, plural, one {# like} other {# likes}}' },
repostsLabel: { id: 'reposts.label', defaultMessage: '{number, plural, one {# repost} other {# reposts}}' },
commentsLabel: { id: 'comments.label', defaultMessage: '{number, plural, one {# comment} other {# comments}}' },
open: { id: 'status.open', defaultMessage: 'Expand this status' },
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
@ -195,8 +198,9 @@ class StatusActionBar extends ImmutablePureComponent {
favoriteCount > 0 &&
<button className={interactionBtnClasses}>
<Text color='secondary' size='small'>
{favoriteCount}
&nbsp;Likes
{formatMessage(messages.likesLabel, {
number: favoriteCount,
})}
</Text>
</button>
}
@ -204,8 +208,9 @@ class StatusActionBar extends ImmutablePureComponent {
replyCount > 0 &&
<button className={interactionBtnClasses}>
<Text color='secondary' size='small'>
{replyCount}
&nbsp;Comments
{formatMessage(messages.commentsLabel, {
number: replyCount,
})}
</Text>
</button>
}
@ -213,8 +218,9 @@ class StatusActionBar extends ImmutablePureComponent {
repostCount > 0 &&
<button className={interactionBtnClasses}>
<Text color='secondary' size='small'>
{repostCount}
&nbsp;Reposts
{formatMessage(messages.repostsLabel, {
number: repostCount,
})}
</Text>
</button>
}
@ -224,8 +230,8 @@ class StatusActionBar extends ImmutablePureComponent {
<div className={[_s.default, _s.flexRow, _s.py2, _s.width100PC].join(' ')}>
<StatusActionBarItem
title={formatMessage(messages.like)}
icon='like'
active={!!status.get('favorited')}
icon={!!status.get('favourited') ? 'liked' : 'like'}
active={!!status.get('favourited')}
onClick={this.handleFavoriteClick}
/>
<StatusActionBarItem

View File

@ -1,5 +1,7 @@
import classNames from 'classnames/bind'
import Button from './button'
import Icon from './icon'
import Text from './text'
const cx = classNames.bind(_s)
@ -24,37 +26,36 @@ export default class StatusActionBarItem extends PureComponent {
} = this.props
const btnClasses = cx({
default: 1,
text: 1,
fontSize13PX: 1,
fontWeightMedium: 1,
cursorPointer: 1,
displayFlex: 1,
justifyContentCenter: 1,
flexRow: 1,
alignItemsCenter: 1,
py10: 1,
px10: 1,
width100PC: 1,
radiusSmall: 1,
outlineNone: 1,
backgroundTransparent: 1,
backgroundSubtle_onHover: 1,
colorSecondary: 1,
})
const color = active ? 'brand' : 'secondary'
const weight = active ? 'bold' : 'medium'
return (
<div className={[_s.default, _s.flexGrow1, _s.px5].join(' ')}>
<button
ref={buttonRef}
<div className={[_s.default, _s.flexNormal, _s.px5].join(' ')}>
<Button
block
radiusSmall
backgroundColor='none'
color={color}
buttonRef={buttonRef}
className={btnClasses}
onClick={onClick}
active={active}
disabled={disabled}
icon={icon}
iconWidth='16px'
iconHeight='16px'
iconClassName={[_s.default, _s.mr10, _s.inheritFill].join(' ')}
>
<Icon width='16px' height='16px' id={icon} className={[_s.default, _s.mr10, _s.fillColorSecondary].join(' ')} />
{title}
</button>
<Text color='inherit' size='small' weight={weight}>
{title}
</Text>
</Button>
</div>
)
}

View File

@ -1,12 +1,8 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import { blockDomain, unblockDomain } from '../actions/domain_blocks'
import { unblockDomain } from '../actions/domain_blocks'
import { openModal } from '../actions/modal'
import Domain from '../components/domain'
const messages = defineMessages({
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
})
const mapDispatchToProps = (dispatch, { intl }) => ({
onBlockDomain (domain) {
dispatch(openModal('BLOCK_DOMAIN', {

View File

@ -1,62 +1,86 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { debounce } from 'lodash';
import { fetchDomainBlocks, expandDomainBlocks } from '../actions/domain_blocks';
import DomainContainer from '../containers/domain_container';
import ColumnIndicator from '../components/column_indicator';
import ScrollableList from '../components/scrollable_list';
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { debounce } from 'lodash'
import { unblockDomain, fetchDomainBlocks, expandDomainBlocks } from '../actions/domain_blocks'
import ColumnIndicator from '../components/column_indicator'
import List from '../components/list'
const messages = defineMessages({
heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' },
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },
emptyMessage: { id: 'empty_column.domain_blocks', defaultMessage: 'There are no hidden domains yet.' },
});
const mapDispatchToProps = (dispatch) => ({
onExpandDomainBlocks() {
dispatch(expandDomainBlocks())
},
onFetchDomainBlocks() {
dispatch(fetchDomainBlocks())
},
onUnblockDomain (domain) {
dispatch(unblockDomain(domain))
},
})
const mapStateToProps = state => ({
domains: state.getIn(['domain_lists', 'blocks', 'items']),
hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']),
});
})
export default
@connect(mapStateToProps)
@injectIntl
@connect(mapStateToProps, mapDispatchToProps)
class Blocks extends ImmutablePureComponent {
static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
hasMore: PropTypes.bool,
domains: ImmutablePropTypes.orderedSet,
intl: PropTypes.object.isRequired,
};
onUnblockDomain: PropTypes.func.isRequired,
onFetchDomainBlocks: PropTypes.func.isRequired,
onExpandDomainBlocks: PropTypes.func.isRequired,
}
componentWillMount() {
this.props.dispatch(fetchDomainBlocks());
this.props.onFetchDomainBlocks()
}
handleUnblockDomain = (domain) => {
this.props.onUnblockDomain(domain)
}
handleLoadMore = debounce(() => {
this.props.dispatch(expandDomainBlocks());
}, 300, { leading: true });
this.props.onExpandDomainBlocks()
}, 300, { leading: true })
render() {
const { intl, domains, hasMore } = this.props;
const { intl, domains, hasMore } = this.props
if (!domains) {
return (<ColumnIndicator type='loading' />);
return <ColumnIndicator type='loading' />
}
const items = domains.map((domain) => {
return {
title: intl.formatMessage(messages.unblockDomain, { domain }),
onClick: () => this.handleUnblockDomain(domain),
hideArrow: true,
}
})
return (
<ScrollableList
<List
scrollKey='domain_blocks'
onLoadMore={this.handleLoadMore}
hasMore={hasMore}
emptyMessage={<FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no hidden domains yet.' />}
>
{domains.map(domain =>
<DomainContainer key={domain} domain={domain} />
)}
</ScrollableList>
);
emptyMessage={intl.formatMessage(messages.emptyMessage)}
items={items}
/>
)
}
}

View File

@ -40,7 +40,7 @@ export default class ComposeExtraButton extends PureComponent {
fillColorWhite: active,
})
const iconSize = !!small ? '12px' : '16px'
const iconSize = !!small ? '14px' : '16px'
return (
<div className={[_s.default, _s.mr2].join(' ')} ref={buttonRef}>

View File

@ -260,15 +260,6 @@ class ComposeForm extends ImmutablePureComponent {
px15: !shouldCondense,
})
const contentWarningClasses = cx({
default: 1,
px15: 1,
py10: 1,
borderBottom1PX: 1,
borderColorSecondary: 1,
displayNone: !spoiler
})
return (
<div className={parentContainerClasses}>
<div className={[_s.default, _s.flexRow, _s.width100PC].join(' ')}>
@ -285,23 +276,26 @@ class ComposeForm extends ImmutablePureComponent {
onClick={this.handleClick}
>
<div className={contentWarningClasses}>
<AutosuggestTextbox
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={this.props.spoilerText}
onChange={this.handleChangeSpoilerText}
onKeyDown={this.handleKeyDown}
disabled={!this.props.spoiler}
ref={this.setSpoilerText}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']}
prependIcon='warning'
id='cw-spoiler-input'
/>
</div>
{
!!spoiler &&
<div className={[_s.default, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
<AutosuggestTextbox
placeholder={intl.formatMessage(messages.spoiler_placeholder)}
value={this.props.spoilerText}
onChange={this.handleChangeSpoilerText}
onKeyDown={this.handleKeyDown}
disabled={!this.props.spoiler}
ref={this.setSpoilerText}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']}
prependIcon='warning'
id='cw-spoiler-input'
/>
</div>
}
<AutosuggestTextbox
ref={(isModalOpen && shouldCondense) ? null : this.setAutosuggestTextarea}
@ -335,22 +329,35 @@ class ComposeForm extends ImmutablePureComponent {
<div className={actionsContainerClasses}>
<div className={[_s.default, _s.flexRow, _s.marginRightAuto].join(' ')}>
<RichTextEditorButton small={shouldCondense} />
{
!shouldCondense &&
<RichTextEditorButton />
}
<UploadButton small={shouldCondense} />
{
!edit && <PollButton small={shouldCondense} />
!edit && !shouldCondense &&
<PollButton />
}
{
!shouldCondense &&
<StatusVisibilityButton small={shouldCondense} />
<StatusVisibilityButton />
}
{
!shouldCondense &&
<SpoilerButton />
}
{
!shouldCondense &&
<SchedulePostButton />
}
<SpoilerButton small={shouldCondense} />
<SchedulePostButton small={shouldCondense} />
<GifSelectorButton small={shouldCondense} />
<EmojiPickerButton small={shouldCondense} />
</div>
<CharacterCounter max={maxPostCharacterCount} text={text} small={shouldCondense} />
{
!shouldCondense &&
<CharacterCounter max={maxPostCharacterCount} text={text} />
}
{
!shouldCondense &&

View File

@ -55,7 +55,7 @@ const mapDispatchToProps = dispatch => ({
},
onFavorite (status) {
if (status.get('favorited')) {
if (status.get('favourited')) {
dispatch(unfavorite(status))
} else {
dispatch(favorite(status))

View File

@ -80,7 +80,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
},
onFavorite (status) {
if (status.get('favorited')) {
if (status.get('favourited')) {
dispatch(unfavorite(status));
} else {
dispatch(favorite(status));

View File

@ -85,7 +85,7 @@ export default class GroupLayout extends ImmutablePureComponent {
className={_s.mr5}
>
<Text color='inherit' size='small'>
Leave/Join
Leave/Join
</Text>
</Button>
<Button

View File

@ -1,55 +0,0 @@
import {
GIFS_CLEAR_RESULTS,
GIF_SET_SELECTED,
GIF_CHANGE_SEARCH_TEXT,
GIF_RESULTS_FETCH_REQUEST,
GIF_RESULTS_FETCH_SUCCESS,
GIF_RESULTS_FETCH_FAIL,
GIF_CATEGORIES_FETCH_REQUEST,
GIF_CATEGORIES_FETCH_SUCCESS,
GIF_CATEGORIES_FETCH_FAIL
} from '../actions/tenor'
import { Map as ImmutableMap } from 'immutable'
const initialState = ImmutableMap({
categories: [],
results: [],
chosenUrl: '',
searchText: '',
loading: false,
error: false,
})
export default function (state = initialState, action) {
switch (action.type) {
case GIF_RESULTS_FETCH_REQUEST:
case GIF_CATEGORIES_FETCH_REQUEST:
return state.set('loading', true)
case GIF_RESULTS_FETCH_SUCCESS:
return state.withMutations(map => {
map.set('results', action.results);
map.set('error', false);
map.set('loading', false);
});
case GIF_CATEGORIES_FETCH_SUCCESS:
return state.withMutations(map => {
map.set('categories', action.categories);
map.set('error', false);
map.set('loading', false);
});
case GIF_RESULTS_FETCH_FAIL:
case GIF_CATEGORIES_FETCH_FAIL:
return state.withMutations(map => {
map.set('error', !!action.error);
map.set('loading', false);
});
case GIFS_CLEAR_RESULTS:
return state.set('results', [])
case GIF_SET_SELECTED:
return state.set('chosenUrl', action.url)
case GIF_CHANGE_SEARCH_TEXT:
return state.set('searchText', action.text.trim());
default:
return state
}
}